diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 00000000..5355bee5 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,27 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can find +the full documentation for it [in the repository](https://github.com/changesets/changesets). + +## What is a changeset? + +A changeset is a piece of information about changes made in a branch or commit. It holds three bits of information: + +- What packages need to be released +- What semver bump type each package should receive (major / minor / patch) +- A summary of the changes + +## How do I create a changeset? + +Run `npx changeset` or create a `.md` file in this directory with the following format: + +```markdown +--- +"codeman": patch +--- + +Description of changes +``` + +The frontmatter specifies which package(s) to bump and the bump type. The body is the changelog entry. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 00000000..316cd17e --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "master", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..a1ee8840 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +name: Release + +on: + push: + branches: + - master + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Create release PR or publish + id: changesets + uses: changesets/action@v1 + with: + publish: npm run release + version: npm run version-packages + title: "chore: version packages" + commit: "chore: version packages" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..67802fd9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# codeman + +## 0.2.1 + +### Patch Changes + +- Integrate @changesets/cli for automated releases with changelogs, GitHub Releases, and npm publishing + +## 0.2.0 + +### Minor Changes + +- Initial public release with changesets-based versioning diff --git a/CLAUDE.md b/CLAUDE.md index 62555567..1f2698f0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,11 +31,26 @@ The production server caches static files for 1 hour (`maxAge: '1h'` in `server. ## COM Shorthand (Deployment) -When user says "COM": -1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`) -2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart codeman-web` +Uses [Semantic Versioning](https://semver.org/) (`MAJOR.MINOR.PATCH`) via `@changesets/cli`. -**Version**: 0.1658 (must match `package.json`) +When user says "COM": +1. **Determine bump type**: `COM` = patch (default), `COM minor` = minor, `COM major` = major +2. **Create a changeset file** (no interactive prompts). Write a `.md` file in `.changeset/` with a random filename: + ```bash + cat > .changeset/$(openssl rand -hex 4).md << 'CHANGESET' + --- + "codeman": patch + --- + + Description of changes + CHANGESET + ``` + Replace `patch` with `minor` or `major` as needed. Include `"xterm-zerolag-input": patch` on a separate line if that package changed too. +3. **Consume the changeset**: `npm run version-packages` (bumps versions in `package.json` files and updates `CHANGELOG.md`) +4. **Sync CLAUDE.md version**: Update the `**Version**` line below to match the new version from `package.json` +5. **Commit and deploy**: `git add -A && git commit -m "chore: version packages" && git push && npm run build && systemctl --user restart codeman-web` + +**Version**: 0.2.1 (must match `package.json`) ## Project Overview diff --git a/install.sh b/install.sh index 6f0168a0..0266f1c4 100755 --- a/install.sh +++ b/install.sh @@ -23,7 +23,7 @@ TARGET_NODE_VERSION="${CODEMAN_NODE_VERSION:-22}" NONINTERACTIVE="${CODEMAN_NONINTERACTIVE:-0}" SKIP_SYSTEMD="${CODEMAN_SKIP_SYSTEMD:-0}" -# Claude CLI search paths (from src/session.ts) +# Claude CLI search paths (from src/utils/claude-cli-resolver.ts) CLAUDE_SEARCH_PATHS=( "$HOME/.local/bin/claude" "$HOME/.claude/local/claude" @@ -32,6 +32,17 @@ CLAUDE_SEARCH_PATHS=( "$HOME/bin/claude" ) +# OpenCode CLI search paths (from src/utils/opencode-cli-resolver.ts) +OPENCODE_SEARCH_PATHS=( + "$HOME/.opencode/bin/opencode" + "$HOME/.local/bin/opencode" + "/usr/local/bin/opencode" + "$HOME/go/bin/opencode" + "$HOME/.bun/bin/opencode" + "$HOME/.npm-global/bin/opencode" + "$HOME/bin/opencode" +) + # ============================================================================ # Color Output # ============================================================================ @@ -273,6 +284,34 @@ get_claude_path() { done } +check_opencode() { + if command -v opencode &>/dev/null; then + return 0 + fi + + for path in "${OPENCODE_SEARCH_PATHS[@]}"; do + if [[ -x "$path" ]]; then + return 0 + fi + done + + return 1 +} + +get_opencode_path() { + if command -v opencode &>/dev/null; then + command -v opencode + return + fi + + for path in "${OPENCODE_SEARCH_PATHS[@]}"; do + if [[ -x "$path" ]]; then + echo "$path" + return + fi + done +} + # ============================================================================ # Dependency Installation # ============================================================================ @@ -688,8 +727,10 @@ EOF loginctl enable-linger "$USER" 2>/dev/null || true fi - success "Systemd service installed at $service_file" - info "Start with: systemctl --user start codeman-web" + # Start the service immediately + systemctl --user start codeman-web.service 2>/dev/null || true + + success "Systemd service installed and started" } # ============================================================================ @@ -806,24 +847,72 @@ main() { fi fi - # Claude CLI (required) - info "Checking Claude CLI..." - if ! check_claude; then - info "Installing Claude CLI..." - download_to_stdout https://claude.ai/install.sh | bash - # Rehash to pick up new binary - hash -r 2>/dev/null || true - if check_claude; then - local claude_path - claude_path=$(get_claude_path) - success "Claude CLI installed at $claude_path" + # AI CLI (at least one required: Claude Code or OpenCode) + local has_claude=false + local has_opencode=false + + info "Checking AI CLI tools..." + if check_claude; then + has_claude=true + success "Claude Code found at $(get_claude_path)" + fi + if check_opencode; then + has_opencode=true + success "OpenCode found at $(get_opencode_path)" + fi + + if [[ "$has_claude" == "false" ]] && [[ "$has_opencode" == "false" ]]; then + echo "" + warn "No AI CLI found. Codeman requires at least one: Claude Code or OpenCode." + echo "" + echo -e " ${BOLD}Which AI CLI would you like to install?${NC}" + echo -e " ${CYAN}1)${NC} Claude Code (Anthropic)" + echo -e " ${CYAN}2)${NC} OpenCode (open-source)" + echo -e " ${CYAN}3)${NC} Both" + echo "" + + local cli_choice="" + if [[ "$NONINTERACTIVE" == "1" ]] || [[ ! -t 0 ]]; then + # Non-interactive: default to Claude Code + cli_choice="1" else - die "Claude CLI installation failed. Install manually with: curl -fsSL https://claude.ai/install.sh | bash" + while true; do + echo -en "${CYAN}Choose [1/2/3]:${NC} " >&2 + read -r cli_choice + case "$cli_choice" in + 1|2|3) break ;; + *) echo "Please enter 1, 2, or 3." >&2 ;; + esac + done + fi + + if [[ "$cli_choice" == "1" ]] || [[ "$cli_choice" == "3" ]]; then + info "Installing Claude Code CLI..." + download_to_stdout https://claude.ai/install.sh | bash + hash -r 2>/dev/null || true + if check_claude; then + has_claude=true + success "Claude Code installed at $(get_claude_path)" + else + warn "Claude Code installation failed." + fi + fi + + if [[ "$cli_choice" == "2" ]] || [[ "$cli_choice" == "3" ]]; then + info "Installing OpenCode CLI..." + download_to_stdout https://opencode.ai/install | bash + hash -r 2>/dev/null || true + if check_opencode; then + has_opencode=true + success "OpenCode installed at $(get_opencode_path)" + else + warn "OpenCode installation failed." + fi + fi + + if [[ "$has_claude" == "false" ]] && [[ "$has_opencode" == "false" ]]; then + die "At least one AI CLI is required. Install manually and re-run the installer." fi - else - local claude_path - claude_path=$(get_claude_path) - success "Claude CLI found at $claude_path" fi echo "" @@ -919,17 +1008,40 @@ main() { echo -e "${GREEN}${BOLD} Codeman installed successfully!${NC}" echo -e "${GREEN}${BOLD}============================================================${NC}" echo "" - echo -e " ${BOLD}Quick Start:${NC}" - echo "" - echo -e " ${CYAN}# Start the web server${NC}" - echo -e " codeman web" - echo "" - echo -e " ${CYAN}# Start with HTTPS (only needed for remote access)${NC}" - echo -e " codeman web --https" - echo "" - echo -e " ${CYAN}# Open in browser${NC}" - echo -e " http://localhost:3000" - echo "" + + # Check if systemd service is running (we just started it above) + local service_running=false + if systemctl --user is-active codeman-web.service &>/dev/null; then + service_running=true + fi + + if [[ "$service_running" == "true" ]]; then + echo -e " ${GREEN}${BOLD}Codeman is running now!${NC}" + echo "" + echo -e " ${CYAN}# Open in browser${NC}" + echo -e " http://localhost:3000" + echo "" + echo -e " ${BOLD}Manage the service:${NC}" + echo "" + echo -e " ${CYAN}systemctl --user stop codeman-web${NC} # Stop" + echo -e " ${CYAN}systemctl --user restart codeman-web${NC} # Restart" + echo -e " ${CYAN}systemctl --user status codeman-web${NC} # Check status" + echo -e " ${CYAN}journalctl --user -u codeman-web -f${NC} # View logs" + echo "" + else + echo -e " ${BOLD}Quick Start:${NC}" + echo "" + echo -e " ${CYAN}# Start the web server${NC}" + echo -e " codeman web" + echo "" + echo -e " ${CYAN}# Start with HTTPS (only needed for remote access)${NC}" + echo -e " codeman web --https" + echo "" + echo -e " ${CYAN}# Open in browser${NC}" + echo -e " http://localhost:3000" + echo "" + fi + echo -e " ${BOLD}Mobile Access (Termius/SSH):${NC}" echo "" echo -e " ${CYAN}sc${NC} # Interactive tmux session chooser" @@ -937,33 +1049,27 @@ main() { echo -e " ${CYAN}sc -h${NC} # Help" echo "" - if [[ "$os" == "linux" ]] && [[ -f "$HOME/.config/systemd/user/codeman-web.service" ]]; then - echo -e " ${BOLD}Systemd Service:${NC}" - echo "" - echo -e " ${CYAN}systemctl --user start codeman-web${NC} # Start" - echo -e " ${CYAN}systemctl --user status codeman-web${NC} # Check status" - echo -e " ${CYAN}journalctl --user -u codeman-web -f${NC} # View logs" - echo "" - fi - echo -e " ${BOLD}Documentation:${NC}" echo -e " https://github.com/Ark0N/Codeman" echo "" - if ! check_claude; then - echo -e " ${YELLOW}${BOLD}Reminder:${NC} Install Claude CLI to start using Codeman:" - echo -e " ${CYAN}curl -fsSL https://claude.ai/install.sh | bash${NC}" + if ! check_claude && ! check_opencode; then + echo -e " ${YELLOW}${BOLD}Reminder:${NC} Install at least one AI CLI to start using Codeman:" + echo -e " ${CYAN}curl -fsSL https://claude.ai/install.sh | bash${NC} # Claude Code" + echo -e " ${CYAN}curl -fsSL https://opencode.ai/install | bash${NC} # OpenCode" echo "" fi - # Check if PATH needs reload in user's shell - local profile - profile=$(detect_shell_profile) - if ! command -v codeman &>/dev/null 2>&1; then - echo -e " ${YELLOW}Run this to start using codeman now:${NC}" - echo "" - echo -e " ${CYAN}source $profile && codeman web${NC}" - echo "" + # Check if PATH needs reload in user's shell (only relevant if service not running) + if [[ "$service_running" != "true" ]]; then + local profile + profile=$(detect_shell_profile) + if ! command -v codeman &>/dev/null 2>&1; then + echo -e " ${YELLOW}Run this to start using codeman now:${NC}" + echo "" + echo -e " ${CYAN}source $profile && codeman web${NC}" + echo "" + fi fi } @@ -984,8 +1090,71 @@ update() { echo "" } +uninstall() { + print_banner + info "Uninstalling Codeman..." + echo "" + + # Stop and remove systemd service + if systemctl --user is-active codeman-web.service &>/dev/null; then + info "Stopping codeman-web service..." + systemctl --user stop codeman-web.service + fi + if systemctl --user is-enabled codeman-web.service &>/dev/null 2>&1; then + info "Disabling codeman-web service..." + systemctl --user disable codeman-web.service 2>/dev/null || true + fi + local service_file="$HOME/.config/systemd/user/codeman-web.service" + if [[ -f "$service_file" ]]; then + rm -f "$service_file" + systemctl --user daemon-reload 2>/dev/null || true + success "Systemd service removed" + fi + + # Remove symlinks + local symlink_dir="$HOME/.local/bin" + if [[ -L "$symlink_dir/codeman" ]]; then + rm -f "$symlink_dir/codeman" + success "Removed symlink: $symlink_dir/codeman" + fi + if [[ -L "$symlink_dir/tmux-chooser" ]]; then + rm -f "$symlink_dir/tmux-chooser" + success "Removed symlink: $symlink_dir/tmux-chooser" + fi + + # Remove install directory + if [[ -d "$INSTALL_DIR" ]]; then + if prompt_yes_no "Remove installation directory ($INSTALL_DIR)?"; then + rm -rf "$INSTALL_DIR" + success "Removed $INSTALL_DIR" + else + info "Kept $INSTALL_DIR" + fi + fi + + # Ask about data directory + local data_dir="$HOME/.codeman" + if [[ -d "$data_dir" ]]; then + warn "Data directory exists at $data_dir (contains sessions, settings, state)" + if prompt_yes_no "Remove data directory ($data_dir)?" "n"; then + rm -rf "$data_dir" + success "Removed $data_dir" + else + info "Kept $data_dir" + fi + fi + + echo "" + success "Codeman uninstalled." + echo "" + echo -e " ${DIM}Note: Shell profile entries (PATH, sc alias) were not removed.${NC}" + echo -e " ${DIM}You can remove them manually from $(detect_shell_profile)${NC}" + echo "" +} + # Wrap in main to prevent partial execution on curl | bash case "${1:-}" in - update) update ;; - *) main "$@" ;; + update) update ;; + uninstall) uninstall ;; + *) main "$@" ;; esac diff --git a/package-lock.json b/package-lock.json index ccbe53ce..9e348367 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codeman", - "version": "0.1655", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codeman", - "version": "0.1655", + "version": "0.2.0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -34,6 +34,7 @@ "codeman": "dist/index.js" }, "devDependencies": { + "@changesets/cli": "^2.29.8", "@types/node": "^20.19.33", "@types/pngjs": "^6.0.5", "@types/react": "^19.2.14", @@ -54,6 +55,31 @@ "node": ">=18.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -112,6 +138,16 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", @@ -136,6 +172,426 @@ "node": ">=18" } }, + "node_modules/@changesets/apply-release-plan": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.0.14.tgz", + "integrity": "sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/config": "^3.1.2", + "@changesets/get-version-range-type": "^0.4.0", + "@changesets/git": "^3.0.4", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "detect-indent": "^6.0.0", + "fs-extra": "^7.0.1", + "lodash.startcase": "^4.4.0", + "outdent": "^0.5.0", + "prettier": "^2.7.1", + "resolve-from": "^5.0.0", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/apply-release-plan/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/@changesets/apply-release-plan/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@changesets/assemble-release-plan": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.9.tgz", + "integrity": "sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/changelog-git": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.1.tgz", + "integrity": "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0" + } + }, + "node_modules/@changesets/cli": { + "version": "2.29.8", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.29.8.tgz", + "integrity": "sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/apply-release-plan": "^7.0.14", + "@changesets/assemble-release-plan": "^6.0.9", + "@changesets/changelog-git": "^0.2.1", + "@changesets/config": "^3.1.2", + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/get-release-plan": "^4.0.14", + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.6", + "@changesets/should-skip-package": "^0.1.2", + "@changesets/types": "^6.1.0", + "@changesets/write": "^0.4.0", + "@inquirer/external-editor": "^1.0.2", + "@manypkg/get-packages": "^1.1.3", + "ansi-colors": "^4.1.3", + "ci-info": "^3.7.0", + "enquirer": "^2.4.1", + "fs-extra": "^7.0.1", + "mri": "^1.2.0", + "p-limit": "^2.2.0", + "package-manager-detector": "^0.2.0", + "picocolors": "^1.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.5.3", + "spawndamnit": "^3.0.1", + "term-size": "^2.1.0" + }, + "bin": { + "changeset": "bin.js" + } + }, + "node_modules/@changesets/cli/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@changesets/config": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.1.2.tgz", + "integrity": "sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/get-dependents-graph": "^2.1.3", + "@changesets/logger": "^0.1.1", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "fs-extra": "^7.0.1", + "micromatch": "^4.0.8" + } + }, + "node_modules/@changesets/errors": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.2.0.tgz", + "integrity": "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==", + "dev": true, + "license": "MIT", + "dependencies": { + "extendable-error": "^0.1.5" + } + }, + "node_modules/@changesets/get-dependents-graph": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.3.tgz", + "integrity": "sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "picocolors": "^1.1.0", + "semver": "^7.5.3" + } + }, + "node_modules/@changesets/get-release-plan": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.14.tgz", + "integrity": "sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/assemble-release-plan": "^6.0.9", + "@changesets/config": "^3.1.2", + "@changesets/pre": "^2.0.2", + "@changesets/read": "^0.6.6", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" + } + }, + "node_modules/@changesets/get-version-range-type": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", + "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@changesets/git": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.4.tgz", + "integrity": "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@manypkg/get-packages": "^1.1.3", + "is-subdir": "^1.1.1", + "micromatch": "^4.0.8", + "spawndamnit": "^3.0.1" + } + }, + "node_modules/@changesets/logger": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.1.1.tgz", + "integrity": "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/parse": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.2.tgz", + "integrity": "sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "js-yaml": "^4.1.1" + } + }, + "node_modules/@changesets/pre": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.2.tgz", + "integrity": "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^0.2.0", + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3", + "fs-extra": "^7.0.1" + } + }, + "node_modules/@changesets/read": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.6.tgz", + "integrity": "sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/git": "^3.0.4", + "@changesets/logger": "^0.1.1", + "@changesets/parse": "^0.4.2", + "@changesets/types": "^6.1.0", + "fs-extra": "^7.0.1", + "p-filter": "^2.1.0", + "picocolors": "^1.1.0" + } + }, + "node_modules/@changesets/should-skip-package": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.2.tgz", + "integrity": "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "@manypkg/get-packages": "^1.1.3" + } + }, + "node_modules/@changesets/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", + "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@changesets/write": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.4.0.tgz", + "integrity": "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^6.1.0", + "fs-extra": "^7.0.1", + "human-id": "^4.1.1", + "prettier": "^2.7.1" + } + }, + "node_modules/@changesets/write/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@emnapi/core": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", @@ -829,6 +1285,28 @@ "glob": "^11.0.0" } }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@isaacs/cliui": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", @@ -892,6 +1370,78 @@ "node": ">=8" } }, + "node_modules/@manypkg/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@types/node": "^12.7.1", + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" + } + }, + "node_modules/@manypkg/find-root/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@manypkg/find-root/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@manypkg/get-packages": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", + "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.5.5", + "@changesets/types": "^4.0.1", + "@manypkg/find-root": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "^11.0.0", + "read-yaml-file": "^1.1.0" + } + }, + "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", + "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@manypkg/get-packages/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/@module-federation/error-codes": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.22.0.tgz", @@ -957,6 +1507,44 @@ "@tybys/wasm-util": "^0.10.1" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@pinojs/redact": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", @@ -3007,6 +3595,16 @@ "ajv": "^8.8.2" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3051,6 +3649,16 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/asn1.js": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", @@ -3098,6 +3706,15 @@ "js-tokens": "^10.0.0" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -3291,6 +3908,19 @@ "node": ">=10.0.0" } }, + "node_modules/better-path-resolve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", + "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-windows": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -3420,6 +4050,22 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3481,6 +4127,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -3538,6 +4191,22 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -3571,6 +4240,21 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -3692,6 +4376,31 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -3709,6 +4418,65 @@ "node": ">= 14" } }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -3735,6 +4503,15 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -3759,6 +4536,18 @@ "node": ">= 14" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3777,6 +4566,16 @@ "node": ">=6" } }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/devtools-protocol": { "version": "0.0.1566079", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz", @@ -3790,6 +4589,19 @@ "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", "license": "MIT" }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dotenv": { "version": "17.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", @@ -3802,6 +4614,23 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -3896,6 +4725,35 @@ "node": ">=10.13.0" } }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -3925,6 +4783,30 @@ "stackframe": "^1.3.4" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -3932,6 +4814,39 @@ "dev": true, "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", @@ -4166,6 +5081,13 @@ "node": ">=12.0.0" } }, + "node_modules/extendable-error": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", + "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", + "dev": true, + "license": "MIT" + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -4205,6 +5127,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4382,6 +5321,40 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/fs-monkey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", @@ -4402,6 +5375,18 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -4411,6 +5396,49 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -4496,6 +5524,42 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "license": "BSD-2-Clause" }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -4511,6 +5575,69 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-entities": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", @@ -4590,6 +5717,16 @@ "node": ">= 14" } }, + "node_modules/human-id": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", + "integrity": "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4599,6 +5736,23 @@ "node": ">=10.17.0" } }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", @@ -4631,6 +5785,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -4746,6 +5910,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -4758,6 +5931,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-subdir": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", + "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "better-path-resolve": "1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -4885,6 +6081,92 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "24.1.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.3.tgz", + "integrity": "sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "cssstyle": "^4.0.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.4", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -4928,6 +6210,16 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -5047,6 +6339,13 @@ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "license": "MIT" }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "11.2.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", @@ -5094,6 +6393,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mediabunny": { "version": "1.34.5", "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.34.5.tgz", @@ -5129,6 +6440,30 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", @@ -5223,6 +6558,16 @@ "dev": true, "license": "MIT" }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5306,6 +6651,15 @@ "node": ">=8" } }, + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", @@ -5367,6 +6721,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/outdent": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", + "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -5394,6 +6768,16 @@ "node": ">=8" } }, + "node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -5443,6 +6827,16 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/package-manager-detector": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5475,6 +6869,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5509,6 +6918,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -5551,6 +6970,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pino": { "version": "10.3.1", "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", @@ -5870,6 +7299,21 @@ "dev": true, "license": "MIT" }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -6059,6 +7503,53 @@ "node": ">=6" } }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", @@ -6107,6 +7598,46 @@ "node": ">=0.10.0" } }, + "node_modules/read-yaml-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", + "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.6.1", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/read-yaml-file/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/read-yaml-file/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/readable-stream": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", @@ -6206,6 +7737,15 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "license": "ISC" }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -6296,6 +7836,39 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -6350,6 +7923,21 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -6507,6 +8095,16 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "license": "MIT" }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -6585,6 +8183,17 @@ "source-map": "^0.6.0" } }, + "node_modules/spawndamnit": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", + "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", + "dev": true, + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "cross-spawn": "^7.0.5", + "signal-exit": "^4.0.1" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -6594,6 +8203,13 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -6676,6 +8292,16 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -6714,6 +8340,15 @@ "node": ">=8" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -6765,6 +8400,19 @@ "streamx": "^2.12.5" } }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/terser": { "version": "5.46.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", @@ -7032,6 +8680,36 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", @@ -7094,6 +8772,16 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -7133,6 +8821,19 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7349,6 +9050,21 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/watchpack": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", @@ -7493,6 +9209,49 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", @@ -7588,6 +9347,27 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -7697,6 +9477,17 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "packages/xterm-zerolag-input": { + "version": "0.1.4", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "jsdom": "^24.1.3", + "tsup": "^8.5.1", + "typescript": "^5.5.0", + "vitest": "^2.1.9" + } } } } diff --git a/package.json b/package.json index a7154109..7c7fc587 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codeman", - "version": "0.1658", + "version": "0.2.1", "description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", @@ -19,7 +19,10 @@ "test:watch": "vitest", "test:coverage": "vitest run --coverage", "typecheck": "tsc --noEmit", - "capture:subagents": "node scripts/capture-subagent-screenshots.mjs" + "capture:subagents": "node scripts/capture-subagent-screenshots.mjs", + "changeset": "changeset", + "version-packages": "changeset version", + "release": "changeset publish" }, "keywords": [ "claude", @@ -58,6 +61,7 @@ "zod": "^4.3.6" }, "devDependencies": { + "@changesets/cli": "^2.29.8", "@types/node": "^20.19.33", "@types/pngjs": "^6.0.5", "@types/react": "^19.2.14", diff --git a/packages/xterm-zerolag-input/CHANGELOG.md b/packages/xterm-zerolag-input/CHANGELOG.md new file mode 100644 index 00000000..2e5ff380 --- /dev/null +++ b/packages/xterm-zerolag-input/CHANGELOG.md @@ -0,0 +1,7 @@ +# xterm-zerolag-input + +## 0.1.4 + +### Patch Changes + +- Initial changelog entry for changesets-based versioning diff --git a/src/web/public/app.js b/src/web/public/app.js index 84bcef1a..1c9ae914 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -6337,2945 +6337,26 @@ class CodemanApp { this.renderRalphStatePanel(); } - // ========== Ralph Loop Wizard ========== + // ========== Ralph Loop Wizard (methods in ralph-wizard.js) ========== - // Wizard state + // Wizard state (initialized here, methods loaded from ralph-wizard.js) ralphWizardStep = 1; ralphWizardConfig = { taskDescription: '', completionPhrase: 'COMPLETE', maxIterations: 10, caseName: 'testcase', - enableRespawn: true, - // Plan generation fields - generatedPlan: null, // [{content, priority, enabled, id}] or null + enableRespawn: false, + generatedPlan: null, planGenerated: false, skipPlanGeneration: false, - planDetailLevel: 'detailed', // 'brief', 'standard', 'detailed' + planDetailLevel: 'detailed', + existingPlan: null, + useExistingPlan: false, }; planLoadingTimer = null; planLoadingStartTime = null; - showRalphWizard() { - // Reset wizard state - this.ralphWizardStep = 1; - this.ralphWizardConfig = { - taskDescription: '', - completionPhrase: 'COMPLETE', - maxIterations: 10, - caseName: document.getElementById('quickStartCase')?.value || 'testcase', - enableRespawn: true, - autoStart: false, // Auto-start loop after plan generation - // Plan generation fields - generatedPlan: null, - planGenerated: false, - skipPlanGeneration: false, - planDetailLevel: 'detailed', - // Existing plan detection - existingPlan: null, // { todos, stats, content } from @fix_plan.md - useExistingPlan: false, // User chose to use existing plan - }; - - // Reset UI - document.getElementById('ralphTaskDescription').value = ''; - document.getElementById('ralphCompletionPhrase').value = 'COMPLETE'; - this.selectIterationPreset(10); - // Reset auto-start checkboxes (off by default) - there are two, one in step 2 and one in step 3 - const autoStartEl = document.getElementById('ralphAutoStart'); - const autoStartStep2El = document.getElementById('ralphAutoStartStep2'); - if (autoStartEl) autoStartEl.checked = false; - if (autoStartStep2El) autoStartStep2El.checked = false; - - // Populate case selector - this.populateRalphCaseSelector(); - - // Reset plan generation UI - this.resetPlanGenerationUI(); - - // Check for existing @fix_plan.md in selected case - this.checkExistingFixPlan(); - - // Show wizard modal - this.updateRalphWizardUI(); - const modal = document.getElementById('ralphWizardModal'); - modal.classList.add('active'); - - // Reset wizard position to center - this.wizardPosition = null; - this.planAgentsMinimized = false; - const wizardContent = modal.querySelector('.modal-content'); - if (wizardContent) { - wizardContent.classList.remove('draggable'); - wizardContent.style.left = ''; - wizardContent.style.top = ''; - } - - // Set up wizard dragging - this.setupWizardDragging(); - - // Hide minimize button initially (no agents yet) - this.updateWizardAgentCount(); - - // Activate focus trap (but don't auto-focus since we have specific focus below) - this.activeFocusTrap = new FocusTrap(modal); - this.activeFocusTrap.previouslyFocused = document.activeElement; - modal.addEventListener('keydown', this.activeFocusTrap.boundHandleKeydown); - - document.getElementById('ralphTaskDescription').focus(); - - // Update connection lines to show wizard connections - this.updateConnectionLines(); - } - - closeRalphWizard() { - const modal = document.getElementById('ralphWizardModal'); - modal?.classList.remove('active'); - - // Deactivate focus trap and restore focus - if (this.activeFocusTrap) { - this.activeFocusTrap.deactivate(); - this.activeFocusTrap = null; - } - - // Clean up wizard drag listeners - this.cleanupWizardDragging(); - - // Remove agents minimized tab if exists - this.removeAgentsMinimizedTab(); - - // Hide minimized indicator - this.hideWizardMinimizedIndicator(); - - // Reset wizard position - this.wizardPosition = null; - this.planAgentsMinimized = false; - - // === CRITICAL: Clean up any orphaned plan generation state === - - // Cancel any in-flight plan generation - if (this.activePlanOrchestratorId) { - console.log('[Wizard Close] Cancelling active plan orchestrator:', this.activePlanOrchestratorId); - fetch('/api/cancel-plan-generation', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ orchestratorId: this.activePlanOrchestratorId }), - }).catch(err => console.error('[Wizard Close] Failed to cancel plan:', err)); - this.activePlanOrchestratorId = null; - } - - // Abort any in-flight fetch request - if (this.planGenerationAbortController) { - this.planGenerationAbortController.abort(); - this.planGenerationAbortController = null; - } - - // Clear progress handler to prevent orphaned callbacks - this._planProgressHandler = null; - - // Clear plan loading timers - if (this.planLoadingTimer) { - clearInterval(this.planLoadingTimer); - this.planLoadingTimer = null; - } - if (this.planPhaseTimer) { - clearTimeout(this.planPhaseTimer); - this.planPhaseTimer = null; - } - - // Mark generation as stopped to ignore any late SSE events - this.planGenerationStopped = true; - - // Close all plan subagent windows - this.closePlanSubagentWindows(); - - // Clear wizard minimized windows tracking - this.wizardMinimizedWindows = null; - - // Update monitor panel to reflect changes - this.renderMonitorPlanAgents(); - - // Update connection lines (wizard closed, revert to normal) - this.updateConnectionLines(); - } - - minimizeRalphWizard() { - const modal = document.getElementById('ralphWizardModal'); - modal?.classList.remove('active'); - - // Track windows minimized due to wizard minimize (so we can restore them) - this.wizardMinimizedWindows = new Set(); - - // Minimize all plan subagent windows - for (const [agentId, windowData] of this.planSubagents) { - if (windowData.element && windowData.element.style.display !== 'none') { - windowData.element.style.display = 'none'; - this.wizardMinimizedWindows.add(`plan:${agentId}`); - } - } - - // Minimize all regular subagent windows - for (const [agentId, windowData] of this.subagentWindows) { - if (windowData.element && !windowData.minimized && windowData.element.style.display !== 'none') { - windowData.element.style.display = 'none'; - windowData.hiddenByWizard = true; - this.wizardMinimizedWindows.add(`subagent:${agentId}`); - } - } - - // Show the minimized indicator - this.showWizardMinimizedIndicator(); - - // Update connection lines (all hidden now) - this.updateConnectionLines(); - } - - restoreRalphWizard() { - // Hide the minimized indicator - this.hideWizardMinimizedIndicator(); - - // Show the modal again - const modal = document.getElementById('ralphWizardModal'); - modal?.classList.add('active'); - - // Restore windows that were minimized due to wizard minimize - if (this.wizardMinimizedWindows) { - for (const key of this.wizardMinimizedWindows) { - if (key.startsWith('plan:')) { - const agentId = key.slice(5); - const windowData = this.planSubagents.get(agentId); - if (windowData?.element) { - windowData.element.style.display = ''; - } - } else if (key.startsWith('subagent:')) { - const agentId = key.slice(9); - const windowData = this.subagentWindows.get(agentId); - if (windowData?.element && windowData.hiddenByWizard) { - windowData.element.style.display = ''; - windowData.hiddenByWizard = false; - } - } - } - this.wizardMinimizedWindows.clear(); - } - - // Update connection lines - this.updateConnectionLines(); - } - - showWizardMinimizedIndicator() { - const indicator = document.getElementById('wizardMinimizedIndicator'); - if (!indicator) return; - - indicator.classList.add('visible'); - - // Start time counter if plan generation is active - if (this.planLoadingStartTime) { - this.wizardMinimizedTimer = setInterval(() => { - const elapsed = Math.floor((Date.now() - this.planLoadingStartTime) / 1000); - const timeEl = document.getElementById('wizardMinimizedTime'); - if (timeEl) timeEl.textContent = `${elapsed}s`; - }, 1000); - } - } - - hideWizardMinimizedIndicator() { - const indicator = document.getElementById('wizardMinimizedIndicator'); - if (indicator) indicator.classList.remove('visible'); - - if (this.wizardMinimizedTimer) { - clearInterval(this.wizardMinimizedTimer); - this.wizardMinimizedTimer = null; - } - } - - populateRalphCaseSelector() { - const select = document.getElementById('ralphCaseSelect'); - const quickStartSelect = document.getElementById('quickStartCase'); - - if (quickStartSelect && select) { - select.innerHTML = quickStartSelect.innerHTML; - select.value = this.ralphWizardConfig.caseName; - } - } - - selectIterationPreset(iterations) { - this.ralphWizardConfig.maxIterations = iterations; - - // Update button states - document.querySelectorAll('.iteration-preset-btn').forEach(btn => { - const btnIterations = parseInt(btn.dataset.iterations); - btn.classList.toggle('active', btnIterations === iterations); - }); - } - - // Check for existing @fix_plan.md in the selected case - async checkExistingFixPlan() { - const caseName = this.ralphWizardConfig.caseName; - if (!caseName) return; - - try { - const res = await fetch(`/api/cases/${encodeURIComponent(caseName)}/fix-plan`); - const data = await res.json(); - - if (data.success && data.exists && data.todos?.length > 0) { - this.ralphWizardConfig.existingPlan = { - todos: data.todos, - stats: data.stats, - content: data.content, - }; - this.updateExistingPlanUI(); - } else { - this.ralphWizardConfig.existingPlan = null; - this.updateExistingPlanUI(); - } - } catch (err) { - console.error('Failed to check for existing plan:', err); - this.ralphWizardConfig.existingPlan = null; - } - } - - // Check for existing ralph-wizard research files - async checkExistingWizardFiles() { - const caseName = this.ralphWizardConfig.caseName; - if (!caseName) return; - - try { - const res = await fetch(`/api/cases/${encodeURIComponent(caseName)}/ralph-wizard/files`); - const data = await res.json(); - - if (data.success && data.data?.files?.length > 0) { - this.ralphWizardConfig.existingWizardFiles = data.data.files; - this.updateExistingWizardFilesUI(); - } else { - this.ralphWizardConfig.existingWizardFiles = null; - this.updateExistingWizardFilesUI(); - } - } catch (err) { - console.error('Failed to check for existing wizard files:', err); - this.ralphWizardConfig.existingWizardFiles = null; - } - } - - // Update UI to show existing wizard files option - updateExistingWizardFilesUI() { - const files = this.ralphWizardConfig.existingWizardFiles; - let section = document.getElementById('existingWizardFilesSection'); - - if (!files || files.length === 0) { - section?.remove(); - return; - } - - // Count agents with results - const agentsWithResults = files.filter(f => f.resultFile).length; - - if (!section) { - // Create the section if it doesn't exist - insert before loading state - const loadingState = document.getElementById('planGenerationLoading'); - section = document.createElement('div'); - section.id = 'existingWizardFilesSection'; - section.className = 'existing-wizard-files-section'; - loadingState?.parentNode?.insertBefore(section, loadingState); - } - - section.innerHTML = ` -
-
- ๐Ÿ“ - Previous Research Found -
-

- Found ${agentsWithResults} agent result${agentsWithResults !== 1 ? 's' : ''} from a previous plan generation. -

-
- ${files.filter(f => f.resultFile).map(f => ` - ${this.getAgentTypeLabel(f.agentType)} - `).join('')} -
-
- - -
-
- `; - } - - getAgentTypeLabel(agentType) { - const labels = { - research: 'Research', - requirements: 'Requirements', - architecture: 'Architecture', - testing: 'Testing', - risks: 'Risks', - verification: 'Verification', - execution: 'Execution', - 'final-review': 'Review', - }; - return labels[agentType] || agentType; - } - - async reuseExistingWizardFiles() { - // Load the final result if it exists - const caseName = this.ralphWizardConfig.caseName; - try { - const res = await fetch(`/api/cases/${encodeURIComponent(caseName)}/ralph-wizard/file/${encodeURIComponent('final-result.json')}`); - const data = await res.json(); - - if (data.success && data.data?.parsed?.items) { - // Use the existing plan items - this.ralphWizardConfig.generatedPlan = data.data.parsed.items.map((item, idx) => ({ - content: item.content || item.title || `Task ${idx + 1}`, - priority: item.priority || 'P1', - enabled: true, - id: item.id || `reused-${Date.now()}-${idx}`, - source: item.source, - rationale: item.rationale, - tddPhase: item.tddPhase, - })); - this.ralphWizardConfig.planGenerated = true; - this.ralphWizardConfig.reusingExistingFiles = true; - this.ralphWizardConfig.planCost = 0; - - // Hide the existing files section - document.getElementById('existingWizardFilesSection')?.remove(); - - // Show the plan editor - this.renderPlanEditor(); - this.updateDetailLevelButtons(); - } else { - // No final result, just skip to generate with existing research - this.ralphWizardConfig.reusingExistingFiles = true; - this.generatePlan(); - } - } catch (err) { - console.error('Failed to load existing results:', err); - // Fall back to regenerating - this.generatePlan(); - } - } - - regenerateAllWizardFiles() { - // Clear the reuse flag and hide the section - this.ralphWizardConfig.reusingExistingFiles = false; - document.getElementById('existingWizardFilesSection')?.remove(); - this.generatePlan(); - } - - // Called when case selector changes - onRalphCaseChange() { - const caseName = document.getElementById('ralphCaseSelect')?.value; - if (caseName) { - this.ralphWizardConfig.caseName = caseName; - this.ralphWizardConfig.existingPlan = null; - this.ralphWizardConfig.useExistingPlan = false; - this.ralphWizardConfig.existingWizardFiles = null; - this.checkExistingFixPlan(); - } - } - - // Update UI to show existing plan indicator - updateExistingPlanUI() { - const existingPlanBadge = document.getElementById('existingPlanBadge'); - const existingPlanSection = document.getElementById('existingPlanSection'); - const plan = this.ralphWizardConfig.existingPlan; - - if (existingPlanBadge) { - if (plan) { - const pending = plan.stats?.pending || 0; - const total = plan.stats?.total || 0; - existingPlanBadge.textContent = `${pending}/${total} tasks remaining`; - existingPlanBadge.style.display = ''; - } else { - existingPlanBadge.style.display = 'none'; - } - } - - if (existingPlanSection) { - if (plan) { - const pending = plan.stats?.pending || 0; - const completed = plan.stats?.completed || 0; - const total = plan.stats?.total || 0; - existingPlanSection.innerHTML = ` -
-
- ๐Ÿ“‹ - Existing @fix_plan.md found -
-
- ${pending} pending - ${completed} completed - ${total} total -
-
- - -
-
- `; - existingPlanSection.classList.remove('hidden'); - } else { - existingPlanSection.classList.add('hidden'); - } - } - } - - // Use the existing @fix_plan.md - useExistingPlan() { - const plan = this.ralphWizardConfig.existingPlan; - if (!plan) return; - - // Stop any ongoing plan generation - this.stopPlanGeneration(); - - // Convert existing todos to generatedPlan format (only pending items) - const pendingTodos = plan.todos.filter(t => t.status === 'pending' || t.status === 'in_progress'); - this.ralphWizardConfig.generatedPlan = pendingTodos.map((todo, idx) => ({ - content: todo.content, - priority: todo.priority, - enabled: true, - id: `existing-${Date.now()}-${idx}`, - })); - this.ralphWizardConfig.planGenerated = true; - this.ralphWizardConfig.useExistingPlan = true; - this.ralphWizardConfig.planCost = 0; // No cost for existing plan - - this.renderPlanEditor(); - this.updateDetailLevelButtons(); - } - - // Sync auto-start checkboxes between step 2 and step 3 - syncAutoStartCheckbox(sourceEl) { - const step2El = document.getElementById('ralphAutoStartStep2'); - const step3El = document.getElementById('ralphAutoStart'); - const isChecked = sourceEl.checked; - - if (step2El) step2El.checked = isChecked; - if (step3El) step3El.checked = isChecked; - - // Update config - this.ralphWizardConfig.autoStart = isChecked; - } - - // Stop any ongoing plan generation (abort fetch, clear timers, hide spinner) - stopPlanGeneration() { - // Abort ongoing fetch - if (this.planGenerationAbortController) { - this.planGenerationAbortController.abort(); - this.planGenerationAbortController = null; - } - - // Clear timers - if (this.planLoadingTimer) { - clearInterval(this.planLoadingTimer); - this.planLoadingTimer = null; - } - if (this.planPhaseTimer) { - clearInterval(this.planPhaseTimer); - this.planPhaseTimer = null; - } - - // Hide loading spinner - document.getElementById('planGenerationLoading')?.classList.add('hidden'); - } - - // Generate a new plan (ignore existing) - generateNewPlan() { - this.ralphWizardConfig.useExistingPlan = false; - document.getElementById('existingPlanSection')?.classList.add('hidden'); - this.generatePlan(); - } - - ralphWizardNext() { - if (this.ralphWizardStep === 1) { - // Validate step 1 - const taskDescription = document.getElementById('ralphTaskDescription').value.trim(); - const completionPhrase = document.getElementById('ralphCompletionPhrase').value.trim() || 'COMPLETE'; - const caseName = document.getElementById('ralphCaseSelect').value; - - if (!taskDescription) { - this.showToast('Please enter a task description', 'error'); - document.getElementById('ralphTaskDescription').focus(); - return; - } - - // Save config - this.ralphWizardConfig.taskDescription = taskDescription; - this.ralphWizardConfig.completionPhrase = completionPhrase.toUpperCase(); - this.ralphWizardConfig.caseName = caseName; - - // Move to step 2 (plan generation) - this.ralphWizardStep = 2; - this.updateRalphWizardUI(); - - // Check for existing wizard files first, then existing plan - this.checkExistingWizardFiles().then(() => { - // If there's an existing plan (@fix_plan.md), show the option to use it - if (this.ralphWizardConfig.existingPlan) { - this.updateExistingPlanUI(); - } else if (!this.ralphWizardConfig.existingWizardFiles?.length) { - // No existing files, auto-start plan generation - this.generatePlan(); - } - // If existingWizardFiles exists, the UI will show and wait for user choice - }); - } else if (this.ralphWizardStep === 2) { - // Must have generated or skipped plan - if (!this.ralphWizardConfig.planGenerated && !this.ralphWizardConfig.skipPlanGeneration) { - this.showToast('Wait for plan generation or skip', 'warning'); - return; - } - - // Generate preview - this.updateRalphPromptPreview(); - - // Move to step 3 (launch) - this.ralphWizardStep = 3; - this.updateRalphWizardUI(); - } - } - - ralphWizardBack() { - if (this.ralphWizardStep === 3) { - this.ralphWizardStep = 2; - this.updateRalphWizardUI(); - } else if (this.ralphWizardStep === 2) { - this.ralphWizardStep = 1; - this.updateRalphWizardUI(); - } - } - - updateRalphWizardUI() { - const step = this.ralphWizardStep; - - // Update progress indicators - document.querySelectorAll('.wizard-step').forEach(el => { - const stepNum = parseInt(el.dataset.step); - el.classList.toggle('active', stepNum === step); - el.classList.toggle('completed', stepNum < step); - }); - - // Show/hide pages (now 3 pages) - document.getElementById('ralphWizardStep1').classList.toggle('hidden', step !== 1); - document.getElementById('ralphWizardStep2').classList.toggle('hidden', step !== 2); - document.getElementById('ralphWizardStep3').classList.toggle('hidden', step !== 3); - - // Show/hide buttons - document.getElementById('ralphBackBtn').style.display = step === 1 ? 'none' : 'block'; - document.getElementById('ralphNextBtn').style.display = step === 3 ? 'none' : 'block'; - document.getElementById('ralphStartBtn').style.display = step === 3 ? 'block' : 'none'; - } - - updateRalphPromptPreview() { - const config = this.ralphWizardConfig; - const preview = document.getElementById('ralphPromptPreview'); - const hasPlan = config.generatedPlan && config.generatedPlan.filter(i => i.enabled).length > 0; - - // Build the formatted prompt (abbreviated for preview) - let prompt = config.taskDescription; - prompt += '\n\n---\n\n'; - - if (hasPlan) { - prompt += '## Task Plan\n'; - prompt += '๐Ÿ“‹ @fix_plan.md will be created with your task items\n\n'; - } - - prompt += '## Iteration Protocol\n'; - prompt += 'โ€ข Check previous work โ€ข Make progress โ€ข Commit changes\n\n'; - - prompt += '## Completion Criteria\n'; - prompt += `Output \`${config.completionPhrase}\` when done\n\n`; - - prompt += '## If Stuck\n'; - prompt += 'Output `BLOCKED` with explanation'; - - // Show preview with highlighting (escape first, then apply formatting) - const escapedPrompt = this.escapeHtml(prompt); - const highlightedPrompt = escapedPrompt - .replace(/<promise>/g, '<promise>') - .replace(/<\/promise>/g, '</promise>') - .replace(/`([^`]+)`/g, '$1'); - - preview.innerHTML = highlightedPrompt; - - // Update summary - document.getElementById('summaryPhrase').textContent = config.completionPhrase; - document.getElementById('summaryIterations').textContent = - config.maxIterations === 0 ? 'Unlimited' : config.maxIterations; - document.getElementById('summaryCase').textContent = config.caseName; - - // Show plan status in summary if plan was generated - const planSummary = document.getElementById('summaryPlan'); - if (planSummary) { - if (config.generatedPlan && config.generatedPlan.length > 0) { - const enabledCount = config.generatedPlan.filter(i => i.enabled).length; - planSummary.textContent = `${enabledCount} item${enabledCount !== 1 ? 's' : ''}`; - planSummary.parentElement.style.display = ''; - } else { - planSummary.parentElement.style.display = 'none'; - } - } - } - - // ========== Plan Generation ========== - - resetPlanGenerationUI() { - // Hide all plan generation states - document.getElementById('existingPlanSection')?.classList.add('hidden'); - document.getElementById('planGenerationLoading')?.classList.add('hidden'); - document.getElementById('planGenerationError')?.classList.add('hidden'); - document.getElementById('planEditor')?.classList.add('hidden'); - - // Reset spinner visibility (in case it was hidden after "Done!") - const spinnerEl = document.querySelector('.plan-spinner'); - if (spinnerEl) spinnerEl.style.display = ''; - - // Reset stopped indicator - const stoppedIndicator = document.getElementById('planStoppedIndicator'); - if (stoppedIndicator) stoppedIndicator.style.display = 'none'; - - // Reset existing plan badge - const badge = document.getElementById('existingPlanBadge'); - if (badge) badge.style.display = 'none'; - } - - async generatePlan() { - const config = this.ralphWizardConfig; - const isDetailed = config.planDetailLevel === 'detailed'; - - // Stop any existing generation first - this.stopPlanGeneration(); - - // Close old plan subagent windows and clear their state - // This ensures clicking on prompts shows fresh data, not cached from previous runs - this.closePlanSubagentWindows(); - - // Reset stopped flag to allow new SSE events - this.planGenerationStopped = false; - - // Create abort controller for this generation - this.planGenerationAbortController = new AbortController(); - - // Show loading state, hide other sections - document.getElementById('existingPlanSection')?.classList.add('hidden'); - document.getElementById('planGenerationError')?.classList.add('hidden'); - document.getElementById('planEditor')?.classList.add('hidden'); - document.getElementById('planGenerationLoading')?.classList.remove('hidden'); - - // Different phases for detailed vs standard generation - const standardPhases = [ - { time: 0, title: 'Starting Opus 4.5...', hint: 'Initializing deep reasoning model' }, - { time: 3, title: 'Analyzing task requirements...', hint: 'Understanding the scope and complexity' }, - { time: 8, title: 'Identifying components...', hint: 'Breaking down into modules and features' }, - { time: 15, title: 'Planning TDD approach...', hint: 'Designing test-first implementation strategy' }, - { time: 25, title: 'Generating implementation steps...', hint: 'Creating detailed action items with tests' }, - { time: 40, title: 'Adding verification checkpoints...', hint: 'Ensuring each phase has validation' }, - { time: 55, title: 'Reviewing for completeness...', hint: 'Checking all requirements are covered' }, - { time: 70, title: 'Finalizing plan...', hint: 'Organizing and prioritizing steps' }, - { time: 90, title: 'Still working...', hint: 'Complex tasks take longer - hang tight!' }, - ]; - - const detailedPhases = [ - // Phase 0: Research (can take up to 10 minutes for complex tasks) - { time: 0, title: 'Starting research agent...', hint: 'Gathering external resources and codebase context' }, - { time: 30, title: 'Research agent working...', hint: 'Searching docs, GitHub repos, and analyzing codebase' }, - { time: 60, title: 'Research continuing...', hint: 'Web search and codebase exploration in progress' }, - { time: 120, title: 'Research agent deep diving...', hint: 'Complex tasks require thorough research' }, - { time: 180, title: 'Research almost complete...', hint: 'Compiling findings and recommendations' }, - // Phase 1: Parallel Analysis (after research completes) - // Note: These times are fallbacks - real-time SSE updates override them - { time: 300, title: 'Spawning analysis subagents...', hint: 'Starting 4 specialist agents in parallel' }, - { time: 330, title: 'Subagents analyzing...', hint: 'Requirements, Architecture, Testing, Risk analysts working' }, - { time: 400, title: 'Subagents completing...', hint: 'Collecting analysis results' }, - // Phase 2+: Synthesis and verification - { time: 450, title: 'Synthesizing results...', hint: 'Merging and deduplicating items' }, - { time: 500, title: 'Running verification...', hint: 'Quality assurance and priority assignment' }, - { time: 550, title: 'Optimizing execution...', hint: 'Planning parallelization for Claude Code' }, - { time: 600, title: 'Final review...', hint: 'Holistic validation and gap detection' }, - { time: 660, title: 'Still working...', hint: 'Complex tasks take longer - hang tight!' }, - ]; - - const phases = isDetailed ? detailedPhases : standardPhases; - - // Start elapsed time and phase display - this.planLoadingStartTime = Date.now(); - const timeEl = document.getElementById('planLoadingTime'); - const titleEl = document.getElementById('planLoadingTitle'); - const hintEl = document.getElementById('planLoadingHint'); - - if (timeEl) timeEl.textContent = '0s'; - if (titleEl) titleEl.textContent = phases[0].title; - if (hintEl) hintEl.textContent = phases[0].hint; - - let currentPhaseIndex = 0; - this.planLoadingTimer = setInterval(() => { - const elapsed = Math.floor((Date.now() - this.planLoadingStartTime) / 1000); - if (timeEl) timeEl.textContent = `${elapsed}s`; - - // Update phase based on elapsed time - for (let i = phases.length - 1; i >= 0; i--) { - if (elapsed >= phases[i].time && i > currentPhaseIndex) { - currentPhaseIndex = i; - if (titleEl) titleEl.textContent = phases[i].title; - if (hintEl) hintEl.textContent = phases[i].hint; - break; - } - } - }, 1000); - - // Listen for real-time progress updates from detailed generation - const handlePlanProgress = (event) => { - if (event.type === 'plan:progress' && event.data) { - const titleEl = document.getElementById('planLoadingTitle'); - const hintEl = document.getElementById('planLoadingHint'); - if (titleEl && event.data.phase) { - const phaseLabels = { - 'research': 'Research agent working...', - 'parallel-analysis': 'Spawning analysis subagents...', - 'subagent': event.data.detail || 'Subagent working...', - 'synthesis': 'Synthesizing results...', - 'verification': 'Running verification...', - 'review-injection': 'Adding review tasks...', - 'execution-optimization': 'Optimizing for Claude Code...', - 'final-review': 'Running final review...', - }; - titleEl.textContent = phaseLabels[event.data.phase] || event.data.phase; - } - if (hintEl && event.data.detail) { - hintEl.textContent = event.data.detail; - } - } - }; - - // Add SSE listener for detailed mode progress - if (isDetailed) { - this._planProgressHandler = handlePlanProgress; - } - - try { - // Use different endpoint for detailed mode - const endpoint = isDetailed ? '/api/generate-plan-detailed' : '/api/generate-plan'; - const body = isDetailed - ? { taskDescription: config.taskDescription, caseName: config.caseName } - : { taskDescription: config.taskDescription, detailLevel: config.planDetailLevel }; - - // Retry logic for network errors - const maxRetries = 3; - let lastError = null; - let data = null; - - for (let attempt = 0; attempt < maxRetries; attempt++) { - try { - const res = await fetch(endpoint, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - signal: this.planGenerationAbortController?.signal, - }); - data = await res.json(); - break; // Success, exit retry loop - } catch (fetchErr) { - lastError = fetchErr; - // Don't retry if aborted - if (fetchErr.name === 'AbortError') throw fetchErr; - // Network error - retry with exponential backoff - console.warn(`[Plan] Fetch attempt ${attempt + 1} failed:`, fetchErr.message); - if (attempt < maxRetries - 1) { - const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s - const titleEl = document.getElementById('planLoadingTitle'); - const hintEl = document.getElementById('planLoadingHint'); - if (titleEl) titleEl.textContent = 'Connection lost, retrying...'; - if (hintEl) hintEl.textContent = `Attempt ${attempt + 2} of ${maxRetries} in ${delay / 1000}s`; - await new Promise(r => setTimeout(r, delay)); - } - } - } - - if (!data) { - throw lastError || new Error('Failed to fetch after retries'); - } - - // Stop timer - if (this.planLoadingTimer) { - clearInterval(this.planLoadingTimer); - this.planLoadingTimer = null; - } - - // Remove progress handler - this._planProgressHandler = null; - - if (!data.success) { - this.showPlanError(data.error || 'Failed to generate plan'); - return; - } - - if (!data.data?.items || data.data.items.length === 0) { - this.showPlanError('No plan items generated. Try adding more detail to your task.'); - return; - } - - // Show "Done!" with quality info for detailed mode - const doneTitle = document.getElementById('planLoadingTitle'); - const doneHint = document.getElementById('planLoadingHint'); - const spinnerEl = document.querySelector('.plan-spinner'); - - if (doneTitle) doneTitle.textContent = 'Done!'; - if (doneHint) { - if (isDetailed && data.data.metadata?.qualityScore) { - const quality = Math.round(data.data.metadata.qualityScore * 100); - doneHint.textContent = `Generated ${data.data.items.length} steps (Quality: ${quality}%)`; - } else { - doneHint.textContent = `Generated ${data.data.items.length} steps`; - } - } - if (spinnerEl) spinnerEl.style.display = 'none'; - - // Brief pause to show "Done!" before showing editor - await new Promise(r => setTimeout(r, 500)); - - // Store plan with enabled state and IDs - config.generatedPlan = data.data.items.map((item, idx) => ({ - ...item, - enabled: true, - id: `plan-${Date.now()}-${idx}`, - })); - config.planGenerated = true; - config.skipPlanGeneration = false; - config.planCost = data.data.costUsd || 0; - - // Store metadata for detailed mode - if (isDetailed && data.data.metadata) { - config.planMetadata = data.data.metadata; - } - - // Show editor and update detail buttons - this.renderPlanEditor(); - this.updateDetailLevelButtons(); - - // Check for auto-start - if (this.ralphWizardConfig.autoStart) { - console.log('[RalphWizard] Auto-start enabled, starting loop automatically...'); - this.showToast('Plan complete! Auto-starting Ralph Loop...', 'success'); - // Small delay to let user see the plan briefly - await new Promise(r => setTimeout(r, 1500)); - this.startRalphLoop(); - } - - } catch (err) { - // Stop timer - if (this.planLoadingTimer) { - clearInterval(this.planLoadingTimer); - this.planLoadingTimer = null; - } - - // Remove progress handler - this._planProgressHandler = null; - - // Ignore abort errors (user cancelled, e.g., clicked "Use Existing Plan") - if (err.name === 'AbortError') { - console.log('Plan generation aborted by user'); - return; - } - - console.error('Plan generation failed:', err); - this.showPlanError('Network error: ' + err.message); - } - } - - setPlanDetail(level) { - const previousLevel = this.ralphWizardConfig.planDetailLevel; - this.ralphWizardConfig.planDetailLevel = level; - this.updateDetailLevelButtons(); - - // If plan was already generated and level changed, automatically regenerate - if (this.ralphWizardConfig.planGenerated && previousLevel !== level) { - const modeLabel = level === 'detailed' ? 'Enhanced (Multi-Agent)' : level === 'brief' ? 'Brief' : 'Standard'; - console.log(`[Ralph Wizard] Plan mode changed to ${modeLabel}, regenerating...`); - - // Clear current plan and regenerate - this.ralphWizardConfig.generatedPlan = null; - this.ralphWizardConfig.planGenerated = false; - this.ralphWizardConfig.planMetadata = null; - - // Trigger regeneration with visual feedback - this.generatePlan(); - } - } - - updateDetailLevelButtons() { - const level = this.ralphWizardConfig.planDetailLevel; - document.querySelectorAll('.plan-detail-btn').forEach(btn => { - const isActive = btn.dataset.detail === level; - btn.classList.toggle('active', isActive); - - // Add visual indicator for enhanced mode - if (btn.dataset.detail === 'detailed') { - btn.title = 'Enhanced: Uses 4 parallel subagents + verification (slower but more thorough)'; - } else if (btn.dataset.detail === 'standard') { - btn.title = 'Standard: Single-pass generation with Opus 4.5'; - } else { - btn.title = 'Brief: High-level milestones only'; - } - }); - } - - showPlanError(message) { - document.getElementById('planGenerationLoading')?.classList.add('hidden'); - document.getElementById('planEditor')?.classList.add('hidden'); - - const errorEl = document.getElementById('planGenerationError'); - const msgEl = document.getElementById('planErrorMsg'); - const stoppedIndicator = document.getElementById('planStoppedIndicator'); - - // Hide the stopped indicator for real errors, show error message - if (stoppedIndicator) stoppedIndicator.style.display = 'none'; - if (msgEl) msgEl.textContent = message; - errorEl?.classList.remove('hidden'); - } - - // Plan view mode: 'list' (flat) or 'grouped' (by TDD phase) - planViewMode = 'list'; - - renderPlanEditor() { - document.getElementById('planGenerationLoading')?.classList.add('hidden'); - document.getElementById('planGenerationError')?.classList.add('hidden'); - document.getElementById('planEditor')?.classList.remove('hidden'); - - // Close plan subagent windows since generation is complete - this.closePlanSubagentWindows(); - - const list = document.getElementById('planItemsList'); - if (!list) return; - - list.innerHTML = ''; - const items = this.ralphWizardConfig.generatedPlan || []; - const cost = this.ralphWizardConfig.planCost || 0; - const metadata = this.ralphWizardConfig.planMetadata; - - // Update header with item count, cost, and quality score if available - const statsEl = document.getElementById('planStats'); - if (statsEl) { - let statsText = `${items.length} steps ยท $${cost.toFixed(3)}`; - - // Show quality score and source info for detailed mode - if (metadata) { - const quality = Math.round(metadata.qualityScore * 100); - const qualityClass = quality >= 80 ? 'quality-high' : quality >= 60 ? 'quality-medium' : 'quality-low'; - statsText = `${quality}% quality ยท ${items.length} steps ยท $${cost.toFixed(3)}`; - - // Add subagent breakdown if available - if (metadata.synthesisStats?.sourceBreakdown) { - const sources = metadata.synthesisStats.sourceBreakdown; - const sourceList = Object.entries(sources) - .map(([src, count]) => `${this.escapeHtml(src)}: ${count}`) - .join(', '); - statsText += ` ยท Sources: ${sourceList}`; - } - } - - statsEl.innerHTML = statsText; - } - - // Show warnings/gaps if present from verification - const warningsEl = document.getElementById('planWarnings'); - if (warningsEl) { - if (metadata?.verificationWarnings?.length > 0 || metadata?.verificationGaps?.length > 0) { - let warningsHtml = ''; - if (metadata.verificationGaps?.length > 0) { - warningsHtml += `
Gaps identified: ${metadata.verificationGaps.map(g => this.escapeHtml(g)).join('; ')}
`; - } - if (metadata.verificationWarnings?.length > 0) { - warningsHtml += `
Warnings: ${metadata.verificationWarnings.map(w => this.escapeHtml(w)).join('; ')}
`; - } - warningsEl.innerHTML = warningsHtml; - warningsEl.classList.remove('hidden'); - } else { - warningsEl.classList.add('hidden'); - } - } - - // Render based on view mode - use DocumentFragment for batch DOM updates - if (this.planViewMode === 'grouped' && items.some(i => i.tddPhase)) { - this.renderPlanItemsGrouped(list, items); - } else { - // Flat list view with DocumentFragment for performance - const fragment = document.createDocumentFragment(); - items.forEach((item, index) => { - const row = this.renderPlanItem(item, index); - fragment.appendChild(row); - }); - list.appendChild(fragment); - } - } - - // Render plan items grouped by TDD phase - uses DocumentFragment for batch DOM updates - renderPlanItemsGrouped(container, items) { - const phases = ['setup', 'test', 'impl', 'review', 'verify', null]; - const phaseLabels = { - setup: { label: 'Setup', icon: '๐Ÿ”ง' }, - test: { label: 'Tests', icon: '๐Ÿงช' }, - impl: { label: 'Implementation', icon: '๐Ÿ’ป' }, - review: { label: 'Code Review', icon: '๐Ÿ”' }, - verify: { label: 'Verification', icon: 'โœ…' }, - null: { label: 'Other', icon: '๐Ÿ“‹' }, - }; - - // First, create indexed items to preserve original positions (non-mutating) - const indexedItems = items.map((item, idx) => ({ ...item, _originalIndex: idx })); - - // Build all DOM elements in a DocumentFragment first (no reflows) - const fragment = document.createDocumentFragment(); - - phases.forEach(phase => { - // Filter using indexed items - const phaseItems = indexedItems.filter(item => (item.tddPhase || null) === phase); - - if (phaseItems.length === 0) return; - - const phaseKey = phase || 'null'; - const { label, icon } = phaseLabels[phaseKey]; - - // Create group header - const header = document.createElement('div'); - header.className = 'plan-group-header'; - header.dataset.phase = phaseKey; - header.innerHTML = ` - ${icon} - ${label} - ${phaseItems.length} - ▼ - `; - header.onclick = () => this.togglePlanGroup(phaseKey); - fragment.appendChild(header); - - // Create group items container - const groupItems = document.createElement('div'); - groupItems.className = 'plan-group-items'; - groupItems.dataset.phase = phaseKey; - - phaseItems.forEach(item => { - const row = this.renderPlanItem(item, item._originalIndex); - groupItems.appendChild(row); - }); - - fragment.appendChild(groupItems); - }); - - // Single DOM operation - append all at once - container.appendChild(fragment); - } - - togglePlanGroup(phase) { - const header = document.querySelector(`.plan-group-header[data-phase="${phase}"]`); - const items = document.querySelector(`.plan-group-items[data-phase="${phase}"]`); - - if (header && items) { - header.classList.toggle('collapsed'); - items.classList.toggle('collapsed'); - } - } - - setPlanViewMode(mode) { - this.planViewMode = mode; - - // Update view toggle buttons - document.querySelectorAll('.plan-view-btn').forEach(btn => { - btn.classList.toggle('active', btn.dataset.view === mode); - }); - - this.renderPlanEditor(); - } - - renderPlanItem(item, index) { - const row = document.createElement('div'); - - // Build class with priority indicator - let className = 'plan-item'; - if (item.priority) { - className += ` priority-${item.priority.toLowerCase()}`; - } - row.className = className; - row.dataset.index = index; - row.draggable = true; - - // Add TDD phase badge with enhanced styling - const phaseIcon = this.getTddPhaseIcon(item.tddPhase); - const phaseClass = item.tddPhase ? `phase-${item.tddPhase}` : ''; - const phaseTitle = item.tddPhase ? `TDD Phase: ${item.tddPhase}` : ''; - - // Build verification tooltip - const verificationTitle = item.verificationCriteria - ? `Verification: ${item.verificationCriteria}` - : ''; - - // Dependencies indicator - const depsCount = item.dependencies?.length || 0; - const depsTitle = depsCount > 0 - ? `Depends on: ${item.dependencies.join(', ')}` - : ''; - - // Review checklist indicator for review phase tasks - const checklistCount = item.reviewChecklist?.length || 0; - const checklistTitle = checklistCount > 0 - ? `Review Checklist:\nโ€ข ${item.reviewChecklist.join('\nโ€ข ')}` - : ''; - - row.innerHTML = ` - ☰ - - ${phaseIcon ? `${phaseIcon}` : ''} - - - ${depsCount > 0 ? `↗ ${depsCount}` : ''} - ${item.verificationCriteria ? `✓ verify` : ''} - ${checklistCount > 0 ? `☑ ${checklistCount}` : ''} -
- - ${checklistCount > 0 ? `` : ''} - -
- - `; - - // Add drag-and-drop event handlers - row.addEventListener('dragstart', (e) => this.handlePlanItemDragStart(e, index)); - row.addEventListener('dragend', (e) => this.handlePlanItemDragEnd(e)); - row.addEventListener('dragover', (e) => this.handlePlanItemDragOver(e)); - row.addEventListener('drop', (e) => this.handlePlanItemDrop(e, index)); - - return row; - } - - // Drag and drop handlers for plan items - handlePlanItemDragStart(e, index) { - this._draggedPlanIndex = index; - e.target.classList.add('dragging'); - e.dataTransfer.effectAllowed = 'move'; - e.dataTransfer.setData('text/plain', index); - } - - handlePlanItemDragEnd(e) { - e.target.classList.remove('dragging'); - // Remove drag-over class from all items - document.querySelectorAll('.plan-item.drag-over').forEach(el => { - el.classList.remove('drag-over'); - }); - this._draggedPlanIndex = null; - } - - handlePlanItemDragOver(e) { - e.preventDefault(); - e.dataTransfer.dropEffect = 'move'; - - const item = e.target.closest('.plan-item'); - if (item && !item.classList.contains('dragging')) { - // Remove drag-over from other items - document.querySelectorAll('.plan-item.drag-over').forEach(el => { - if (el !== item) el.classList.remove('drag-over'); - }); - item.classList.add('drag-over'); - } - } - - handlePlanItemDrop(e, targetIndex) { - e.preventDefault(); - const sourceIndex = this._draggedPlanIndex; - - if (sourceIndex !== null && sourceIndex !== targetIndex) { - const plan = this.ralphWizardConfig.generatedPlan; - if (plan) { - // Move the item - const [movedItem] = plan.splice(sourceIndex, 1); - plan.splice(targetIndex, 0, movedItem); - this.renderPlanEditor(); - } - } - } - - // Edit verification criteria inline - editPlanItemVerification(index) { - const plan = this.ralphWizardConfig.generatedPlan; - if (!plan || !plan[index]) return; - - const currentCriteria = plan[index].verificationCriteria || ''; - const newCriteria = prompt('Verification criteria:', currentCriteria); - - if (newCriteria !== null) { - plan[index].verificationCriteria = newCriteria.trim() || undefined; - this.renderPlanEditor(); - } - } - - // Duplicate a plan item - duplicatePlanItem(index) { - const plan = this.ralphWizardConfig.generatedPlan; - if (!plan || !plan[index]) return; - - const original = plan[index]; - const duplicate = { - ...original, - id: `plan-${Date.now()}-dup`, - content: original.content + ' (copy)', - }; - plan.splice(index + 1, 0, duplicate); - this.renderPlanEditor(); - } - - showReviewChecklist(index) { - const plan = this.ralphWizardConfig.generatedPlan; - if (!plan || !plan[index]) return; - - const item = plan[index]; - if (!item.reviewChecklist || item.reviewChecklist.length === 0) return; - - // Create modal content - const checklistHtml = item.reviewChecklist.map((check, i) => ` -
  • - - -
  • - `).join(''); - - const content = ` -
    -

    Review Checklist

    -

    ${this.escapeHtml(item.content)}

    - - -
    - `; - - // Show in a modal - const modal = document.createElement('div'); - modal.className = 'modal checklist-modal-container'; - modal.innerHTML = ``; - modal.onclick = (e) => { if (e.target === modal) modal.remove(); }; - document.body.appendChild(modal); - } - - getTddPhaseIcon(phase) { - switch (phase) { - case 'setup': return '๐Ÿ”ง'; - case 'test': return '๐Ÿงช'; - case 'impl': return '๐Ÿ’ป'; - case 'verify': return 'โœ…'; - case 'review': return '๐Ÿ”'; - default: return ''; - } - } - - escapeHtml(text) { - const div = document.createElement('div'); - div.textContent = text; - return div.innerHTML; - } - - togglePlanItem(index) { - const plan = this.ralphWizardConfig.generatedPlan; - if (plan && plan[index]) { - plan[index].enabled = !plan[index].enabled; - this.renderPlanEditor(); - } - } - - updatePlanItemContent(index, value) { - const plan = this.ralphWizardConfig.generatedPlan; - if (plan && plan[index]) { - plan[index].content = value.trim(); - } - } - - updatePlanItemPriority(index, value) { - const plan = this.ralphWizardConfig.generatedPlan; - if (plan && plan[index]) { - plan[index].priority = value || null; - } - } - - removePlanItem(index) { - const plan = this.ralphWizardConfig.generatedPlan; - if (plan) { - plan.splice(index, 1); - this.renderPlanEditor(); - } - } - - addPlanItem() { - const plan = this.ralphWizardConfig.generatedPlan || []; - plan.push({ - content: '', - priority: null, - enabled: true, - id: `plan-${Date.now()}-new`, - }); - this.ralphWizardConfig.generatedPlan = plan; - this.renderPlanEditor(); - - // Focus the new item's input - setTimeout(() => { - const list = document.getElementById('planItemsList'); - const lastInput = list?.querySelector('.plan-item:last-child .plan-item-content'); - lastInput?.focus(); - }, 50); - } - - async cancelPlanGeneration() { - this.stopPlanGeneration(); - this.planGenerationStopped = true; // Ignore future SSE events - - // Call the cancel API to stop server-side processing - if (this.activePlanOrchestratorId) { - try { - console.log('[Cancel] Sending cancel request for', this.activePlanOrchestratorId); - await fetch('/api/cancel-plan-generation', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ orchestratorId: this.activePlanOrchestratorId }), - }); - this.activePlanOrchestratorId = null; - } catch (err) { - console.error('[Cancel] Failed to cancel plan generation:', err); - } - } - - this.showToast('Plan generation stopped', 'info'); - - // Allow user to proceed without a plan by clicking Next - this.ralphWizardConfig.skipPlanGeneration = true; - - // Show stopped state with clear visual indicator - const errorEl = document.getElementById('planGenerationError'); - const msgEl = document.getElementById('planErrorMsg'); - const stoppedIndicator = document.getElementById('planStoppedIndicator'); - - // Show the stopped indicator, hide error message since this was intentional - if (stoppedIndicator) stoppedIndicator.style.display = 'flex'; - if (msgEl) msgEl.textContent = ''; - - // Hide spinner immediately and show stopped state - document.getElementById('planGenerationLoading')?.classList.add('hidden'); - errorEl?.classList.remove('hidden'); - - // Close all plan subagent windows - this.closePlanSubagentWindows(); - } - - // ========== Plan Subagent Windows (Visible Opus Agents) ========== - - handlePlanSubagentEvent(event) { - // Ignore events if user has stopped plan generation - if (this.planGenerationStopped) return; - - const { type, agentId, agentType, model, status, detail, itemCount, durationMs, error } = event; - - if (type === 'started') { - // Create new plan subagent window - this.createPlanSubagentWindow(agentId, agentType, model, detail); - } else if (type === 'completed' || type === 'failed') { - // Update window to show completion/failure - this.updatePlanSubagentWindow(agentId, status, itemCount, durationMs, error); - } else if (type === 'progress') { - // Update progress detail - const windowData = this.planSubagents.get(agentId); - if (windowData?.element) { - const detailEl = windowData.element.querySelector('.plan-subagent-detail'); - if (detailEl) detailEl.textContent = detail || ''; - } - } - - // Update monitor panel to show plan agents - this.renderMonitorPlanAgents(); - } - - /** - * Find a free position for a window that doesn't overlap with existing windows. - * Tries positions around the wizard, avoiding all occupied spaces. - */ - findFreeWindowPosition(windowWidth, windowHeight, wizardRect, preferredSide = 'right') { - const gap = 30; - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; - const minY = 60; - const maxY = viewportHeight - windowHeight - 10; - - // Collect all occupied rectangles (plan subagent windows + regular subagent windows) - const occupiedRects = []; - - // Add wizard rect as occupied - if (wizardRect) { - occupiedRects.push({ - left: wizardRect.left - gap, - top: wizardRect.top, - right: wizardRect.right + gap, - bottom: wizardRect.bottom, - }); - } - - // Add existing plan subagent windows - for (const [, windowData] of this.planSubagents) { - if (windowData.element) { - const rect = windowData.element.getBoundingClientRect(); - occupiedRects.push({ - left: rect.left, - top: rect.top, - right: rect.right, - bottom: rect.bottom, - }); - } - } - - // Add regular subagent windows - for (const [, windowInfo] of this.subagentWindows) { - if (windowInfo.element && !windowInfo.hidden && !windowInfo.minimized) { - const rect = windowInfo.element.getBoundingClientRect(); - occupiedRects.push({ - left: rect.left, - top: rect.top, - right: rect.right, - bottom: rect.bottom, - }); - } - } - - // Check if a position overlaps with any occupied rectangle - const overlaps = (x, y) => { - const newRect = { - left: x, - top: y, - right: x + windowWidth, - bottom: y + windowHeight, - }; - return occupiedRects.some(rect => - newRect.left < rect.right && - newRect.right > rect.left && - newRect.top < rect.bottom && - newRect.bottom > rect.top - ); - }; - - // Try positions on preferred side first, then other side - const sides = preferredSide === 'right' ? ['right', 'left'] : ['left', 'right']; - - for (const side of sides) { - // Calculate base X position for this side - let baseX; - if (wizardRect) { - if (side === 'right') { - baseX = wizardRect.right + gap; - } else { - baseX = wizardRect.left - windowWidth - gap; - } - } else { - baseX = side === 'right' ? viewportWidth - windowWidth - 50 : 50; - } - - // Clamp to viewport - baseX = Math.max(10, Math.min(baseX, viewportWidth - windowWidth - 10)); - - // Try vertical positions from top to bottom - for (let y = minY; y <= maxY; y += windowHeight + 15) { - if (!overlaps(baseX, y)) { - return { x: baseX, y }; - } - } - - // If side is full, try additional columns - const columnOffset = side === 'right' ? windowWidth + gap : -(windowWidth + gap); - for (let col = 1; col <= 2; col++) { - const x = baseX + col * columnOffset; - if (x < 10 || x > viewportWidth - windowWidth - 10) continue; - - for (let y = minY; y <= maxY; y += windowHeight + 15) { - if (!overlaps(x, y)) { - return { x, y }; - } - } - } - } - - // Fallback: cascade position - const fallbackOffset = this.planSubagents.size * 30; - return { - x: Math.min(50 + fallbackOffset, viewportWidth - windowWidth - 10), - y: Math.min(120 + fallbackOffset, maxY), - }; - } - - createPlanSubagentWindow(agentId, agentType, model, detail) { - if (this.planSubagents.has(agentId)) return; - - const wizardModal = document.getElementById('ralphWizardModal'); - const wizardContent = wizardModal?.querySelector('.modal-content'); - const wizardRect = wizardContent?.getBoundingClientRect(); - - // Calculate position using smart positioning that avoids overlaps - const windowWidth = 280; - const windowHeight = 100; - const windowCount = this.planSubagents.size; - - // Alternate preferred side: even=right, odd=left - const preferredSide = windowCount % 2 === 0 ? 'right' : 'left'; - const { x, y } = this.findFreeWindowPosition(windowWidth, windowHeight, wizardRect, preferredSide); - - // Create window element - const win = document.createElement('div'); - win.className = 'plan-subagent-window'; - win.id = `plan-subagent-${agentId}`; - win.style.left = `${x}px`; - win.style.top = `${y}px`; - win.style.zIndex = ++this.planSubagentWindowZIndex; - - const typeLabels = { - research: 'Research Agent', - requirements: 'Requirements Analyst', - architecture: 'Architecture Planner', - testing: 'TDD Specialist', - risks: 'Risk Analyst', - verification: 'Verification Expert', - execution: 'Execution Optimizer', - 'final-review': 'Final Review', - }; - - const typeIcons = { - research: '๐Ÿ”ฌ', - requirements: '๐Ÿ“‹', - architecture: '๐Ÿ—๏ธ', - testing: '๐Ÿงช', - risks: 'โš ๏ธ', - verification: 'โœ“', - execution: 'โšก', - 'final-review': '๐Ÿ”', - }; - - win.innerHTML = ` -
    - - ${typeIcons[agentType] || '๐Ÿค–'} - ${typeLabels[agentType] || this.escapeHtml(agentType)} - - ${model} -
    -
    -
    - - Running... -
    -
    ${detail || ''}
    -
    - `; - - // Add click handler for prompt link - const promptLink = win.querySelector('.plan-subagent-prompt-link'); - promptLink.addEventListener('click', (e) => { - e.stopPropagation(); - this.showPlanAgentPrompt(agentId, agentType); - }); - - document.body.appendChild(win); - - // Calculate relative position to wizard - let relativePos = null; - if (wizardRect) { - relativePos = { - dx: x - wizardRect.left, - dy: y - wizardRect.top, - }; - } - - // Make the window draggable - const header = win.querySelector('.plan-subagent-header'); - const dragListeners = this.makePlanSubagentDraggable(win, header); - - // Store reference with relative position and drag listeners - this.planSubagents.set(agentId, { - agentId, - type: agentType, - model, - status: 'running', - startTime: Date.now(), - element: win, - relativePos, - dragListeners, - }); - - // If agents are minimized, hide this new window too - if (this.planAgentsMinimized) { - win.style.display = 'none'; - } - - // Update wizard agent count button - this.updateWizardAgentCount(); - - // Update connection lines - this.updateConnectionLines(); - } - - makePlanSubagentDraggable(win, handle) { - let isDragging = false; - let startX, startY, startLeft, startTop; - let dragUpdateScheduled = false; - - const mousedownHandler = (e) => { - isDragging = true; - startX = e.clientX; - startY = e.clientY; - startLeft = parseInt(win.style.left) || 0; - startTop = parseInt(win.style.top) || 0; - win.style.zIndex = ++this.planSubagentWindowZIndex; - e.preventDefault(); - }; - - const moveHandler = (e) => { - if (!isDragging) return; - const dx = e.clientX - startX; - const dy = e.clientY - startY; - - // Constrain to viewport - const winWidth = win.offsetWidth || 280; - const winHeight = win.offsetHeight || 100; - const maxX = window.innerWidth - winWidth - 10; - const maxY = window.innerHeight - winHeight - 10; - const newLeft = Math.max(10, Math.min(startLeft + dx, maxX)); - const newTop = Math.max(10, Math.min(startTop + dy, maxY)); - - win.style.left = `${newLeft}px`; - win.style.top = `${newTop}px`; - - // Throttle connection line updates - if (!dragUpdateScheduled) { - dragUpdateScheduled = true; - requestAnimationFrame(() => { - this.updateConnectionLines(); - dragUpdateScheduled = false; - }); - } - }; - - const upHandler = () => { - isDragging = false; - }; - - handle.addEventListener('mousedown', mousedownHandler); - document.addEventListener('mousemove', moveHandler); - document.addEventListener('mouseup', upHandler); - - return { mousedown: mousedownHandler, move: moveHandler, up: upHandler }; - } - - updatePlanSubagentWindow(agentId, status, itemCount, durationMs, error) { - const windowData = this.planSubagents.get(agentId); - if (!windowData?.element) return; - - const win = windowData.element; - const statusEl = win.querySelector('.plan-subagent-status'); - const statusTextEl = win.querySelector('.plan-subagent-status-text'); - const spinnerEl = win.querySelector('.plan-subagent-spinner'); - const detailEl = win.querySelector('.plan-subagent-detail'); - - windowData.status = status; - windowData.itemCount = itemCount; - - if (status === 'completed') { - statusEl?.classList.remove('running'); - statusEl?.classList.add('completed'); - if (spinnerEl) spinnerEl.style.display = 'none'; - if (statusTextEl) { - statusTextEl.textContent = `Done (${itemCount || 0} items)`; - statusTextEl.classList.add('clickable'); - statusTextEl.title = 'Click to view result files'; - statusTextEl.onclick = (e) => { - e.stopPropagation(); - this.showPlanAgentFiles(windowData.agentId, windowData.type); - }; - } - if (detailEl && durationMs) detailEl.textContent = `${(durationMs / 1000).toFixed(1)}s`; - } else if (status === 'failed' || status === 'cancelled') { - statusEl?.classList.remove('running'); - statusEl?.classList.add('failed'); - if (spinnerEl) spinnerEl.style.display = 'none'; - if (statusTextEl) statusTextEl.textContent = status === 'cancelled' ? 'Cancelled' : 'Failed'; - if (detailEl) detailEl.textContent = error || ''; - } - - // Update minimized tab if agents are hidden - if (this.planAgentsMinimized) { - this.createAgentsMinimizedTab(); - } - - // Update connection lines - this.updateConnectionLines(); - } - - closePlanSubagentWindows() { - for (const [agentId, windowData] of this.planSubagents) { - // Clean up drag listeners - if (windowData.dragListeners) { - document.removeEventListener('mousemove', windowData.dragListeners.move); - document.removeEventListener('mouseup', windowData.dragListeners.up); - if (windowData.dragListeners.touchMove) { - document.removeEventListener('touchmove', windowData.dragListeners.touchMove); - document.removeEventListener('touchend', windowData.dragListeners.up); - document.removeEventListener('touchcancel', windowData.dragListeners.up); - } - } - if (windowData.element) { - windowData.element.remove(); - } - } - this.planSubagents.clear(); - this.updateWizardAgentCount(); - this.removeAgentsMinimizedTab(); - this.updateConnectionLines(); - // Update monitor panel (will hide section if no agents) - this.renderMonitorPlanAgents(); - } - - // ========== Wizard Dragging ========== - - setupWizardDragging() { - const modal = document.getElementById('ralphWizardModal'); - const wizardContent = modal?.querySelector('.modal-content'); - const header = wizardContent?.querySelector('.modal-header'); - if (!wizardContent || !header) return; - - // Clean up any existing listeners - this.cleanupWizardDragging(); - - let isDragging = false; - let startX, startY, startLeft, startTop; - let dragUpdateScheduled = false; - let agentStartPositions = new Map(); // Track agent window start positions during drag - - const startHandler = (e) => { - // Don't drag if clicking buttons - if (e.target.tagName === 'BUTTON' || e.target.closest('button')) return; - - isDragging = true; - - // If wizard hasn't been moved yet, calculate its current centered position - if (!this.wizardPosition) { - const rect = wizardContent.getBoundingClientRect(); - this.wizardPosition = { left: rect.left, top: rect.top }; - } - - const coords = getEventCoords(e); - startX = coords.clientX; - startY = coords.clientY; - startLeft = this.wizardPosition.left; - startTop = this.wizardPosition.top; - - // Capture agent window start positions - agentStartPositions.clear(); - for (const [agentId, windowData] of this.planSubagents) { - if (windowData.element) { - agentStartPositions.set(agentId, { - left: parseInt(windowData.element.style.left) || 0, - top: parseInt(windowData.element.style.top) || 0, - }); - } - } - - // Switch to absolute positioning - wizardContent.classList.add('draggable'); - wizardContent.style.left = `${startLeft}px`; - wizardContent.style.top = `${startTop}px`; - - // Add move/end listeners only during drag (prevents blocking scroll) - document.addEventListener('mousemove', moveHandler); - document.addEventListener('mouseup', upHandler); - document.addEventListener('touchmove', moveHandler, { passive: false }); - document.addEventListener('touchend', upHandler); - - e.preventDefault(); - }; - - const moveHandler = (e) => { - if (!isDragging) return; - - const coords = getEventCoords(e); - const dx = coords.clientX - startX; - const dy = coords.clientY - startY; - - // Constrain to viewport - const winWidth = wizardContent.offsetWidth || 480; - const winHeight = wizardContent.offsetHeight || 400; - const maxX = window.innerWidth - winWidth - 10; - const maxY = window.innerHeight - winHeight - 10; - const newLeft = Math.max(10, Math.min(startLeft + dx, maxX)); - const newTop = Math.max(10, Math.min(startTop + dy, maxY)); - - this.wizardPosition = { left: newLeft, top: newTop }; - wizardContent.style.left = `${newLeft}px`; - wizardContent.style.top = `${newTop}px`; - - // Move agent windows using their start positions + delta - this.moveAgentWindowsWithWizard(agentStartPositions, dx, dy); - - // Throttle connection line updates - if (!dragUpdateScheduled) { - dragUpdateScheduled = true; - requestAnimationFrame(() => { - this.updateConnectionLines(); - dragUpdateScheduled = false; - }); - } - }; - - const upHandler = () => { - if (isDragging) { - isDragging = false; - // Remove move/end listeners when drag ends - document.removeEventListener('mousemove', moveHandler); - document.removeEventListener('mouseup', upHandler); - document.removeEventListener('touchmove', moveHandler); - document.removeEventListener('touchend', upHandler); - // Save agent window positions for next drag - this.saveAgentWindowRelativePositions(); - } - }; - - // Only attach start handlers - move/end are added dynamically during drag - header.addEventListener('mousedown', startHandler); - header.addEventListener('touchstart', startHandler, { passive: false }); - - this.wizardDragListeners = { - header, - start: startHandler, - move: moveHandler, - up: upHandler, - }; - } - - cleanupWizardDragging() { - if (this.wizardDragListeners) { - const { header, start, move, up } = this.wizardDragListeners; - // Mouse events - header?.removeEventListener('mousedown', start); - document.removeEventListener('mousemove', move); - document.removeEventListener('mouseup', up); - // Touch events - header?.removeEventListener('touchstart', start); - document.removeEventListener('touchmove', move); - document.removeEventListener('touchend', up); - this.wizardDragListeners = null; - } - } - - moveAgentWindowsWithWizard(agentStartPositions, dx, dy) { - // Move plan subagent windows using their start positions + delta - for (const [agentId, windowData] of this.planSubagents) { - if (windowData.element && !this.planAgentsMinimized) { - const startPos = agentStartPositions.get(agentId); - if (startPos) { - windowData.element.style.left = `${startPos.left + dx}px`; - windowData.element.style.top = `${startPos.top + dy}px`; - } - } - } - } - - saveAgentWindowRelativePositions() { - const wizardModal = document.getElementById('ralphWizardModal'); - const wizardContent = wizardModal?.querySelector('.modal-content'); - if (!wizardContent) return; - - const wizardRect = wizardContent.getBoundingClientRect(); - - for (const [, windowData] of this.planSubagents) { - if (windowData.element) { - const winRect = windowData.element.getBoundingClientRect(); - windowData.relativePos = { - dx: winRect.left - wizardRect.left, - dy: winRect.top - wizardRect.top, - }; - } - } - } - - // ========== Agent Windows Minimize/Expand ========== - - togglePlanAgentsMinimized() { - this.planAgentsMinimized = !this.planAgentsMinimized; - - if (this.planAgentsMinimized) { - // Hide all agent windows and show tab - for (const [, windowData] of this.planSubagents) { - if (windowData.element) { - windowData.element.style.display = 'none'; - } - } - this.createAgentsMinimizedTab(); - } else { - // Show all agent windows and remove tab - for (const [, windowData] of this.planSubagents) { - if (windowData.element) { - windowData.element.style.display = ''; - } - } - this.removeAgentsMinimizedTab(); - } - - this.updateConnectionLines(); - } - - createAgentsMinimizedTab() { - // Remove existing tab if any - this.removeAgentsMinimizedTab(); - - const runningCount = Array.from(this.planSubagents.values()) - .filter(w => w.status === 'running').length; - const totalCount = this.planSubagents.size; - - const tab = document.createElement('div'); - tab.className = 'wizard-agents-tab'; - tab.id = 'wizardAgentsMinimizedTab'; - tab.onclick = () => this.togglePlanAgentsMinimized(); - - tab.innerHTML = ` - ๐Ÿค– - Plan Agents - ${totalCount} - ${runningCount > 0 ? ` - - - ${runningCount} running - - ` : ''} - `; - - document.body.appendChild(tab); - } - - removeAgentsMinimizedTab() { - document.getElementById('wizardAgentsMinimizedTab')?.remove(); - } - - updateWizardAgentCount() { - const btn = document.getElementById('wizardMinimizeAgentsBtn'); - const countEl = document.getElementById('wizardAgentCount'); - if (!btn || !countEl) return; - - const count = this.planSubagents.size; - countEl.textContent = count; - btn.style.display = count > 0 ? '' : 'none'; - - // Update minimized tab if it exists - if (this.planAgentsMinimized) { - this.createAgentsMinimizedTab(); - } - } - - // ========== Plan Agent Prompts & Files Viewer ========== - - async showPlanAgentPrompt(agentId, agentType) { - const caseName = this.ralphWizardConfig?.caseName; - if (!caseName) { - console.error('No case name available'); - return; - } - - // Get agent info from planSubagents Map - const agentInfo = this.planSubagents.get(agentId); - const isRunning = agentInfo?.status === 'running'; - const taskDescription = this.ralphWizardConfig?.taskDescription || 'No task description available'; - - try { - const filePath = `${agentType}/prompt.md`; - const res = await fetch(`/api/cases/${encodeURIComponent(caseName)}/ralph-wizard/file/${encodeURIComponent(filePath)}`); - const data = await res.json(); - - if (!data.success) { - // File not found - show contextual message based on agent status - if (isRunning) { - const content = this.buildAgentRunningContent(agentType, agentInfo, taskDescription); - this.showPlanFileWindow(agentType, 'prompt.md', content, false); - } else { - // Agent completed but no prompt file - might have failed - const content = this.buildAgentNoPromptContent(agentType, taskDescription); - this.showPlanFileWindow(agentType, 'prompt.md', content, false); - } - return; - } - - this.showPlanFileWindow(agentType, 'prompt.md', data.data.content, false); - } catch (err) { - console.error('Failed to fetch prompt:', err); - if (isRunning) { - const content = this.buildAgentRunningContent(agentType, agentInfo, taskDescription); - this.showPlanFileWindow(agentType, 'prompt.md', content, false); - } else { - this.showPlanFileWindow(agentType, 'prompt.md', `Error fetching prompt: ${err.message}`, false); - } - } - } - - buildAgentRunningContent(agentType, agentInfo, taskDescription) { - const typeDescriptions = { - research: { - title: 'Research Agent', - role: 'Gathering external resources, exploring the codebase, and identifying patterns', - activities: [ - 'Reading CLAUDE.md and project documentation', - 'Exploring the codebase structure with Glob and Grep', - 'Searching the web for relevant documentation and examples', - 'Identifying existing patterns and conventions', - 'Finding similar implementations to reference', - ], - }, - requirements: { - title: 'Requirements Analyst', - role: 'Breaking down the task into concrete, actionable requirements', - activities: [ - 'Analyzing the task description for functional requirements', - 'Identifying non-functional requirements (performance, security)', - 'Detecting edge cases and error scenarios', - 'Defining acceptance criteria for each requirement', - ], - }, - architecture: { - title: 'Architecture Planner', - role: 'Designing the technical approach and file structure', - activities: [ - 'Identifying files to create or modify', - 'Planning the module/component structure', - 'Defining interfaces and data flow', - 'Ensuring consistency with existing architecture', - ], - }, - testing: { - title: 'TDD Specialist', - role: 'Planning the test-first development strategy', - activities: [ - 'Identifying test cases for each requirement', - 'Planning unit, integration, and E2E tests', - 'Defining test fixtures and mocks needed', - 'Ensuring edge cases have test coverage', - ], - }, - risks: { - title: 'Risk Analyst', - role: 'Identifying potential issues and mitigation strategies', - activities: [ - 'Analyzing potential failure points', - 'Identifying security concerns', - 'Flagging performance bottlenecks', - 'Suggesting rollback strategies', - ], - }, - verification: { - title: 'Verification Expert', - role: 'Validating plan completeness and quality', - activities: [ - 'Checking all requirements have implementation steps', - 'Verifying test coverage is adequate', - 'Ensuring dependencies are correctly ordered', - 'Scoring plan quality and completeness', - ], - }, - 'execution-optimizer': { - title: 'Execution Optimizer', - role: 'Optimizing the plan for Claude Code execution', - activities: [ - 'Identifying tasks that can run in parallel', - 'Grouping related tasks for efficiency', - 'Marking fresh context requirements', - 'Estimating token usage per task', - ], - }, - 'final-review': { - title: 'Final Reviewer', - role: 'Performing holistic validation of the complete plan', - activities: [ - 'Checking for gaps or missing steps', - 'Validating task ordering and dependencies', - 'Ensuring the plan is implementable', - 'Final quality assessment', - ], - }, - }; - - const info = typeDescriptions[agentType] || { - title: agentType, - role: 'Processing the task', - activities: ['Working on the task...'], - }; - - const elapsed = agentInfo?.startTime - ? Math.floor((Date.now() - agentInfo.startTime) / 1000) - : 0; - const model = agentInfo?.model || 'unknown'; - - let content = `# ${info.title} - Still Running\n\n`; - content += `โณ **Status:** Generating... (${elapsed}s elapsed)\n`; - content += `๐Ÿค– **Model:** ${model}\n\n`; - content += `---\n\n`; - content += `## Role\n${info.role}\n\n`; - content += `## Currently Working On\n`; - info.activities.forEach(activity => { - content += `- ${activity}\n`; - }); - content += `\n---\n\n`; - content += `## Task Being Analyzed\n\n`; - content += `\`\`\`\n${taskDescription}\n\`\`\`\n\n`; - content += `---\n\n`; - content += `*The full prompt will be available once the agent completes.*\n`; - content += `*Click again after completion to see the actual prompt that was used.*`; - - return content; - } - - buildAgentNoPromptContent(agentType, taskDescription) { - const typeLabels = { - research: 'Research Agent', - requirements: 'Requirements Analyst', - architecture: 'Architecture Planner', - testing: 'TDD Specialist', - risks: 'Risk Analyst', - verification: 'Verification Expert', - 'execution-optimizer': 'Execution Optimizer', - 'final-review': 'Final Reviewer', - }; - - let content = `# ${typeLabels[agentType] || agentType} - No Prompt Available\n\n`; - content += `The prompt file for this agent was not saved.\n\n`; - content += `This can happen if:\n`; - content += `- The agent failed before completing\n`; - content += `- The plan generation was cancelled\n`; - content += `- There was an error saving the output\n\n`; - content += `---\n\n`; - content += `## Task Description\n\n`; - content += `\`\`\`\n${taskDescription}\n\`\`\``; - - return content; - } - - async showPlanAgentFiles(agentId, agentType) { - const caseName = this.ralphWizardConfig?.caseName; - if (!caseName) { - console.error('No case name available'); - return; - } - - // Create file manager popup - this.showPlanFileManager(agentType, caseName); - } - - showPlanFileManager(agentType, caseName) { - // Remove existing file manager if any - document.getElementById('planFileManager')?.remove(); - - const typeLabels = { - research: 'Research Agent', - requirements: 'Requirements Analyst', - architecture: 'Architecture Planner', - testing: 'TDD Specialist', - risks: 'Risk Analyst', - verification: 'Verification Expert', - execution: 'Execution Optimizer', - 'final-review': 'Final Review', - }; - - const fm = document.createElement('div'); - fm.className = 'plan-file-manager'; - fm.id = 'planFileManager'; - fm.innerHTML = ` -
    - ${typeLabels[agentType] || agentType} Files - -
    -
    -
    Loading files...
    -
    - `; - - document.body.appendChild(fm); - - // Make draggable - this.makePlanFileManagerDraggable(fm); - - // Fetch files - this.loadPlanFileManagerFiles(agentType, caseName, fm); - } - - async loadPlanFileManagerFiles(agentType, caseName, fm) { - const body = fm.querySelector('.plan-fm-body'); - - try { - // First try to get the result.json - const resultPath = `${agentType}/result.json`; - const res = await fetch(`/api/cases/${encodeURIComponent(caseName)}/ralph-wizard/file/${encodeURIComponent(resultPath)}`); - const data = await res.json(); - - if (!data.success) { - body.innerHTML = `
    No result file found
    `; - return; - } - - const parsed = data.data.parsed; - let items = []; - - // Extract items based on agent type - if (agentType === 'research' && parsed?.findings) { - // Research has nested structure - items = [ - { name: 'External Resources', type: 'section', count: parsed.findings.externalResources?.length || 0 }, - { name: 'Codebase Patterns', type: 'section', count: parsed.findings.codebasePatterns?.length || 0 }, - { name: 'Technical Recommendations', type: 'section', count: parsed.findings.technicalRecommendations?.length || 0 }, - { name: 'Potential Challenges', type: 'section', count: parsed.findings.potentialChallenges?.length || 0 }, - { name: 'Recommended Tools', type: 'section', count: parsed.findings.recommendedTools?.length || 0 }, - ]; - } else if (parsed?.items) { - items = parsed.items.map((item, idx) => ({ - name: item.category || item.content?.substring(0, 50) || `Item ${idx + 1}`, - type: 'item', - content: item.content || JSON.stringify(item, null, 2), - rationale: item.rationale, - })); - } else { - // Fallback: show raw JSON structure - items = Object.keys(parsed || {}).map(key => ({ - name: key, - type: 'key', - content: JSON.stringify(parsed[key], null, 2), - })); - } - - body.innerHTML = ` -
    -
    - ๐Ÿ“„ - prompt.md -
    -
    - ๐Ÿ“Š - result.json -
    -
    -
    Result Contents (${items.length} items)
    - ${items.map((item, idx) => ` -
    - ${item.type === 'section' ? '๐Ÿ“' : '๐Ÿ“'} - ${this.escapeHtml(item.name)} - ${item.count !== undefined ? `${item.count}` : ''} -
    - `).join('')} -
    - `; - - // Store items data for click handlers - fm._itemsData = items; - fm._parsedData = parsed; - fm._caseName = caseName; - fm._agentType = agentType; - - // Add click handlers - body.querySelectorAll('.plan-fm-file').forEach(el => { - el.onclick = () => this.openPlanFile(agentType, caseName, el.dataset.file); - }); - - body.querySelectorAll('.plan-fm-item').forEach(el => { - el.onclick = () => this.openPlanFileItem(fm, parseInt(el.dataset.itemIdx)); - }); - - } catch (err) { - body.innerHTML = `
    Error: ${this.escapeHtml(err.message)}
    `; - } - } - - async openPlanFile(agentType, caseName, fileName) { - const filePath = `${agentType}/${fileName}`; - try { - const res = await fetch(`/api/cases/${encodeURIComponent(caseName)}/ralph-wizard/file/${encodeURIComponent(filePath)}`); - const data = await res.json(); - - if (!data.success) { - this.showPlanFileWindow(agentType, fileName, `Error: ${data.error}`, fileName.endsWith('.json')); - return; - } - - const content = fileName.endsWith('.json') - ? JSON.stringify(data.data.parsed, null, 2) - : data.data.content; - - this.showPlanFileWindow(agentType, fileName, content, fileName.endsWith('.json')); - } catch (err) { - this.showPlanFileWindow(agentType, fileName, `Error: ${err.message}`, false); - } - } - - openPlanFileItem(fm, itemIdx) { - const items = fm._itemsData; - const parsed = fm._parsedData; - const agentType = fm._agentType; - - if (!items || itemIdx >= items.length) return; - - const item = items[itemIdx]; - - // For research agent, expand the section - if (agentType === 'research' && item.type === 'section') { - let sectionData; - switch (item.name) { - case 'External Resources': - sectionData = parsed.findings?.externalResources; - break; - case 'Codebase Patterns': - sectionData = parsed.findings?.codebasePatterns; - break; - case 'Technical Recommendations': - sectionData = parsed.findings?.technicalRecommendations; - break; - case 'Potential Challenges': - sectionData = parsed.findings?.potentialChallenges; - break; - case 'Recommended Tools': - sectionData = parsed.findings?.recommendedTools; - break; - } - const content = JSON.stringify(sectionData, null, 2); - this.showPlanFileWindow(agentType, item.name, content, true); - } else { - // Show item content - const content = item.content || JSON.stringify(item, null, 2); - this.showPlanFileWindow(agentType, item.name, content, typeof item.content !== 'string'); - } - } - - showPlanFileWindow(agentType, fileName, content, isJson) { - const windowId = `plan-file-${Date.now()}`; - - const win = document.createElement('div'); - win.className = 'plan-file-window'; - win.id = windowId; - win.style.left = `${100 + Math.random() * 200}px`; - win.style.top = `${100 + Math.random() * 100}px`; - win.style.zIndex = ++this.planSubagentWindowZIndex; - - const typeLabels = { - research: 'Research', - requirements: 'Requirements', - architecture: 'Architecture', - testing: 'Testing', - risks: 'Risks', - verification: 'Verification', - execution: 'Execution', - 'final-review': 'Review', - }; - - win.innerHTML = ` -
    - ${typeLabels[agentType] || agentType} / ${this.escapeHtml(fileName)} - -
    -
    -
    ${this.escapeHtml(content)}
    -
    -
    - `; - - document.body.appendChild(win); - - // Make draggable and resizable - this.makePlanFileWindowDraggable(win); - this.makePlanFileWindowResizable(win); - } - - makePlanFileManagerDraggable(fm) { - const header = fm.querySelector('.plan-fm-header'); - let isDragging = false; - let startX, startY, startLeft, startTop; - - header.addEventListener('mousedown', (e) => { - if (e.target.tagName === 'BUTTON') return; - isDragging = true; - startX = e.clientX; - startY = e.clientY; - startLeft = parseInt(fm.style.left) || (window.innerWidth / 2 - 150); - startTop = parseInt(fm.style.top) || (window.innerHeight / 2 - 200); - fm.style.left = `${startLeft}px`; - fm.style.top = `${startTop}px`; - e.preventDefault(); - }); - - const moveHandler = (e) => { - if (!isDragging) return; - const dx = e.clientX - startX; - const dy = e.clientY - startY; - fm.style.left = `${Math.max(10, startLeft + dx)}px`; - fm.style.top = `${Math.max(10, startTop + dy)}px`; - }; - - const upHandler = () => { - isDragging = false; - }; - - document.addEventListener('mousemove', moveHandler); - document.addEventListener('mouseup', upHandler); - - // Store handlers for cleanup - fm._dragHandlers = { move: moveHandler, up: upHandler }; - } - - // Clean up plan file manager and remove document listeners - closePlanFileManager() { - const fm = document.getElementById('planFileManager'); - if (fm) { - if (fm._dragHandlers) { - document.removeEventListener('mousemove', fm._dragHandlers.move); - document.removeEventListener('mouseup', fm._dragHandlers.up); - } - fm.remove(); - } - } - - makePlanFileWindowDraggable(win) { - const header = win.querySelector('.plan-file-window-header'); - let isDragging = false; - let startX, startY, startLeft, startTop; - - header.addEventListener('mousedown', (e) => { - if (e.target.tagName === 'BUTTON') return; - isDragging = true; - startX = e.clientX; - startY = e.clientY; - startLeft = parseInt(win.style.left) || 0; - startTop = parseInt(win.style.top) || 0; - win.style.zIndex = ++this.planSubagentWindowZIndex; - e.preventDefault(); - }); - - const moveHandler = (e) => { - if (!isDragging) return; - const dx = e.clientX - startX; - const dy = e.clientY - startY; - win.style.left = `${Math.max(10, startLeft + dx)}px`; - win.style.top = `${Math.max(10, startTop + dy)}px`; - }; - - const upHandler = () => { - isDragging = false; - }; - - document.addEventListener('mousemove', moveHandler); - document.addEventListener('mouseup', upHandler); - - // Store handlers for cleanup - win._dragHandlers = { move: moveHandler, up: upHandler }; - } - - makePlanFileWindowResizable(win) { - const resizer = win.querySelector('.plan-file-window-resize'); - let isResizing = false; - let startX, startY, startWidth, startHeight; - - resizer.addEventListener('mousedown', (e) => { - isResizing = true; - startX = e.clientX; - startY = e.clientY; - startWidth = win.offsetWidth; - startHeight = win.offsetHeight; - e.preventDefault(); - e.stopPropagation(); - }); - - const moveHandler = (e) => { - if (!isResizing) return; - const dx = e.clientX - startX; - const dy = e.clientY - startY; - win.style.width = `${Math.max(300, startWidth + dx)}px`; - win.style.height = `${Math.max(200, startHeight + dy)}px`; - }; - - const upHandler = () => { - isResizing = false; - }; - - document.addEventListener('mousemove', moveHandler); - document.addEventListener('mouseup', upHandler); - - // Store handlers for cleanup - win._resizeHandlers = { move: moveHandler, up: upHandler }; - } - - // Clean up plan file window and remove document listeners - closePlanFileWindow(windowId) { - const win = document.getElementById(windowId); - if (win) { - if (win._dragHandlers) { - document.removeEventListener('mousemove', win._dragHandlers.move); - document.removeEventListener('mouseup', win._dragHandlers.up); - } - if (win._resizeHandlers) { - document.removeEventListener('mousemove', win._resizeHandlers.move); - document.removeEventListener('mouseup', win._resizeHandlers.up); - } - win.remove(); - } - } - - regeneratePlan() { - this.ralphWizardConfig.generatedPlan = null; - this.ralphWizardConfig.planGenerated = false; - this.generatePlan(); - } - - generateFixPlanContent(items) { - // Group items by priority - const p0Items = items.filter(i => i.priority === 'P0'); - const p1Items = items.filter(i => i.priority === 'P1'); - const p2Items = items.filter(i => i.priority === 'P2'); - const noPriorityItems = items.filter(i => !i.priority); - - let content = '# Implementation Plan\n\n'; - content += `Generated: ${new Date().toISOString().slice(0, 10)}\n\n`; - - if (p0Items.length > 0) { - content += '## Critical Path (P0)\n\n'; - p0Items.forEach(item => { - content += `- [ ] ${item.content}\n`; - }); - content += '\n'; - } - - if (p1Items.length > 0) { - content += '## Standard (P1)\n\n'; - p1Items.forEach(item => { - content += `- [ ] ${item.content}\n`; - }); - content += '\n'; - } - - if (p2Items.length > 0) { - content += '## Nice-to-Have (P2)\n\n'; - p2Items.forEach(item => { - content += `- [ ] ${item.content}\n`; - }); - content += '\n'; - } - - if (noPriorityItems.length > 0) { - content += '## Tasks\n\n'; - noPriorityItems.forEach(item => { - content += `- [ ] ${item.content}\n`; - }); - content += '\n'; - } - - return content; - } - - async startRalphLoop() { - const config = this.ralphWizardConfig; - - // Read advanced options (respawn disabled by default for Ralph Loops) - config.enableRespawn = document.getElementById('ralphEnableRespawn')?.checked ?? false; - // Check either auto-start checkbox (step 2 or step 3) - config.autoStart = document.getElementById('ralphAutoStart')?.checked || - document.getElementById('ralphAutoStartStep2')?.checked || - this.ralphWizardConfig.autoStart || false; - - // Close wizard - this.closeRalphWizard(); - - // Determine if we have a task plan - const hasPlan = config.generatedPlan && config.generatedPlan.filter(i => i.enabled).length > 0; - - // Build the full prompt following Ralph Wiggum methodology - let fullPrompt = config.taskDescription; - fullPrompt += '\n\n---\n\n'; - - // Add @fix_plan.md reference if plan was generated - if (hasPlan) { - fullPrompt += '## Task Plan\n\n'; - fullPrompt += 'A task plan has been written to `@fix_plan.md`. Use this to track progress:\n'; - fullPrompt += '- Reference the plan at the start of each iteration\n'; - fullPrompt += '- Update task checkboxes as you complete items\n'; - fullPrompt += '- Work through items in priority order (P0 > P1 > P2)\n\n'; - } - - fullPrompt += '## Iteration Protocol\n\n'; - fullPrompt += 'This is an autonomous loop. Files from previous iterations persist. On each iteration:\n'; - fullPrompt += '1. Check what work has already been done\n'; - fullPrompt += '2. Make incremental progress toward completion\n'; - fullPrompt += '3. Commit meaningful changes with descriptive messages\n\n'; - - fullPrompt += '## Verification\n\n'; - fullPrompt += 'After each significant change:\n'; - fullPrompt += '- Run tests to verify (npm test, pytest, etc.)\n'; - fullPrompt += '- Check for type/lint errors if applicable\n'; - fullPrompt += '- If tests fail, read the error, fix it, and retry\n\n'; - - fullPrompt += '## Completion Criteria\n\n'; - fullPrompt += `Output \`${config.completionPhrase}\` when ALL of the following are true:\n`; - fullPrompt += '- All requirements from the task description are implemented\n'; - fullPrompt += '- All tests pass\n'; - fullPrompt += '- Changes are committed\n\n'; - - fullPrompt += '## If Stuck\n\n'; - fullPrompt += 'If you encounter the same error for 3+ iterations:\n'; - fullPrompt += '1. Document what you\'ve tried\n'; - fullPrompt += '2. Identify the specific blocker\n'; - fullPrompt += '3. Try an alternative approach\n'; - fullPrompt += '4. If truly blocked, output `BLOCKED` with an explanation\n'; - - try { - // Step 1: Create/verify case exists and start session - this.terminal.clear(); - this.terminal.writeln(`\x1b[1;33m Starting Ralph Loop in ${config.caseName}...\x1b[0m`); - this.terminal.writeln(''); - - // Create session via quick-start - const res = await fetch('/api/quick-start', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ caseName: config.caseName, mode: 'claude' }) - }); - - const data = await res.json(); - if (!data.success) { - this.showToast(data.error || 'Failed to start session', 'error'); - return; - } - - const sessionId = data.sessionId; - - // Clear from closed set in case user previously closed Ralph for this session - this.ralphClosedSessions.delete(sessionId); - - // Step 2: Configure Ralph tracker for this session - await fetch(`/api/sessions/${sessionId}/ralph-config`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - enabled: true, - completionPhrase: config.completionPhrase, - maxIterations: config.maxIterations || null, - }) - }); - - // Step 2.5: Write @fix_plan.md if plan was generated - if (config.generatedPlan && config.generatedPlan.length > 0) { - const enabledItems = config.generatedPlan.filter(i => i.enabled); - if (enabledItems.length > 0) { - const planContent = this.generateFixPlanContent(enabledItems); - // Import the plan content and get the todos back - const importRes = await fetch(`/api/sessions/${sessionId}/fix-plan/import`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ content: planContent }) - }); - const importData = await importRes.json(); - - // Update local ralph state immediately to avoid 100% display bug - // (SSE event might arrive after selectSession is called) - if (importData.success && importData.data?.todos) { - this.updateRalphState(sessionId, { todos: importData.data.todos }); - } - - // Write to disk - await fetch(`/api/sessions/${sessionId}/fix-plan/write`, { method: 'POST' }); - } - } - - // Step 3: Enable respawn if requested (with Ralph-specific prompts) - // NOTE: Prompts must be single-line because tmux send-keys handles them as single input - if (config.enableRespawn) { - const ralphUpdatePrompt = 'Before /clear: Update CLAUDE.md with discoveries and notes, mark completed tasks in @fix_plan.md, write a brief progress summary to a file so the next iteration can continue seamlessly.'; - - const ralphKickstartPrompt = `You are in a Ralph Wiggum loop. Read @fix_plan.md for task status, continue on the next uncompleted task, output ${config.completionPhrase} when ALL tasks are complete.`; - - await fetch(`/api/sessions/${sessionId}/respawn/enable`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - updatePrompt: ralphUpdatePrompt, - sendClear: true, - sendInit: true, - kickstartPrompt: ralphKickstartPrompt, - }) - }); - } - - // Step 4: Switch to session and wait for it to be ready - await this.selectSession(sessionId); - - // Wait for Claude CLI to be ready (shows prompt) - // Check session status multiple times - Claude CLI can take a while to initialize - let attempts = 0; - const maxAttempts = 30; // 15 seconds total - let sessionReady = false; - console.log('[RalphWizard] Waiting for session to become ready...'); - while (attempts < maxAttempts) { - await new Promise(r => setTimeout(r, 500)); - try { - // Use /terminal?tail=2048 instead of /sessions/:id to avoid fetching 2-3MB full state - const statusRes = await fetch(`/api/sessions/${sessionId}/terminal?tail=2048`); - const statusData = await statusRes.json(); - // Session is ready ONLY when Claude CLI shows its UI: - // Must see prompt character 'โฏ' OR 'tokens' status line - // Do NOT use isWorking=false - that fires before CLI is ready! - const termBuf = statusData?.terminalBuffer || ''; - const hasPrompt = termBuf.includes('โฏ'); - const hasTokensLine = termBuf.includes('tokens'); - if (hasPrompt || hasTokensLine) { - console.log(`[RalphWizard] Session ready after ${attempts + 1} attempts (hasPrompt=${hasPrompt}, hasTokensLine=${hasTokensLine})`); - sessionReady = true; - break; - } - } catch (e) { - console.warn('[RalphWizard] Error checking session status:', e); - } - attempts++; - } - - if (!sessionReady) { - console.warn(`[RalphWizard] Session did not become ready after ${maxAttempts} attempts, sending prompt anyway`); - this.showToast('Session took longer than expected to initialize, sending prompt...', 'warning'); - // Add extra delay to give Claude CLI more time - await new Promise(r => setTimeout(r, 2000)); - } - - // Add delay after session ready before sending prompt - console.log('[RalphWizard] Adding 2s delay before sending prompt...'); - await new Promise(r => setTimeout(r, 2000)); - - // Write prompt to file first (avoids screen input escaping issues) - console.log(`[RalphWizard] Writing prompt to @ralph_prompt.md (${fullPrompt.length} chars)...`); - const writeRes = await fetch(`/api/sessions/${sessionId}/ralph-prompt/write`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ content: fullPrompt }) - }); - - if (!writeRes.ok) { - const errorData = await writeRes.json().catch(() => ({})); - throw new Error(errorData.error || `Failed to write prompt file: ${writeRes.status}`); - } - - console.log('[RalphWizard] Prompt file written.'); - - // If plan was generated, send /init first to load CLAUDE.md with research context - // This gives Claude awareness of all the research files before starting tasks - if (hasPlan) { - console.log('[RalphWizard] Sending /init to load research context from CLAUDE.md...'); - const initRes = await fetch(`/api/sessions/${sessionId}/input`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ input: '/init\r', useMux: true }) - }); - - if (!initRes.ok) { - console.warn('[RalphWizard] Failed to send /init, continuing anyway...'); - } else { - // Wait for /init to complete - can take up to 3 minutes for large CLAUDE.md files - console.log('[RalphWizard] Waiting for /init to complete (can take up to 3 minutes)...'); - let initComplete = false; - const initMaxAttempts = 180; // 3 minutes at 1 second intervals - for (let attempt = 0; attempt < initMaxAttempts; attempt++) { - await new Promise(r => setTimeout(r, 1000)); - try { - // Fetch terminal tail (lightweight) + session state separately to avoid 2-3MB full buffer - const [termRes, stateRes] = await Promise.all([ - fetch(`/api/sessions/${sessionId}/terminal?tail=2048`), - fetch(`/api/sessions/${sessionId}`) - ]); - const termData = await termRes.json(); - const statusData = await stateRes.json(); - const termBuf = termData?.terminalBuffer || ''; - // /init is complete when we see the prompt indicator (โฏ) and not working - const hasPrompt = termBuf.includes('โฏ'); - const isIdle = !statusData.isWorking; - if (hasPrompt && isIdle) { - console.log(`[RalphWizard] /init complete after ${attempt + 1}s, Claude now has research context.`); - initComplete = true; - break; - } - // Log progress every 15 seconds - if ((attempt + 1) % 15 === 0) { - console.log(`[RalphWizard] Still waiting for /init... ${attempt + 1}s elapsed (isWorking=${statusData.isWorking})`); - } - } catch (e) { - console.warn('[RalphWizard] Status check error:', e); - } - } - if (!initComplete) { - console.warn('[RalphWizard] /init did not complete within 3 minutes, proceeding anyway...'); - this.showToast('/init took longer than expected, prompt may not have full context', 'warning'); - } - // Delay after /init to let Claude settle - console.log('[RalphWizard] Adding 3s delay after /init...'); - await new Promise(r => setTimeout(r, 3000)); - } - } - - console.log('[RalphWizard] Sending read command to Claude...'); - - // Send a simple command to Claude to read the prompt file - // This avoids all the escaping issues with long multi-line prompts - const readCommand = 'Read @ralph_prompt.md and follow the instructions. Start working immediately.\r'; - const inputRes = await fetch(`/api/sessions/${sessionId}/input`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ input: readCommand, useMux: true }) - }); - - if (!inputRes.ok) { - const errorData = await inputRes.json().catch(() => ({})); - throw new Error(errorData.error || `Failed to send input: ${inputRes.status}`); - } - - console.log('[RalphWizard] Read command sent, verifying it was received...'); - - // Verify the prompt was actually received by checking for activity - let verified = false; - for (let verifyAttempt = 0; verifyAttempt < 15; verifyAttempt++) { - await new Promise(r => setTimeout(r, 1000)); - try { - const verifyRes = await fetch(`/api/sessions/${sessionId}`); - const verifyData = await verifyRes.json(); - if (verifyData.isWorking || verifyData.tokens?.total > 0) { - console.log(`[RalphWizard] Prompt verified! tokens=${verifyData.tokens?.total}`); - verified = true; - break; - } - console.log(`[RalphWizard] Verify attempt ${verifyAttempt + 1}: status=${verifyData.status}, tokens=${verifyData.tokens?.total || 0}`); - } catch (e) { - console.warn('[RalphWizard] Verify error:', e); - } - } - - if (!verified) { - console.warn('[RalphWizard] Session may not have started yet - prompt file is at @ralph_prompt.md'); - this.showToast('Session started - check @ralph_prompt.md if prompt not received', 'warning'); - } else { - this.showToast(`Ralph Loop started in ${config.caseName}`, 'success'); - } - - } catch (err) { - console.error('Failed to start Ralph loop:', err); - this.showToast('Failed to start Ralph loop: ' + err.message, 'error'); - } - } - // ========== Quick Start ========== async loadQuickStartCases(selectCaseName = null, settingsPromise = null) { @@ -15582,20 +12663,6 @@ class CodemanApp { } this.logViewerWindows.clear(); - // Clean up plan file manager and plan file windows (drag/resize handlers) - this.closePlanFileManager(); - document.querySelectorAll('.plan-file-window').forEach(win => { - if (win._dragHandlers) { - document.removeEventListener('mousemove', win._dragHandlers.move); - document.removeEventListener('mouseup', win._dragHandlers.up); - } - if (win._resizeHandlers) { - document.removeEventListener('mousemove', win._resizeHandlers.move); - document.removeEventListener('mouseup', win._resizeHandlers.up); - } - win.remove(); - }); - // Clean up plan subagent windows (wizard agents) if (this.planSubagents) { for (const [agentId, windowData] of this.planSubagents) { diff --git a/src/web/public/index.html b/src/web/public/index.html index e7e27892..01eaf765 100644 --- a/src/web/public/index.html +++ b/src/web/public/index.html @@ -247,16 +247,13 @@ Run OpenCode -

    Or press Ctrl+Enter to start

    + @@ -1535,12 +1532,6 @@ - -
    - ๐Ÿ”„ - Ralph Wizard - 0s -