diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..7592d42f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +dist/ +node_modules/ +*.md +*.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..2d30e11b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 100, + "bracketSpacing": true, + "arrowParens": "always" +} diff --git a/CLAUDE.md b/CLAUDE.md index b040965e..8c0a61ac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,47 +13,59 @@ CLI wrapper for instant switching between multiple Claude accounts and alternati - **DRY**: One source of truth (config.json) - **CLI-First**: All features must have CLI interface +## TypeScript Quality Gates (CORE PURPOSE) + +**The npm package is 100% TypeScript. Quality gates MUST pass before publish.** + +**Package Manager: bun (preferred)** - 10-25x faster than npm +```bash +bun install # Install dependencies (creates bun.lockb) +bun run build # Compile src/ → dist/ +bun run validate # Full validation: typecheck + lint + format + test +``` + +**Quality gate scripts:** +```bash +bun run typecheck # Type-check without emit (tsc --noEmit) +bun run lint # ESLint TypeScript rules +bun run lint:fix # Auto-fix lint issues +bun run format # Prettier formatting (write) +bun run format:check # Prettier check (CI) +bun run test # Build + run all tests +``` + +**Automatic enforcement:** +- `prepublishOnly` runs `validate` before `npm publish` +- `prepack` runs `validate` before `npm pack` +- CI/CD should run `bun run validate` on every PR + +**File structure:** +- `src/` - TypeScript source (development) +- `dist/` - Compiled JavaScript (production, npm package) +- `lib/` - Native shell scripts (bash, PowerShell) + +**Linting rules (eslint.config.mjs):** +- `no-unused-vars` - warn (upgrade to error incrementally) +- `no-explicit-any` - warn (upgrade to error incrementally) +- `no-non-null-assertion` - warn +- `prefer-const`, `no-var`, `eqeqeq` - error + +**Type safety rules:** +- Avoid `any` types - use proper typing or `unknown` +- Avoid `@ts-ignore` - fix the type error properly +- Strict mode enabled in tsconfig.json + ## Critical Constraints (NEVER VIOLATE) 1. **NO EMOJIS** - ASCII only: [OK], [!], [X], [i] 2. **TTY-aware colors** - Respect NO_COLOR env var 3. **Non-invasive** - NEVER modify `~/.claude/settings.json` 4. **Cross-platform parity** - bash/PowerShell/Node.js must behave identically -5. **CLI documentation** - ALL changes MUST update `--help` in bin/ccs.js, lib/ccs, lib/ccs.ps1 +5. **CLI documentation** - ALL changes MUST update `--help` in src/ccs.ts, lib/ccs, lib/ccs.ps1 6. **Idempotent** - All install operations safe to run multiple times ## Key Technical Details -### GLMT Implementation Notes - -**[!] GLMT only in Node.js version** (`bin/ccs.js`). Native shell versions don't support GLMT (requires HTTP server). - -**Critical files when working on GLMT**: -- `bin/glmt/glmt-proxy.js`: HTTP proxy server with streaming + auto-fallback -- `bin/glmt/glmt-transformer.js`: Format conversion + delta handling + tool transformation -- `bin/glmt/locale-enforcer.js`: Enforces English output -- `bin/glmt/reasoning-enforcer.js`: Injects explicit reasoning instructions (hybrid approach) -- `bin/glmt/sse-parser.js`: SSE stream parser -- `bin/glmt/delta-accumulator.js`: State tracking for streaming + tool calls -- `tests/unit/glmt/glmt-transformer.test.js`: Unit tests (35 tests passing) -- `tests/unit/glmt/reasoning-enforcer.test.js`: ReasoningEnforcer unit tests (15 tests passing) - -**Reasoning control mechanisms (hybrid approach)**: -- **Keywords**: `think`, `think hard`, `think harder`, `ultrathink` -- **Tags**: ``, `` -- **Precedence**: CLI parameter > message tags > keywords -- **Hybrid mode**: Uses BOTH API parameters (`reasoning: true`) AND prompt injection - - API params: Native Z.AI support (deterministic, zero overhead) - - Prompt injection: Explicit format instructions using `` tags - - ReasoningEnforcer has 4 effort-aware prompts (low/medium/high/max) - - **Enabled by default** for all GLMT usage (always active) - -**Security limits** (DoS protection): -- SSE buffer: 1MB max -- Content buffers: 10MB max per block -- Content blocks: 100 max per message -- Request timeout: 120s - ### Profile Mechanisms **Settings-based**: `--settings` flag → GLM, GLMT, Kimi, default @@ -75,8 +87,11 @@ Windows fallback: Copies if symlinks unavailable - PowerShell 5.1+, `$ErrorActionPreference = "Stop"` - Native JSON only, no external dependencies -### Node.js (bin/ccs.js) -- Node.js 14+, `child_process.spawn`, handle SIGINT/SIGTERM +### TypeScript/Node.js (src/*.ts → dist/*.js) +- Node.js 14+, Bun 1.0+, TypeScript 5.3, strict mode +- `child_process.spawn`, handle SIGINT/SIGTERM +- Run `bun run lint && bun run typecheck` before committing +- Format with `bun run format` if needed ### Terminal Output (ENFORCE) - ASCII only: [OK], [!], [X], [i] (NO emojis) @@ -107,7 +122,7 @@ rm -rf ~/.ccs # Clean environment ### New Feature Checklist 1. Verify YAGNI/KISS/DRY alignment - reject if doesn't align 2. Implement in bash + PowerShell + Node.js (all three) -3. **REQUIRED**: Update `--help` in bin/ccs.js, lib/ccs, lib/ccs.ps1 +3. **REQUIRED**: Update `--help` in src/ccs.ts, lib/ccs, lib/ccs.ps1 4. Test on macOS/Linux/Windows 5. Add test cases to tests/edge-cases.* 6. Update README.md if user-facing @@ -128,8 +143,9 @@ Code standards: - [ ] ASCII only (NO emojis) - [ ] TTY colors disabled when piped - [ ] NO_COLOR respected -- [ ] `--help` updated in bin/ccs.js, lib/ccs, lib/ccs.ps1 +- [ ] `--help` updated in src/ccs.ts, lib/ccs, lib/ccs.ps1 - [ ] `--help` consistent across all three +- [ ] `bun run validate` passes (typecheck + lint + format + tests) Install/behavior: - [ ] Idempotent install @@ -159,31 +175,6 @@ All env values MUST be strings (not booleans/objects) to prevent PowerShell cras } ``` -## GLMT Debugging (Common Issues) - -### Debug Mode -```bash -export CCS_DEBUG=1 -ccs glmt --verbose "test" # File logs: ~/.ccs/logs/ -``` - -### Known Issues & Fixes - -**No Thinking Blocks**: -- Check Z.AI API plan supports reasoning_content -- Test with keywords: `ccs glmt "think about the solution"` - -**Empty Thinking Blocks** (v3.5.1+): -- Fixed: Signature timing race (see tests/unit/glmt/test-thinking-signature-race.js) - -**Tool Execution Issues**: -- MCP tools outputting XML: Fixed in v3.5 -- Debug with `CCS_DEBUG=1` to inspect transformation - -**Streaming Issues**: -- Buffer errors: Hit DoS limits (1MB SSE, 10MB content) -- Auto-fallback to buffered mode on error - ## Error Handling Principles - Validate early, fail fast with clear messages diff --git a/bun.lock b/bun.lock new file mode 100644 index 00000000..09ab892d --- /dev/null +++ b/bun.lock @@ -0,0 +1,407 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "@kaitranntt/ccs", + "dependencies": { + "cli-table3": "^0.6.5", + "ora": "^9.0.0", + }, + "devDependencies": { + "@types/node": "^20.19.25", + "@typescript-eslint/eslint-plugin": "^8.48.0", + "@typescript-eslint/parser": "^8.48.0", + "eslint": "^9.39.1", + "eslint-config-prettier": "^10.1.8", + "mocha": "^11.7.5", + "prettier": "^3.6.2", + "typescript": "5.3", + }, + }, + }, + "packages": { + "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + + "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], + + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.48.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.48.0", "@typescript-eslint/type-utils": "8.48.0", "@typescript-eslint/utils": "8.48.0", "@typescript-eslint/visitor-keys": "8.48.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.48.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.48.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.48.0", "@typescript-eslint/types": "8.48.0", "@typescript-eslint/typescript-estree": "8.48.0", "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.48.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.48.0", "@typescript-eslint/types": "^8.48.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.48.0", "", { "dependencies": { "@typescript-eslint/types": "8.48.0", "@typescript-eslint/visitor-keys": "8.48.0" } }, "sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.48.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.48.0", "", { "dependencies": { "@typescript-eslint/types": "8.48.0", "@typescript-eslint/typescript-estree": "8.48.0", "@typescript-eslint/utils": "8.48.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.48.0", "", {}, "sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.48.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.48.0", "@typescript-eslint/tsconfig-utils": "8.48.0", "@typescript-eslint/types": "8.48.0", "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4", "minimatch": "^9.0.4", "semver": "^7.6.0", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.48.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.48.0", "@typescript-eslint/types": "8.48.0", "@typescript-eslint/typescript-estree": "8.48.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.48.0", "", { "dependencies": { "@typescript-eslint/types": "8.48.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "browser-stdout": ["browser-stdout@1.3.1", "", {}, "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-spinners": ["cli-spinners@3.3.0", "", {}, "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ=="], + + "cli-table3": ["cli-table3@0.6.5", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decamelize": ["decamelize@4.0.0", "", {}, "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "diff": ["diff@7.0.0", "", {}, "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="], + + "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat": ["flat@5.0.2", "", { "bin": { "flat": "cli.js" } }, "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], + + "glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], + + "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], + + "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], + + "is-plain-obj": ["is-plain-obj@2.1.0", "", {}, "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="], + + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="], + + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "mocha": ["mocha@11.7.5", "", { "dependencies": { "browser-stdout": "^1.3.1", "chokidar": "^4.0.1", "debug": "^4.3.5", "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^9.0.5", "ms": "^2.1.3", "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^9.2.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { "mocha": "bin/mocha.js", "_mocha": "bin/_mocha" } }, "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "ora": ["ora@9.0.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", "string-width": "^8.1.0", "strip-ansi": "^7.1.2" } }, "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "typescript": ["typescript@5.3.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "workerpool": ["workerpool@9.3.4", "", {}, "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yargs-unparser": ["yargs-unparser@2.0.0", "", { "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" } }, "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "log-symbols/is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], + + "mocha/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "ora/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "ora/log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], + + "ora/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], + + "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "mocha/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..8c226d88 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,50 @@ +import tseslint from '@typescript-eslint/eslint-plugin'; +import tsparser from '@typescript-eslint/parser'; +import prettier from 'eslint-config-prettier'; + +export default [ + { + files: ['src/**/*.ts'], + languageOptions: { + parser: tsparser, + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + project: './tsconfig.json', + }, + }, + plugins: { + '@typescript-eslint': tseslint, + }, + rules: { + // TypeScript rules - warnings for now, upgrade to errors incrementally + // TODO: Upgrade these to 'error' as codebase is cleaned up + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }, + ], + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-non-null-assertion': 'warn', + + // General code quality + 'no-console': 'off', // CLI tool needs console + 'prefer-const': 'error', + 'no-var': 'error', + 'eqeqeq': ['error', 'always'], + }, + }, + { + files: ['tests/**/*.js', 'scripts/**/*.js'], + languageOptions: { + ecmaVersion: 2020, + sourceType: 'commonjs', + }, + rules: { + 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + 'prefer-const': 'error', + 'no-var': 'error', + }, + }, + prettier, +]; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 897ce0c1..00000000 --- a/package-lock.json +++ /dev/null @@ -1,1495 +0,0 @@ -{ - "name": "@kaitranntt/ccs", - "version": "4.4.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@kaitranntt/ccs", - "version": "4.4.0", - "hasInstallScript": true, - "license": "MIT", - "os": [ - "darwin", - "linux", - "win32" - ], - "dependencies": { - "cli-table3": "^0.6.5", - "ora": "^9.0.0" - }, - "bin": { - "ccs": "dist/ccs.js" - }, - "devDependencies": { - "@types/node": "^20.19.25", - "mocha": "^11.7.5", - "typescript": "5.3" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@types/node": { - "version": "20.19.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", - "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true, - "license": "ISC" - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.3.0.tgz", - "integrity": "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==", - "license": "MIT", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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" - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mocha": { - "version": "11.7.5", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", - "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz", - "integrity": "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==", - "license": "MIT", - "dependencies": { - "chalk": "^5.6.2", - "cli-cursor": "^5.0.0", - "cli-spinners": "^3.2.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.1.0", - "log-symbols": "^7.0.1", - "stdin-discarder": "^0.2.2", - "string-width": "^8.1.0", - "strip-ansi": "^7.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workerpool": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", - "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json index ffaa5168..8702cf10 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,10 @@ "LICENSE" ], "engines": { - "node": ">=14.0.0" + "node": ">=14.0.0", + "bun": ">=1.0.0" }, + "packageManager": "bun@1.2.21", "os": [ "darwin", "linux", @@ -51,14 +53,20 @@ "build": "tsc && node scripts/add-shebang.js", "build:watch": "tsc --watch", "prebuild": "rm -rf dist tsconfig.tsbuildinfo", - "test": "npm run build && npm run test:all", - "test:all": "npm run test:unit && npm run test:npm", - "test:unit": "npx mocha tests/shared/unit/**/*.test.js --timeout 5000", - "test:npm": "npx mocha tests/npm/**/*.test.js --timeout 10000", + "typecheck": "tsc --noEmit", + "lint": "eslint src/", + "lint:fix": "eslint src/ --fix", + "format": "prettier --write src/", + "format:check": "prettier --check src/", + "validate": "bun run typecheck && bun run lint && bun run format:check && bun run test", + "test": "bun run build && bun run test:all", + "test:all": "bun run test:unit && bun run test:npm", + "test:unit": "mocha tests/shared/unit/**/*.test.js --timeout 5000", + "test:npm": "mocha tests/npm/**/*.test.js --timeout 10000", "test:native": "bash tests/native/unix/edge-cases.sh", "test:edge-cases": "bash tests/edge-cases.sh", - "prepublishOnly": "npm run build && node scripts/sync-version.js", - "prepack": "npm run build && node scripts/sync-version.js", + "prepublishOnly": "npm run validate && node scripts/sync-version.js", + "prepack": "npm run validate && node scripts/sync-version.js", "prepare": "node scripts/check-executables.js", "postinstall": "node scripts/postinstall.js" }, @@ -68,7 +76,12 @@ }, "devDependencies": { "@types/node": "^20.19.25", + "@typescript-eslint/eslint-plugin": "^8.48.0", + "@typescript-eslint/parser": "^8.48.0", + "eslint": "^9.39.1", + "eslint-config-prettier": "^10.1.8", "mocha": "^11.7.5", + "prettier": "^3.6.2", "typescript": "5.3" } } diff --git a/src/auth/auth-commands.ts b/src/auth/auth-commands.ts index 8a8131e8..374c0d6c 100644 --- a/src/auth/auth-commands.ts +++ b/src/auth/auth-commands.ts @@ -71,21 +71,43 @@ class AuthCommands { console.log(` ${colored('default ', 'yellow')} Set default profile`); console.log(''); console.log(colored('Examples:', 'cyan')); - console.log(` ${colored('ccs auth create work', 'yellow')} # Create & login to work profile`); - console.log(` ${colored('ccs auth default work', 'yellow')} # Set work as default`); - console.log(` ${colored('ccs auth list', 'yellow')} # List all profiles`); - console.log(` ${colored('ccs work "review code"', 'yellow')} # Use work profile`); - console.log(` ${colored('ccs "review code"', 'yellow')} # Use default profile`); + console.log( + ` ${colored('ccs auth create work', 'yellow')} # Create & login to work profile` + ); + console.log( + ` ${colored('ccs auth default work', 'yellow')} # Set work as default` + ); + console.log( + ` ${colored('ccs auth list', 'yellow')} # List all profiles` + ); + console.log( + ` ${colored('ccs work "review code"', 'yellow')} # Use work profile` + ); + console.log( + ` ${colored('ccs "review code"', 'yellow')} # Use default profile` + ); console.log(''); console.log(colored('Options:', 'cyan')); - console.log(` ${colored('--force', 'yellow')} Allow overwriting existing profile (create)`); - console.log(` ${colored('--yes, -y', 'yellow')} Skip confirmation prompts (remove)`); - console.log(` ${colored('--json', 'yellow')} Output in JSON format (list, show)`); - console.log(` ${colored('--verbose', 'yellow')} Show additional details (list)`); + console.log( + ` ${colored('--force', 'yellow')} Allow overwriting existing profile (create)` + ); + console.log( + ` ${colored('--yes, -y', 'yellow')} Skip confirmation prompts (remove)` + ); + console.log( + ` ${colored('--json', 'yellow')} Output in JSON format (list, show)` + ); + console.log( + ` ${colored('--verbose', 'yellow')} Show additional details (list)` + ); console.log(''); console.log(colored('Note:', 'cyan')); - console.log(` By default, ${colored('ccs', 'yellow')} uses Claude CLI defaults from ~/.claude/`); - console.log(` Use ${colored('ccs auth default ', 'yellow')} to change the default profile.`); + console.log( + ` By default, ${colored('ccs', 'yellow')} uses Claude CLI defaults from ~/.claude/` + ); + console.log( + ` Use ${colored('ccs auth default ', 'yellow')} to change the default profile.` + ); console.log(''); } @@ -93,13 +115,13 @@ class AuthCommands { * Parse command arguments */ private parseArgs(args: string[]): AuthCommandArgs { - const profileName = args.find(arg => !arg.startsWith('--')); + const profileName = args.find((arg) => !arg.startsWith('--')); return { profileName, force: args.includes('--force'), verbose: args.includes('--verbose'), json: args.includes('--json'), - yes: args.includes('--yes') || args.includes('-y') + yes: args.includes('--yes') || args.includes('-y'), }; } @@ -134,11 +156,11 @@ class AuthCommands { // Create/update profile entry if (this.registry.hasProfile(profileName)) { this.registry.updateProfile(profileName, { - type: 'account' + type: 'account', }); } else { this.registry.createProfile(profileName, { - type: 'account' + type: 'account', }); } @@ -161,7 +183,7 @@ class AuthCommands { // Execute Claude in isolated instance (will auto-prompt for login if no credentials) const child: ChildProcess = spawn(claudeCli, [], { stdio: 'inherit', - env: { ...process.env, CLAUDE_CONFIG_DIR: instancePath } + env: { ...process.env, CLAUDE_CONFIG_DIR: instancePath }, }); child.on('exit', (code: number | null) => { @@ -173,7 +195,9 @@ class AuthCommands { console.log(` Instance: ${instancePath}`); console.log(''); console.log('Usage:'); - console.log(` ${colored(`ccs ${profileName} "your prompt here"`, 'yellow')} # Use this specific profile`); + console.log( + ` ${colored(`ccs ${profileName} "your prompt here"`, 'yellow')} # Use this specific profile` + ); console.log(''); console.log('To set as default (so you can use just "ccs"):'); console.log(` ${colored(`ccs auth default ${profileName}`, 'yellow')}`); @@ -194,7 +218,6 @@ class AuthCommands { console.error(`[X] Failed to execute Claude CLI: ${err.message}`); process.exit(1); }); - } catch (error) { console.error(`[X] Failed to create profile: ${(error as Error).message}`); process.exit(1); @@ -216,7 +239,7 @@ class AuthCommands { if (json) { const output: ListOutput = { version: this.version, - profiles: profileNames.map(name => { + profiles: profileNames.map((name) => { const profile = profiles[name]; const isDefault = name === defaultProfile; const instancePath = this.instanceMgr.getInstancePath(name); @@ -227,9 +250,9 @@ class AuthCommands { is_default: isDefault, created: profile.created, last_used: profile.last_used || null, - instance_path: instancePath + instance_path: instancePath, }; - }) + }), }; console.log(JSON.stringify(output, null, 2)); return; @@ -240,7 +263,9 @@ class AuthCommands { console.log(colored('No account profiles found', 'yellow')); console.log(''); console.log('To create your first profile:'); - console.log(` ${colored('ccs auth create ', 'yellow')} # Create and login to profile`); + console.log( + ` ${colored('ccs auth create ', 'yellow')} # Create and login to profile` + ); console.log(''); console.log('Example:'); console.log(` ${colored('ccs auth create work', 'yellow')}`); @@ -271,12 +296,14 @@ class AuthCommands { return a.localeCompare(b); }); - sorted.forEach(name => { + sorted.forEach((name) => { const profile = profiles[name]; const isDefault = name === defaultProfile; const indicator = isDefault ? colored('[*]', 'green') : '[ ]'; - console.log(`${indicator} ${colored(name, 'cyan')}${isDefault ? colored(' (default)', 'green') : ''}`); + console.log( + `${indicator} ${colored(name, 'cyan')}${isDefault ? colored(' (default)', 'green') : ''}` + ); console.log(` Type: ${profile.type || 'account'}`); @@ -292,7 +319,6 @@ class AuthCommands { console.log(`Total profiles: ${profileNames.length}`); console.log(''); - } catch (error) { console.error(`[X] Failed to list profiles: ${(error as Error).message}`); process.exit(1); @@ -324,7 +350,7 @@ class AuthCommands { const sessionsDir = path.join(instancePath, 'session-env'); if (fs.existsSync(sessionsDir)) { const files = fs.readdirSync(sessionsDir); - sessionCount = files.filter(f => f.endsWith('.json')).length; + sessionCount = files.filter((f) => f.endsWith('.json')).length; } } catch (e) { // Ignore errors counting sessions @@ -339,7 +365,7 @@ class AuthCommands { created: profile.created, last_used: profile.last_used || null, instance_path: instancePath, - session_count: sessionCount + session_count: sessionCount, }; console.log(JSON.stringify(output, null, 2)); return; @@ -360,7 +386,6 @@ class AuthCommands { } console.log(''); - } catch (error) { console.error(`[X] ${(error as Error).message}`); process.exit(1); @@ -394,7 +419,7 @@ class AuthCommands { const sessionsDir = path.join(instancePath, 'session-env'); if (fs.existsSync(sessionsDir)) { const files = fs.readdirSync(sessionsDir); - sessionCount = files.filter(f => f.endsWith('.json')).length; + sessionCount = files.filter((f) => f.endsWith('.json')).length; } } catch (e) { // Ignore errors counting sessions @@ -408,10 +433,12 @@ class AuthCommands { console.log(''); // Interactive confirmation (or --yes flag) - const confirmed = yes || await InteractivePrompt.confirm( - 'Delete this profile?', - { default: false } // Default to NO (safe) - ); + const confirmed = + yes || + (await InteractivePrompt.confirm( + 'Delete this profile?', + { default: false } // Default to NO (safe) + )); if (!confirmed) { console.log('[i] Cancelled'); @@ -427,7 +454,6 @@ class AuthCommands { console.log(colored('[OK] Profile removed successfully', 'green')); console.log(` Profile: ${profileName}`); console.log(''); - } catch (error) { console.error(`[X] Failed to remove profile: ${(error as Error).message}`); process.exit(1); @@ -456,7 +482,6 @@ class AuthCommands { console.log('Now you can use:'); console.log(` ${colored('ccs "your prompt"', 'yellow')} # Uses ${profileName} profile`); console.log(''); - } catch (error) { console.error(`[X] ${(error as Error).message}`); process.exit(1); @@ -530,4 +555,4 @@ class AuthCommands { } } -export default AuthCommands; \ No newline at end of file +export default AuthCommands; diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index 37fd7253..ea3c2381 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -95,7 +95,7 @@ class ProfileDetector { return { type: 'settings', name: profileName, - settingsPath: config.profiles[profileName] + settingsPath: config.profiles[profileName], }; } @@ -106,7 +106,7 @@ class ProfileDetector { return { type: 'account', name: profileName, - profile: profiles.profiles[profileName] + profile: profiles.profiles[profileName], }; } @@ -133,7 +133,7 @@ class ProfileDetector { return { type: 'account', name: profiles.default, - profile: profiles.profiles[profiles.default] + profile: profiles.profiles[profiles.default], }; } @@ -144,7 +144,7 @@ class ProfileDetector { return { type: 'settings', name: 'default', - settingsPath: config.profiles['default'] + settingsPath: config.profiles['default'], }; } @@ -152,7 +152,7 @@ class ProfileDetector { return { type: 'default', name: 'default', - message: 'No profile configured. Using Claude CLI defaults from ~/.claude/' + message: 'No profile configured. Using Claude CLI defaults from ~/.claude/', }; } @@ -168,7 +168,7 @@ class ProfileDetector { if (settingsProfiles.length > 0) { lines.push('Settings-based profiles (GLM, Kimi, etc.):'); - settingsProfiles.forEach(name => { + settingsProfiles.forEach((name) => { lines.push(` - ${name}`); }); } @@ -179,15 +179,17 @@ class ProfileDetector { if (accountProfiles.length > 0) { lines.push('Account-based profiles:'); - accountProfiles.forEach(name => { + accountProfiles.forEach((name) => { const isDefault = name === profiles.default; lines.push(` - ${name}${isDefault ? ' [DEFAULT]' : ''}`); }); } if (lines.length === 0) { - return ' (no profiles configured)\n' + - ' Run "ccs auth save " to create your first account profile.'; + return ( + ' (no profiles configured)\n' + + ' Run "ccs auth save " to create your first account profile.' + ); } return lines.join('\n'); @@ -215,9 +217,9 @@ class ProfileDetector { return { settings: Object.keys(config.profiles || {}), accounts: Object.keys(profiles.profiles || {}), - default: profiles.default + default: profiles.default, }; } } -export default ProfileDetector; \ No newline at end of file +export default ProfileDetector; diff --git a/src/auth/profile-registry.ts b/src/auth/profile-registry.ts index febb0a9b..df7dd3ec 100644 --- a/src/auth/profile-registry.ts +++ b/src/auth/profile-registry.ts @@ -49,7 +49,7 @@ export class ProfileRegistry { return { version: '2.0.0', profiles: {}, - default: null + default: null, }; } @@ -103,7 +103,7 @@ export class ProfileRegistry { data.profiles[name] = { type: metadata.type || 'account', created: metadata.created || new Date().toISOString(), - last_used: metadata.last_used || null + last_used: metadata.last_used || null, }; // Note: No longer auto-set as default @@ -138,7 +138,7 @@ export class ProfileRegistry { data.profiles[name] = { ...data.profiles[name], - ...updates + ...updates, }; this._write(data); @@ -217,9 +217,9 @@ export class ProfileRegistry { */ touchProfile(name: string): void { this.updateProfile(name, { - last_used: new Date().toISOString() + last_used: new Date().toISOString(), }); } } -export default ProfileRegistry; \ No newline at end of file +export default ProfileRegistry; diff --git a/src/ccs.ts b/src/ccs.ts index d69ea992..709b6ccc 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -17,7 +17,9 @@ import { getSettingsPath, getConfigPath } from './utils/config-manager'; import { ErrorManager } from './utils/error-manager'; // Version (sync with package.json) -const CCS_VERSION = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8')).version; +const CCS_VERSION = JSON.parse( + fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8') +).version; // ========== Helper Functions ========== @@ -31,7 +33,11 @@ function escapeShellArg(arg: string): string { /** * Execute Claude CLI with unified spawn logic */ -function execClaude(claudeCli: string, args: string[], envVars: NodeJS.ProcessEnv | null = null): void { +function execClaude( + claudeCli: string, + args: string[], + envVars: NodeJS.ProcessEnv | null = null +): void { const isWindows = process.platform === 'win32'; const needsShell = isWindows && /\.(cmd|bat|ps1)$/i.test(claudeCli); @@ -46,14 +52,14 @@ function execClaude(claudeCli: string, args: string[], envVars: NodeJS.ProcessEn stdio: 'inherit', windowsHide: true, shell: true, - env + env, }); } else { // When no shell needed: use array form (faster, no shell overhead) child = spawn(claudeCli, args, { stdio: 'inherit', windowsHide: true, - env + env, }); } @@ -125,7 +131,9 @@ function handleVersionCommand(): void { if (readyProfiles.length > 0) { console.log(colored('Delegation Ready:', 'cyan')); - console.log(` ${colored('[OK]', 'yellow')} ${readyProfiles.join(', ')} profiles are ready for delegation`); + console.log( + ` ${colored('[OK]', 'yellow')} ${readyProfiles.join(', ')} profiles are ready for delegation` + ); console.log(''); } else if (delegationEnabled) { console.log(colored('Delegation Ready:', 'cyan')); @@ -136,7 +144,7 @@ function handleVersionCommand(): void { console.log(`${colored('Documentation:', 'cyan')} https://github.com/kaitranntt/ccs`); console.log(`${colored('License:', 'cyan')} MIT`); console.log(''); - console.log(colored('Run \'ccs --help\' for usage information', 'yellow')); + console.log(colored("Run 'ccs --help' for usage information", 'yellow')); process.exit(0); } @@ -145,7 +153,9 @@ function handleVersionCommand(): void { * Handle help command */ function handleHelpCommand(): void { - console.log(colored('CCS (Claude Code Switch) - Instant profile switching for Claude CLI', 'bold')); + console.log( + colored('CCS (Claude Code Switch) - Instant profile switching for Claude CLI', 'bold') + ); console.log(''); console.log(colored('Usage:', 'cyan')); @@ -162,34 +172,52 @@ function handleHelpCommand(): void { console.log(colored('Model Switching:', 'cyan')); console.log(` ${colored('ccs', 'yellow')} Use default Claude account`); console.log(` ${colored('ccs glm', 'yellow')} Switch to GLM 4.6 model`); - console.log(` ${colored('ccs glmt', 'yellow')} Switch to GLM with thinking mode`); + console.log( + ` ${colored('ccs glmt', 'yellow')} Switch to GLM with thinking mode` + ); console.log(` ${colored('ccs glmt --verbose', 'yellow')} Enable debug logging`); console.log(` ${colored('ccs kimi', 'yellow')} Switch to Kimi for Coding`); console.log(` ${colored('ccs glm', 'yellow')} "debug this code" Use GLM and run command`); console.log(''); console.log(colored('Account Management:', 'cyan')); - console.log(` ${colored('ccs auth --help', 'yellow')} Run multiple Claude accounts concurrently`); + console.log( + ` ${colored('ccs auth --help', 'yellow')} Run multiple Claude accounts concurrently` + ); console.log(''); console.log(colored('Delegation (inside Claude Code CLI):', 'cyan')); - console.log(` ${colored('/ccs "task"', 'yellow')} Delegate task (auto-selects best profile)`); - console.log(` ${colored('/ccs --glm "task"', 'yellow')} Force GLM-4.6 for simple tasks`); + console.log( + ` ${colored('/ccs "task"', 'yellow')} Delegate task (auto-selects best profile)` + ); + console.log( + ` ${colored('/ccs --glm "task"', 'yellow')} Force GLM-4.6 for simple tasks` + ); console.log(` ${colored('/ccs --kimi "task"', 'yellow')} Force Kimi for long context`); - console.log(` ${colored('/ccs:continue "follow-up"', 'yellow')} Continue last delegation session`); + console.log( + ` ${colored('/ccs:continue "follow-up"', 'yellow')} Continue last delegation session` + ); console.log(' Save tokens by delegating simple tasks to cost-optimized models'); console.log(''); console.log(colored('Diagnostics:', 'cyan')); - console.log(` ${colored('ccs doctor', 'yellow')} Run health check and diagnostics`); - console.log(` ${colored('ccs sync', 'yellow')} Sync delegation commands and skills`); + console.log( + ` ${colored('ccs doctor', 'yellow')} Run health check and diagnostics` + ); + console.log( + ` ${colored('ccs sync', 'yellow')} Sync delegation commands and skills` + ); console.log(` ${colored('ccs update', 'yellow')} Update CCS to latest version`); console.log(''); console.log(colored('Flags:', 'cyan')); console.log(` ${colored('-h, --help', 'yellow')} Show this help message`); - console.log(` ${colored('-v, --version', 'yellow')} Show version and installation info`); - console.log(` ${colored('-sc, --shell-completion', 'yellow')} Install shell auto-completion`); + console.log( + ` ${colored('-v, --version', 'yellow')} Show version and installation info` + ); + console.log( + ` ${colored('-sc, --shell-completion', 'yellow')} Install shell auto-completion` + ); console.log(''); console.log(colored('Configuration:', 'cyan')); @@ -212,7 +240,9 @@ function handleHelpCommand(): void { console.log(` ${colored('$ ccs', 'yellow')} # Use default account`); console.log(` ${colored('$ ccs glm "implement API"', 'yellow')} # Cost-optimized model`); console.log(''); - console.log(` For more: ${colored('https://github.com/kaitranntt/ccs/blob/main/README.md', 'cyan')}`); + console.log( + ` For more: ${colored('https://github.com/kaitranntt/ccs/blob/main/README.md', 'cyan')}` + ); console.log(''); console.log(colored('Uninstall:', 'yellow')); @@ -312,7 +342,7 @@ function detectInstallationMethod(): 'npm' | 'direct' { /\.npm\/global\/bin\//, /\/\.nvm\/versions\/node\/[^/]+\/bin\//, /\/usr\/local\/bin\//, - /\/usr\/bin\// + /\/usr\/bin\//, ]; for (const pattern of npmGlobalBinPatterns) { @@ -388,7 +418,7 @@ function detectPackageManager(): 'npm' | 'yarn' | 'pnpm' | 'bun' { const yarnResult = spawnSync('yarn', ['global', 'list', '--pattern', '@kaitranntt/ccs'], { encoding: 'utf8', shell: true, - timeout: 5000 + timeout: 5000, }); if (yarnResult.status === 0 && yarnResult.stdout.includes('@kaitranntt/ccs')) { return 'yarn'; @@ -401,7 +431,7 @@ function detectPackageManager(): 'npm' | 'yarn' | 'pnpm' | 'bun' { const pnpmResult = spawnSync('pnpm', ['list', '-g', '--pattern', '@kaitranntt/ccs'], { encoding: 'utf8', shell: true, - timeout: 5000 + timeout: 5000, }); if (pnpmResult.status === 0 && pnpmResult.stdout.includes('@kaitranntt/ccs')) { return 'pnpm'; @@ -414,7 +444,7 @@ function detectPackageManager(): 'npm' | 'yarn' | 'pnpm' | 'bun' { const bunResult = spawnSync('bun', ['pm', 'ls', '-g', '--pattern', '@kaitranntt/ccs'], { encoding: 'utf8', shell: true, - timeout: 5000 + timeout: 5000, }); if (bunResult.status === 0 && bunResult.stdout.includes('@kaitranntt/ccs')) { return 'bun'; @@ -501,7 +531,9 @@ async function handleUpdateCommand(): Promise { } // Update available - console.log(colored(`[i] Update available: ${updateResult.current} -> ${updateResult.latest}`, 'yellow')); + console.log( + colored(`[i] Update available: ${updateResult.current} -> ${updateResult.latest}`, 'yellow') + ); console.log(''); if (isNpmInstall) { @@ -548,7 +580,7 @@ async function handleUpdateCommand(): Promise { const performUpdate = (): void => { const child = spawn(updateCommand, updateArgs, { - stdio: 'inherit' + stdio: 'inherit', }); child.on('exit', (code) => { @@ -583,7 +615,7 @@ async function handleUpdateCommand(): Promise { if (cacheCommand && cacheArgs) { console.log(colored('Clearing package cache...', 'cyan')); const cacheChild = spawn(cacheCommand, cacheArgs, { - stdio: 'inherit' + stdio: 'inherit', }); cacheChild.on('exit', (code) => { @@ -611,15 +643,20 @@ async function handleUpdateCommand(): Promise { if (isWindows) { command = 'powershell.exe'; - args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', - 'irm ccs.kaitran.ca/install | iex']; + args = [ + '-NoProfile', + '-ExecutionPolicy', + 'Bypass', + '-Command', + 'irm ccs.kaitran.ca/install | iex', + ]; } else { command = '/bin/bash'; args = ['-c', 'curl -fsSL ccs.kaitran.ca/install | bash']; } const child = spawn(command, args, { - stdio: 'inherit' + stdio: 'inherit', }); child.on('exit', (code) => { @@ -685,7 +722,11 @@ function detectProfile(args: string[]): DetectedProfile { /** * Execute Claude CLI with embedded proxy (for GLMT profile) */ -async function execClaudeWithProxy(claudeCli: string, profileName: string, args: string[]): Promise { +async function execClaudeWithProxy( + claudeCli: string, + profileName: string, + args: string[] +): Promise { // 1. Read settings to get API key const settingsPath = getSettingsPath(profileName); const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); @@ -705,7 +746,7 @@ async function execClaudeWithProxy(claudeCli: string, profileName: string, args: const proxyArgs = verbose ? ['--verbose'] : []; // Use process.execPath for Windows compatibility (CVE-2024-27980) const proxy = spawn(process.execPath, [proxyPath, ...proxyArgs], { - stdio: ['ignore', 'pipe', verbose ? 'pipe' : 'inherit'] + stdio: ['ignore', 'pipe', verbose ? 'pipe' : 'inherit'], }); // 3. Wait for proxy ready signal (with timeout) @@ -765,7 +806,7 @@ async function execClaudeWithProxy(claudeCli: string, profileName: string, args: const envVars: NodeJS.ProcessEnv = { ANTHROPIC_BASE_URL: `http://127.0.0.1:${port}`, ANTHROPIC_AUTH_TOKEN: apiKey, - ANTHROPIC_MODEL: 'glm-4.6' + ANTHROPIC_MODEL: 'glm-4.6', }; const isWindows = process.platform === 'win32'; @@ -779,13 +820,13 @@ async function execClaudeWithProxy(claudeCli: string, profileName: string, args: stdio: 'inherit', windowsHide: true, shell: true, - env + env, }); } else { claude = spawn(claudeCli, args, { stdio: 'inherit', windowsHide: true, - env + env, }); } @@ -1019,7 +1060,7 @@ async function main(): Promise { } // Run main -main().catch(error => { +main().catch((error) => { console.error('Fatal error:', error.message); process.exit(1); }); diff --git a/src/delegation/delegation-handler.ts b/src/delegation/delegation-handler.ts index 31def890..c9ac176f 100644 --- a/src/delegation/delegation-handler.ts +++ b/src/delegation/delegation-handler.ts @@ -42,11 +42,7 @@ export class DelegationHandler { this._validateProfile(parsed.profile); // 4. Execute via HeadlessExecutor - const result = await HeadlessExecutor.execute( - parsed.profile, - parsed.prompt, - parsed.options - ); + const result = await HeadlessExecutor.execute(parsed.profile, parsed.prompt, parsed.options); // 5. Format and display results const formatted = ResultFormatter.format(result); @@ -81,15 +77,11 @@ export class DelegationHandler { } // Execute with resume flag - const result = await HeadlessExecutor.execute( - baseProfile, - parsed.prompt, - { - ...parsed.options, - resumeSession: true, - sessionId: lastSession.sessionId - } - ); + const result = await HeadlessExecutor.execute(baseProfile, parsed.prompt, { + ...parsed.options, + resumeSession: true, + sessionId: lastSession.sessionId, + }); const formatted = ResultFormatter.format(result); console.log(formatted); @@ -178,7 +170,7 @@ export class DelegationHandler { const options: ParsedArgs['options'] = { cwd, outputFormat: 'stream-json', - permissionMode: defaultPermissionMode + permissionMode: defaultPermissionMode, }; // Parse permission-mode (CLI flag overrides settings file) @@ -219,4 +211,4 @@ export class DelegationHandler { process.exit(1); } } -} \ No newline at end of file +} diff --git a/src/delegation/headless-executor.ts b/src/delegation/headless-executor.ts index 6844823a..6625a557 100644 --- a/src/delegation/headless-executor.ts +++ b/src/delegation/headless-executor.ts @@ -81,7 +81,7 @@ export class HeadlessExecutor { timeout = 600000, // 10 minutes default permissionMode = 'acceptEdits', resumeSession = false, - sessionId = null + sessionId = null, } = options; // Validate permission mode @@ -93,7 +93,9 @@ export class HeadlessExecutor { // Detect Claude CLI path const claudeCli = this._detectClaudeCli(); if (!claudeCli) { - throw new Error('Claude CLI not found in PATH. Install from: https://docs.claude.com/en/docs/claude-code/installation'); + throw new Error( + 'Claude CLI not found in PATH. Install from: https://docs.claude.com/en/docs/claude-code/installation' + ); } // Get settings path for profile @@ -101,7 +103,9 @@ export class HeadlessExecutor { // Validate settings file exists if (!fs.existsSync(settingsPath)) { - throw new Error(`Settings file not found: ${settingsPath}\nProfile "${profile}" may not be configured.`); + throw new Error( + `Settings file not found: ${settingsPath}\nProfile "${profile}" may not be configured.` + ); } // Smart slash command detection and preservation @@ -122,7 +126,9 @@ export class HeadlessExecutor { // Warn about dangerous mode if (process.env.CCS_DEBUG) { console.warn('[!] WARNING: Using --dangerously-skip-permissions mode'); - console.warn('[!] This bypasses ALL permission checks. Use only in trusted environments.'); + console.warn( + '[!] This bypasses ALL permission checks. Use only in trusted environments.' + ); } } else { args.push('--permission-mode', permissionMode); @@ -136,8 +142,13 @@ export class HeadlessExecutor { if (lastSession) { args.push('--resume', lastSession.sessionId); if (process.env.CCS_DEBUG) { - const cost = lastSession.totalCost !== undefined && lastSession.totalCost !== null ? lastSession.totalCost.toFixed(4) : '0.0000'; - console.error(`[i] Resuming session: ${lastSession.sessionId} (${lastSession.turns} turns, $${cost})`); + const cost = + lastSession.totalCost !== undefined && lastSession.totalCost !== null + ? lastSession.totalCost.toFixed(4) + : '0.0000'; + console.error( + `[i] Resuming session: ${lastSession.sessionId} (${lastSession.turns} turns, $${cost})` + ); } } else if (sessionId) { args.push('--resume', sessionId); @@ -159,12 +170,12 @@ export class HeadlessExecutor { if (toolRestrictions.allowedTools.length > 0) { args.push('--allowedTools'); - toolRestrictions.allowedTools.forEach(tool => args.push(tool)); + toolRestrictions.allowedTools.forEach((tool) => args.push(tool)); } if (toolRestrictions.disallowedTools.length > 0) { args.push('--disallowedTools'); - toolRestrictions.disallowedTools.forEach(tool => args.push(tool)); + toolRestrictions.disallowedTools.forEach((tool) => args.push(tool)); } // Note: No max-turns limit - using time-based limits instead (default 10min timeout) @@ -183,14 +194,15 @@ export class HeadlessExecutor { // Show initial progress message if (showProgress) { - const modelName = profile === 'glm' ? 'GLM-4.6' : profile === 'kimi' ? 'Kimi' : profile.toUpperCase(); + const modelName = + profile === 'glm' ? 'GLM-4.6' : profile === 'kimi' ? 'Kimi' : profile.toUpperCase(); console.error(`[i] Delegating to ${modelName}...`); } const proc = spawn(claudeCli, args, { cwd, stdio: ['ignore', 'pipe', 'pipe'], - timeout + timeout, }); let stdout = ''; @@ -256,7 +268,7 @@ export class HeadlessExecutor { // Show real-time tool use with verbose details if (showProgress && msg.type === 'assistant') { - const toolUses = msg.message?.content?.filter(c => c.type === 'tool_use') || []; + const toolUses = msg.message?.content?.filter((c) => c.type === 'tool_use') || []; for (const tool of toolUses) { process.stderr.write('\r\x1b[K'); // Clear line @@ -270,9 +282,10 @@ export class HeadlessExecutor { case 'Bash': if (toolInput.command) { // Truncate long commands - const cmd = toolInput.command.length > 80 - ? toolInput.command.substring(0, 77) + '...' - : toolInput.command; + const cmd = + toolInput.command.length > 80 + ? toolInput.command.substring(0, 77) + '...' + : toolInput.command; verboseMsg += `: ${cmd}`; } break; @@ -317,9 +330,10 @@ export class HeadlessExecutor { if (toolInput.description) { verboseMsg += `: ${toolInput.description}`; } else if (toolInput.prompt) { - const prompt = toolInput.prompt.length > 60 - ? toolInput.prompt.substring(0, 57) + '...' - : toolInput.prompt; + const prompt = + toolInput.prompt.length > 60 + ? toolInput.prompt.substring(0, 57) + '...' + : toolInput.prompt; verboseMsg += `: ${prompt}`; } break; @@ -327,7 +341,9 @@ export class HeadlessExecutor { case 'TodoWrite': if (toolInput.todos && Array.isArray(toolInput.todos)) { // Show in_progress task instead of just count - const inProgressTask = toolInput.todos.find((t: any) => t.status === 'in_progress'); + const inProgressTask = toolInput.todos.find( + (t: any) => t.status === 'in_progress' + ); if (inProgressTask && inProgressTask.activeForm) { verboseMsg += `: ${inProgressTask.activeForm}`; } else { @@ -366,7 +382,9 @@ export class HeadlessExecutor { } catch (parseError) { // Skip malformed JSON lines (shouldn't happen with stream-json) if (process.env.CCS_DEBUG) { - console.error(`[!] Failed to parse stream-json line: ${(parseError as Error).message}`); + console.error( + `[!] Failed to parse stream-json line: ${(parseError as Error).message}` + ); } } } @@ -416,12 +434,12 @@ export class HeadlessExecutor { profile, duration, timedOut: false, - success: (exitCode === 0) && !timedOut, - messages // Include all stream-json messages + success: exitCode === 0 && !timedOut, + messages, // Include all stream-json messages }; // Extract metadata from final 'result' message in stream-json - const resultMessage = messages.find(m => m.type === 'result'); + const resultMessage = messages.find((m) => m.type === 'result'); if (resultMessage) { // Add parsed fields from result message result.sessionId = resultMessage.session_id || undefined; @@ -449,19 +467,20 @@ export class HeadlessExecutor { if (resumeSession || sessionId) { // Update existing session sessionMgr.updateSession(profile, result.sessionId, { - totalCost: result.totalCost + totalCost: result.totalCost, }); } else { // Store new session sessionMgr.storeSession(profile, { sessionId: result.sessionId, totalCost: result.totalCost, - cwd: result.cwd + cwd: result.cwd, }); } // Cleanup expired sessions periodically - if (Math.random() < 0.1) { // 10% chance + if (Math.random() < 0.1) { + // 10% chance sessionMgr.cleanupExpired(); } } @@ -490,7 +509,9 @@ export class HeadlessExecutor { } if (process.env.CCS_DEBUG) { - console.error(`[!] Timeout reached after ${timeout}ms, sending SIGTERM for graceful shutdown...`); + console.error( + `[!] Timeout reached after ${timeout}ms, sending SIGTERM for graceful shutdown...` + ); } // Send SIGTERM for graceful shutdown @@ -523,9 +544,7 @@ export class HeadlessExecutor { private static _validatePermissionMode(mode: string): void { const VALID_MODES = ['default', 'plan', 'acceptEdits', 'bypassPermissions']; if (!VALID_MODES.includes(mode)) { - throw new Error( - `Invalid permission mode: "${mode}". Valid modes: ${VALID_MODES.join(', ')}` - ); + throw new Error(`Invalid permission mode: "${mode}". Valid modes: ${VALID_MODES.join(', ')}`); } } @@ -587,7 +606,9 @@ export class HeadlessExecutor { lastError = error as Error; if (attempt < maxRetries) { - console.error(`[!] Attempt ${attempt + 1} errored: ${(error as Error).message}, retrying...`); + console.error( + `[!] Attempt ${attempt + 1} errored: ${(error as Error).message}, retrying...` + ); await this._sleep(1000 * (attempt + 1)); } } @@ -604,7 +625,7 @@ export class HeadlessExecutor { * @private */ private static _sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } /** @@ -659,11 +680,11 @@ export class HeadlessExecutor { static async testProfile(profile: string): Promise { try { const result = await this.execute(profile, 'Say "test successful"', { - timeout: 10000 + timeout: 10000, }); return result.success; } catch (error) { return false; } } -} \ No newline at end of file +} diff --git a/src/delegation/result-formatter.ts b/src/delegation/result-formatter.ts index 7aa0f66a..1e476bf0 100644 --- a/src/delegation/result-formatter.ts +++ b/src/delegation/result-formatter.ts @@ -56,7 +56,23 @@ class ResultFormatter { * Format execution result with complete source-of-truth */ static format(result: ExecutionResult): string { - const { profile, cwd, exitCode, stdout, stderr, duration, success, content, sessionId, totalCost, numTurns, subtype, permissionDenials, errors, timedOut } = result; + const { + profile, + cwd, + exitCode, + stdout, + stderr, + duration, + success, + content, + sessionId, + totalCost, + numTurns, + subtype, + permissionDenials, + errors, + timedOut, + } = result; // Handle timeout (graceful termination) if (timedOut) { @@ -124,7 +140,7 @@ class ResultFormatter { /write:\s*([^\n\r]+)/gi, /new file:\s*([^\n\r]+)/gi, /generated:\s*([^\n\r]+)/gi, - /added:\s*([^\n\r]+)/gi + /added:\s*([^\n\r]+)/gi, ]; const modifiedPatterns = [ @@ -133,7 +149,7 @@ class ResultFormatter { /updated:\s*([^\n\r]+)/gi, /edit:\s*([^\n\r]+)/gi, /edited:\s*([^\n\r]+)/gi, - /changed:\s*([^\n\r]+)/gi + /changed:\s*([^\n\r]+)/gi, ]; // Helper to check if file is infrastructure (should be ignored) @@ -158,7 +174,12 @@ class ResultFormatter { while ((match = pattern.exec(output)) !== null) { const filePath = match[1].trim(); // Don't include if already in created list or is infrastructure - if (filePath && !modified.includes(filePath) && !created.includes(filePath) && !isInfrastructure(filePath)) { + if ( + filePath && + !modified.includes(filePath) && + !created.includes(filePath) && + !isInfrastructure(filePath) + ) { modified.push(filePath); } } @@ -171,8 +192,8 @@ class ResultFormatter { const findCmd = `find . -type f -mmin -5 -not -path "./.git/*" -not -path "./node_modules/*" -not -path "./.claude/*" 2>/dev/null | head -20`; const result = execSync(findCmd, { cwd, encoding: 'utf8', timeout: 5000 }); - const files = result.split('\n').filter(f => f.trim()); - files.forEach(file => { + const files = result.split('\n').filter((f) => f.trim()); + files.forEach((file) => { const fullPath = path.join(cwd, file); // Double-check not infrastructure @@ -188,7 +209,7 @@ class ResultFormatter { // If both mtime and ctime are very recent (within 10 minutes), likely created // ctime = inode change time, for new files this is close to creation time - const isVeryRecent = (now - mtime) < 600000 && (now - ctime) < 600000; + const isVeryRecent = now - mtime < 600000 && now - ctime < 600000; const timeDiff = Math.abs(mtime - ctime); // If mtime and ctime are very close (< 1 second apart) and both recent, it's created @@ -232,7 +253,15 @@ class ResultFormatter { /** * Format info box with delegation details */ - private static formatInfoBox(cwd: string, profile: string, duration: number, exitCode: number, sessionId?: string, totalCost?: number, numTurns?: number): string { + private static formatInfoBox( + cwd: string, + profile: string, + duration: number, + exitCode: number, + sessionId?: string, + totalCost?: number, + numTurns?: number + ): string { const modelName = this.getModelDisplayName(profile); const durationSec = (duration / 1000).toFixed(1); @@ -245,7 +274,7 @@ class ResultFormatter { `Working Directory: ${this.truncate(cwd, boxWidth - 22)}`, `Model: ${modelName}`, `Duration: ${durationSec}s`, - `Exit Code: ${exitCode}` + `Exit Code: ${exitCode}`, ]; // Add JSON-specific fields if available @@ -321,10 +350,10 @@ class ResultFormatter { */ private static getModelDisplayName(profile: string): string { const displayNames: Record = { - 'glm': 'GLM-4.6', - 'glmt': 'GLM-4.6 (Thinking)', - 'kimi': 'Kimi', - 'default': 'Claude' + glm: 'GLM-4.6', + glmt: 'GLM-4.6 (Thinking)', + kimi: 'Kimi', + default: 'Claude', }; return displayNames[profile] || profile.toUpperCase(); @@ -462,4 +491,4 @@ class ResultFormatter { } } -export { ResultFormatter }; \ No newline at end of file +export { ResultFormatter }; diff --git a/src/delegation/session-manager.ts b/src/delegation/session-manager.ts index 42d55471..b9fba1f6 100644 --- a/src/delegation/session-manager.ts +++ b/src/delegation/session-manager.ts @@ -56,7 +56,7 @@ class SessionManager { lastTurnTime: Date.now(), totalCost: sessionData.totalCost || 0, turns: 1, - cwd: sessionData.cwd || process.cwd() + cwd: sessionData.cwd || process.cwd(), }; this.saveSessions(sessions); @@ -80,8 +80,13 @@ class SessionManager { this.saveSessions(sessions); if (process.env.CCS_DEBUG) { - const cost = sessions[key].totalCost !== undefined && sessions[key].totalCost !== null ? sessions[key].totalCost.toFixed(4) : '0.0000'; - console.error(`[i] Updated session: ${sessionId}, total: $${cost}, turns: ${sessions[key].turns}`); + const cost = + sessions[key].totalCost !== undefined && sessions[key].totalCost !== null + ? sessions[key].totalCost.toFixed(4) + : '0.0000'; + console.error( + `[i] Updated session: ${sessionId}, total: $${cost}, turns: ${sessions[key].turns}` + ); } } } @@ -114,7 +119,7 @@ class SessionManager { const maxAge = 30 * 24 * 60 * 60 * 1000; // 30 days let cleaned = 0; - Object.keys(sessions).forEach(key => { + Object.keys(sessions).forEach((key) => { if (now - sessions[key].lastTurnTime > maxAge) { delete sessions[key]; cleaned++; @@ -156,15 +161,11 @@ class SessionManager { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true, mode: 0o700 }); } - fs.writeFileSync( - this.sessionsPath, - JSON.stringify(sessions, null, 2), - { mode: 0o600 } - ); + fs.writeFileSync(this.sessionsPath, JSON.stringify(sessions, null, 2), { mode: 0o600 }); } catch (error) { console.error(`[!] Failed to save sessions: ${(error as Error).message}`); } } } -export { SessionManager }; \ No newline at end of file +export { SessionManager }; diff --git a/src/delegation/settings-parser.ts b/src/delegation/settings-parser.ts index a51b1ea6..72c3fc3b 100644 --- a/src/delegation/settings-parser.ts +++ b/src/delegation/settings-parser.ts @@ -57,7 +57,7 @@ class SettingsParser { return { allowedTools: allowed, - disallowedTools: denied + disallowedTools: denied, }; } @@ -78,17 +78,11 @@ class SettingsParser { // Merge permissions (local overrides shared) return { permissions: { - allow: [ - ...(shared.permissions?.allow || []), - ...(local.permissions?.allow || []) - ], - deny: [ - ...(shared.permissions?.deny || []), - ...(local.permissions?.deny || []) - ], + allow: [...(shared.permissions?.allow || []), ...(local.permissions?.allow || [])], + deny: [...(shared.permissions?.deny || []), ...(local.permissions?.deny || [])], // Local defaultMode takes priority over shared - defaultMode: local.permissions?.defaultMode || shared.permissions?.defaultMode || undefined - } + defaultMode: local.permissions?.defaultMode || shared.permissions?.defaultMode || undefined, + }, }; } @@ -112,4 +106,4 @@ class SettingsParser { } } -export { SettingsParser }; \ No newline at end of file +export { SettingsParser }; diff --git a/src/glmt/delta-accumulator.ts b/src/glmt/delta-accumulator.ts index 4104d2c8..399ba80d 100644 --- a/src/glmt/delta-accumulator.ts +++ b/src/glmt/delta-accumulator.ts @@ -156,7 +156,7 @@ export class DeltaAccumulator { type: type, content: '', started: true, - stopped: false + stopped: false, }; this.contentBlocks.push(block); @@ -215,7 +215,9 @@ export class DeltaAccumulator { // FIX: Log block closure for debugging (helps diagnose timing issues) if (block.type === 'thinking' && process.env.CCS_DEBUG === '1') { - console.error(`[DeltaAccumulator] Stopped thinking block ${block.index}: ${block.content?.length || 0} chars`); + console.error( + `[DeltaAccumulator] Stopped thinking block ${block.index}: ${block.content?.length || 0} chars` + ); } } } @@ -246,8 +248,8 @@ export class DeltaAccumulator { type: 'function', function: { name: '', - arguments: '' - } + arguments: '', + }, }; this.toolCalls.push(toolCall); this.toolCallsIndex[index] = toolCall; @@ -304,7 +306,7 @@ export class DeltaAccumulator { const recentBlocks = this.contentBlocks.slice(-this.loopDetectionThreshold); // Check if all recent blocks are thinking blocks - const allThinking = recentBlocks.every(b => b.type === 'thinking'); + const allThinking = recentBlocks.every((b) => b.type === 'thinking'); // Check if no tool calls have been made at all const noToolCalls = this.toolCalls.length === 0; @@ -342,8 +344,8 @@ export class DeltaAccumulator { loopDetected: this.loopDetected, usage: { input_tokens: this.inputTokens, - output_tokens: this.outputTokens - } + output_tokens: this.outputTokens, + }, }; } @@ -479,4 +481,4 @@ export class DeltaAccumulator { getToolCall(index: number): ToolCall | undefined { return this.toolCallsIndex[index]; } -} \ No newline at end of file +} diff --git a/src/glmt/glmt-proxy.ts b/src/glmt/glmt-proxy.ts index bb017bdc..c47fbdc9 100644 --- a/src/glmt/glmt-proxy.ts +++ b/src/glmt/glmt-proxy.ts @@ -83,10 +83,12 @@ export class GlmtProxy { constructor(config: GlmtProxyConfig = {}) { this.transformer = new GlmtTransformer({ verbose: config.verbose, - debugLog: config.debugLog || process.env.CCS_DEBUG === '1' || process.env.CCS_DEBUG_LOG === '1' + debugLog: + config.debugLog || process.env.CCS_DEBUG === '1' || process.env.CCS_DEBUG_LOG === '1', }); // Use ANTHROPIC_BASE_URL from environment (set by settings.json) or fallback to Z.AI default - this.upstreamUrl = process.env.ANTHROPIC_BASE_URL || 'https://api.z.ai/api/coding/paas/v4/chat/completions'; + this.upstreamUrl = + process.env.ANTHROPIC_BASE_URL || 'https://api.z.ai/api/coding/paas/v4/chat/completions'; this.server = null; this.port = null; this.verbose = config.verbose || false; @@ -111,12 +113,16 @@ export class GlmtProxy { // Info message (only show in verbose mode) if (this.verbose) { - console.error(`[glmt] Proxy listening on port ${this.port} (streaming with auto-fallback)`); + console.error( + `[glmt] Proxy listening on port ${this.port} (streaming with auto-fallback)` + ); } // Debug mode notice if ((this.transformer as unknown as { debugLog: boolean }).debugLog) { - console.error(`[glmt] Debug logging enabled: ${(this.transformer as unknown as { debugLogDir: string }).debugLogDir}`); + console.error( + `[glmt] Debug logging enabled: ${(this.transformer as unknown as { debugLogDir: string }).debugLogDir}` + ); console.error(`[glmt] WARNING: Debug logs contain full request/response data`); } @@ -157,18 +163,22 @@ export class GlmtProxy { } catch (jsonError) { const err = jsonError as Error; res.writeHead(400, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ - error: { - type: 'invalid_request_error', - message: 'Invalid JSON in request body: ' + err.message - } - })); + res.end( + JSON.stringify({ + error: { + type: 'invalid_request_error', + message: 'Invalid JSON in request body: ' + err.message, + }, + }) + ); return; } // Log thinking parameter for debugging if (anthropicRequest.thinking) { - this.log(`Request contains thinking parameter: ${JSON.stringify(anthropicRequest.thinking)}`); + this.log( + `Request contains thinking parameter: ${JSON.stringify(anthropicRequest.thinking)}` + ); } else { this.log(`Request does NOT contain thinking parameter (will use message tags or default)`); } @@ -192,7 +202,6 @@ export class GlmtProxy { } else { await this.handleBufferedRequest(req, res, anthropicRequest, startTime); } - } catch (error) { const err = error as Error; console.error('[glmt-proxy] Request error:', err.message); @@ -200,12 +209,14 @@ export class GlmtProxy { this.log(`Request failed after ${duration}ms: ${err.message}`); res.writeHead(500, { 'Content-Type': 'application/json' }); - res.end(JSON.stringify({ - error: { - type: 'proxy_error', - message: err.message - } - })); + res.end( + JSON.stringify({ + error: { + type: 'proxy_error', + message: err.message, + }, + }) + ); } } @@ -219,16 +230,17 @@ export class GlmtProxy { startTime: number ): Promise { // Transform to OpenAI format - const { openaiRequest, thinkingConfig } = - this.transformer.transformRequest(anthropicRequest as unknown as Parameters[0]); + const { openaiRequest, thinkingConfig } = this.transformer.transformRequest( + anthropicRequest as unknown as Parameters[0] + ); this.log(`Transformed request, thinking: ${thinkingConfig.thinking}`); // Forward to Z.AI - const openaiResponse = await this.forwardToUpstream( + const openaiResponse = (await this.forwardToUpstream( openaiRequest as unknown as OpenAIRequest, {} - ) as OpenAIResponse; + )) as OpenAIResponse; this.log(`Received response from upstream`); @@ -241,7 +253,7 @@ export class GlmtProxy { // Return to Claude CLI res.writeHead(200, { 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*' + 'Access-Control-Allow-Origin': '*', }); res.end(JSON.stringify(anthropicResponse)); @@ -261,8 +273,9 @@ export class GlmtProxy { this.log('Using streaming mode'); // Transform request - const { openaiRequest, thinkingConfig } = - this.transformer.transformRequest(anthropicRequest as unknown as Parameters[0]); + const { openaiRequest, thinkingConfig } = this.transformer.transformRequest( + anthropicRequest as unknown as Parameters[0] + ); // Force streaming (openaiRequest as OpenAIRequest).stream = true; @@ -271,9 +284,9 @@ export class GlmtProxy { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive', + Connection: 'keep-alive', 'Access-Control-Allow-Origin': '*', - 'X-Accel-Buffering': 'no' // Disable proxy buffering + 'X-Accel-Buffering': 'no', // Disable proxy buffering }); // Disable Nagle's algorithm to prevent buffering at socket level @@ -335,9 +348,9 @@ export class GlmtProxy { headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(requestBody), - 'Authorization': process.env.ANTHROPIC_AUTH_TOKEN || '', - 'User-Agent': 'CCS-GLMT-Proxy/1.0' - } + Authorization: process.env.ANTHROPIC_AUTH_TOKEN || '', + 'User-Agent': 'CCS-GLMT-Proxy/1.0', + }, }; // Debug logging @@ -362,9 +375,7 @@ export class GlmtProxy { // Check for non-200 status if (res.statusCode !== 200) { - reject(new Error( - `Upstream error: ${res.statusCode} ${res.statusMessage}\n${body}` - )); + reject(new Error(`Upstream error: ${res.statusCode} ${res.statusMessage}\n${body}`)); return; } @@ -409,10 +420,10 @@ export class GlmtProxy { headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(requestBody), - 'Authorization': process.env.ANTHROPIC_AUTH_TOKEN || '', + Authorization: process.env.ANTHROPIC_AUTH_TOKEN || '', 'User-Agent': 'CCS-GLMT-Proxy/1.0', - 'Accept': 'text/event-stream' - } + Accept: 'text/event-stream', + }, }; this.log(`Forwarding streaming request to: ${url.hostname}${url.pathname}`); @@ -427,7 +438,7 @@ export class GlmtProxy { clearTimeout(timeoutHandle); if (upstreamRes.statusCode !== 200) { let body = ''; - upstreamRes.on('data', (chunk: Buffer) => body += chunk.toString()); + upstreamRes.on('data', (chunk: Buffer) => (body += chunk.toString())); upstreamRes.on('end', () => { reject(new Error(`Upstream error: ${upstreamRes.statusCode}\n${body}`)); }); @@ -441,12 +452,12 @@ export class GlmtProxy { try { const events = parser.parse(chunk); - events.forEach(event => { + events.forEach((event) => { // Transform OpenAI delta → Anthropic events const anthropicEvents = this.transformer.transformDelta(event, accumulator); // Forward to Claude CLI with immediate flush - anthropicEvents.forEach(evt => { + anthropicEvents.forEach((evt) => { const eventLine = `event: ${evt.event}\n`; const dataLine = `data: ${JSON.stringify(evt.data)}\n\n`; clientRes.write(eventLine + dataLine); @@ -521,7 +532,7 @@ if (require.main === module) { const proxy = new GlmtProxy({ verbose }); - proxy.start().catch(error => { + proxy.start().catch((error) => { console.error('[glmt-proxy] Failed to start:', error); process.exit(1); }); diff --git a/src/glmt/glmt-transformer.ts b/src/glmt/glmt-transformer.ts index a5154d0e..70ff1825 100644 --- a/src/glmt/glmt-transformer.ts +++ b/src/glmt/glmt-transformer.ts @@ -203,7 +203,7 @@ export class GlmtTransformer { private verbose: boolean; private debugLog: boolean; private debugMode: boolean; - debugLogDir: string; // public for external access + debugLogDir: string; // public for external access private modelMaxTokens: Record; private localeEnforcer: LocaleEnforcer; private reasoningEnforcer: ReasoningEnforcer; @@ -221,7 +221,7 @@ export class GlmtTransformer { this.modelMaxTokens = { 'GLM-4.6': 128000, 'GLM-4.5': 96000, - 'GLM-4.5-air': 16000 + 'GLM-4.5-air': 16000, }; // Initialize locale enforcer (always enforce English) @@ -229,7 +229,7 @@ export class GlmtTransformer { // Initialize reasoning enforcer (enabled by default for all GLMT usage) this.reasoningEnforcer = new ReasoningEnforcer({ - enabled: config.explicitReasoning ?? true + enabled: config.explicitReasoning ?? true, }); } @@ -242,9 +242,7 @@ export class GlmtTransformer { try { // 1. Extract thinking control from messages (tags like ) - const thinkingConfig = this.extractThinkingControl( - anthropicRequest.messages || [] - ); + const thinkingConfig = this.extractThinkingControl(anthropicRequest.messages || []); const hasControlTags = this.hasThinkingTags(anthropicRequest.messages || []); // 2. Detect "think" keywords in user prompts (Anthropic-style) @@ -252,7 +250,9 @@ export class GlmtTransformer { if (keywordConfig && !anthropicRequest.thinking && !hasControlTags) { thinkingConfig.thinking = keywordConfig.thinking; thinkingConfig.effort = keywordConfig.effort; - this.log(`Detected think keyword: ${keywordConfig.keyword}, effort=${keywordConfig.effort}`); + this.log( + `Detected think keyword: ${keywordConfig.keyword}, effort=${keywordConfig.effort}` + ); } // 3. Check anthropicRequest.thinking parameter (takes precedence) @@ -275,12 +275,16 @@ export class GlmtTransformer { // 4. Inject locale instruction before sanitization const messagesWithLocale = this.localeEnforcer.injectInstruction( - (anthropicRequest.messages || []) as Parameters[0] + (anthropicRequest.messages || []) as Parameters< + typeof this.localeEnforcer.injectInstruction + >[0] ) as unknown as Message[]; // 4.5. Inject reasoning instruction (if enabled or thinking requested) const messagesWithReasoning = this.reasoningEnforcer.injectInstruction( - messagesWithLocale as unknown as Parameters[0], + messagesWithLocale as unknown as Parameters< + typeof this.reasoningEnforcer.injectInstruction + >[0], thinkingConfig ) as unknown as Message[]; @@ -289,14 +293,14 @@ export class GlmtTransformer { model: glmModel, messages: this.sanitizeMessages(messagesWithReasoning), max_tokens: this.getMaxTokens(glmModel), - stream: anthropicRequest.stream ?? false + stream: anthropicRequest.stream ?? false, }; // 5.5. Transform tools parameter if present if (anthropicRequest.tools && anthropicRequest.tools.length > 0) { openaiRequest.tools = this.transformTools(anthropicRequest.tools); // Always use "auto" as Z.AI doesn't support other modes - openaiRequest.tool_choice = "auto"; + openaiRequest.tool_choice = 'auto'; this.log(`Transformed ${anthropicRequest.tools.length} tools for OpenAI format`); } @@ -327,7 +331,7 @@ export class GlmtTransformer { return { openaiRequest: anthropicRequest, thinkingConfig: { thinking: false, effort: 'medium' }, - error: err.message + error: err.message, }; } } @@ -335,7 +339,10 @@ export class GlmtTransformer { /** * Transform OpenAI response to Anthropic format */ - transformResponse(openaiResponse: OpenAIResponse, _thinkingConfig: ThinkingConfig = { thinking: false, effort: 'medium' }): AnthropicResponse { + transformResponse( + openaiResponse: OpenAIResponse, + _thinkingConfig: ThinkingConfig = { thinking: false, effort: 'medium' } + ): AnthropicResponse { // Log original response this.writeDebugLog('response-openai', openaiResponse); @@ -352,10 +359,7 @@ export class GlmtTransformer { if (message.reasoning_content) { const length = message.reasoning_content.length; const lineCount = message.reasoning_content.split('\n').length; - const preview = message.reasoning_content - .substring(0, 100) - .replace(/\n/g, ' ') - .trim(); + const preview = message.reasoning_content.substring(0, 100).replace(/\n/g, ' ').trim(); this.log(`Detected reasoning_content:`); this.log(` Length: ${length} characters`); @@ -365,7 +369,7 @@ export class GlmtTransformer { content.push({ type: 'thinking', thinking: message.reasoning_content, - signature: this.generateThinkingSignature(message.reasoning_content) + signature: this.generateThinkingSignature(message.reasoning_content), }); } else { this.log('No reasoning_content in OpenAI response'); @@ -376,13 +380,13 @@ export class GlmtTransformer { if (message.content) { content.push({ type: 'text', - text: message.content + text: message.content, }); } // Handle tool_calls if present if (message.tool_calls && message.tool_calls.length > 0) { - message.tool_calls.forEach(toolCall => { + message.tool_calls.forEach((toolCall) => { let parsedInput: Record; try { parsedInput = JSON.parse(toolCall.function.arguments || '{}'); @@ -396,7 +400,7 @@ export class GlmtTransformer { type: 'tool_use', id: toolCall.id, name: toolCall.function.name, - input: parsedInput + input: parsedInput, }); }); } @@ -410,14 +414,16 @@ export class GlmtTransformer { stop_reason: this.mapStopReason(choice.finish_reason || 'stop'), usage: { input_tokens: openaiResponse.usage?.prompt_tokens || 0, - output_tokens: openaiResponse.usage?.completion_tokens || 0 - } + output_tokens: openaiResponse.usage?.completion_tokens || 0, + }, }; // Validate transformation in verbose mode if (this.verbose) { const validation = this.validateTransformation(anthropicResponse); - this.log(`Transformation validation: ${validation.passed}/${validation.total} checks passed`); + this.log( + `Transformation validation: ${validation.passed}/${validation.total} checks passed` + ); if (!validation.valid) { this.log(`Failed checks: ${JSON.stringify(validation.checks, null, 2)}`); } @@ -435,13 +441,15 @@ export class GlmtTransformer { id: 'msg_error_' + Date.now(), type: 'message', role: 'assistant', - content: [{ - type: 'text', - text: '[Transformation Error] ' + err.message - }], + content: [ + { + type: 'text', + text: '[Transformation Error] ' + err.message, + }, + ], model: 'glm-4.6', stop_reason: 'end_turn', - usage: { input_tokens: 0, output_tokens: 0 } + usage: { input_tokens: 0, output_tokens: 0 }, }; } } @@ -462,29 +470,31 @@ export class GlmtTransformer { // If content is an array, process blocks if (Array.isArray(msg.content)) { // Separate tool_result blocks from other content - const toolResults = msg.content.filter(block => block.type === 'tool_result'); - const textBlocks = msg.content.filter(block => block.type === 'text'); + const toolResults = msg.content.filter((block) => block.type === 'tool_result'); + const textBlocks = msg.content.filter((block) => block.type === 'text'); // const toolUseBlocks = msg.content.filter(block => block.type === 'tool_use'); // CRITICAL: Tool messages must come BEFORE user text in OpenAI API for (const toolResult of toolResults) { result.push({ role: 'tool', - content: typeof toolResult.content === 'string' - ? toolResult.content - : JSON.stringify(toolResult.content) + content: + typeof toolResult.content === 'string' + ? toolResult.content + : JSON.stringify(toolResult.content), } as Message & { tool_call_id: string }); } // Add text content as user/assistant message AFTER tool messages if (textBlocks.length > 0) { - const textContent = textBlocks.length === 1 - ? textBlocks[0].text || '' - : textBlocks.map(b => b.text || '').join('\n'); + const textContent = + textBlocks.length === 1 + ? textBlocks[0].text || '' + : textBlocks.map((b) => b.text || '').join('\n'); result.push({ role: msg.role, - content: textContent + content: textContent, }); } @@ -492,7 +502,7 @@ export class GlmtTransformer { if (textBlocks.length === 0 && toolResults.length === 0) { result.push({ role: msg.role, - content: '' + content: '', }); } @@ -510,13 +520,13 @@ export class GlmtTransformer { * Transform Anthropic tools to OpenAI tools format */ private transformTools(anthropicTools: AnthropicTool[]): OpenAITool[] { - return anthropicTools.map(tool => ({ + return anthropicTools.map((tool) => ({ type: 'function' as const, function: { name: tool.name, description: tool.description, - parameters: tool.input_schema || {} - } + parameters: tool.input_schema || {}, + }, })); } @@ -543,7 +553,7 @@ export class GlmtTransformer { private extractThinkingControl(messages: Message[]): ThinkingConfig { const config: ThinkingConfig = { thinking: this.defaultThinking, - effort: 'medium' + effort: 'medium', }; // Scan user messages for control tags @@ -574,34 +584,33 @@ export class GlmtTransformer { */ private generateThinkingSignature(thinking: string): ThinkingSignature { // Generate signature hash - const hash = crypto.createHash('sha256') - .update(thinking) - .digest('hex') - .substring(0, 16); + const hash = crypto.createHash('sha256').update(thinking).digest('hex').substring(0, 16); return { type: 'thinking_signature', hash: hash, length: thinking.length, - timestamp: Date.now() + timestamp: Date.now(), }; } /** * Detect Anthropic-style "think" keywords in user prompts */ - private detectThinkKeywords(messages: Message[]): { thinking: boolean; effort: string; keyword: string } | null { + private detectThinkKeywords( + messages: Message[] + ): { thinking: boolean; effort: string; keyword: string } | null { if (!messages || messages.length === 0) return null; // Extract text from user messages const text = messages - .filter(m => m.role === 'user') - .map(m => { + .filter((m) => m.role === 'user') + .map((m) => { if (typeof m.content === 'string') return m.content; if (Array.isArray(m.content)) { return m.content - .filter(block => block.type === 'text') - .map(block => block.text || '') + .filter((block) => block.type === 'text') + .map((block) => block.text || '') .join(' '); } return ''; @@ -628,7 +637,10 @@ export class GlmtTransformer { /** * Inject reasoning parameters into OpenAI request */ - private injectReasoningParams(openaiRequest: OpenAIRequest, thinkingConfig: ThinkingConfig): void { + private injectReasoningParams( + openaiRequest: OpenAIRequest, + thinkingConfig: ThinkingConfig + ): void { // Always enable sampling for temperature/top_p to work openaiRequest.do_sample = true; @@ -659,10 +671,10 @@ export class GlmtTransformer { */ private mapStopReason(openaiReason: string): string { const mapping: Record = { - 'stop': 'end_turn', - 'length': 'max_tokens', - 'tool_calls': 'tool_use', - 'content_filter': 'stop_sequence' + stop: 'end_turn', + length: 'max_tokens', + tool_calls: 'tool_use', + content_filter: 'stop_sequence', }; return mapping[openaiReason] || 'end_turn'; } @@ -699,10 +711,11 @@ export class GlmtTransformer { private validateTransformation(anthropicResponse: AnthropicResponse): ValidationResult { const checks: Record = { hasContent: Boolean(anthropicResponse.content && anthropicResponse.content.length > 0), - hasThinking: anthropicResponse.content?.some(block => block.type === 'thinking') || false, - hasText: anthropicResponse.content?.some(block => block.type === 'text') || false, - validStructure: anthropicResponse.type === 'message' && anthropicResponse.role === 'assistant', - hasUsage: Boolean(anthropicResponse.usage) + hasThinking: anthropicResponse.content?.some((block) => block.type === 'thinking') || false, + hasText: anthropicResponse.content?.some((block) => block.type === 'text') || false, + validStructure: + anthropicResponse.type === 'message' && anthropicResponse.role === 'assistant', + hasUsage: Boolean(anthropicResponse.usage), }; const passed = Object.values(checks).filter(Boolean).length; @@ -767,7 +780,9 @@ export class GlmtTransformer { if (this.debugMode) { console.error(`[GLMT-DEBUG] Reasoning delta: ${delta.reasoning_content.length} chars`); - console.error(`[GLMT-DEBUG] Current block: ${currentBlock?.type || 'none'}, index: ${currentBlock?.index ?? 'N/A'}`); + console.error( + `[GLMT-DEBUG] Current block: ${currentBlock?.type || 'none'}, index: ${currentBlock?.index ?? 'N/A'}` + ); } if (!currentBlock || currentBlock.type !== 'thinking') { @@ -781,10 +796,9 @@ export class GlmtTransformer { } accumulator.addDelta(delta.reasoning_content); - events.push(this.createThinkingDeltaEvent( - accumulator.getCurrentBlock()!, - delta.reasoning_content - )); + events.push( + this.createThinkingDeltaEvent(accumulator.getCurrentBlock()!, delta.reasoning_content) + ); } // Text content delta @@ -808,15 +822,14 @@ export class GlmtTransformer { } accumulator.addDelta(delta.content); - events.push(this.createTextDeltaEvent( - accumulator.getCurrentBlock()!, - delta.content - )); + events.push(this.createTextDeltaEvent(accumulator.getCurrentBlock()!, delta.content)); } // Check for planning loop if (accumulator.checkForLoop()) { - this.log('WARNING: Planning loop detected - 3 consecutive thinking blocks with no tool calls'); + this.log( + 'WARNING: Planning loop detected - 3 consecutive thinking blocks with no tool calls' + ); this.log('Forcing early finalization to prevent unbounded planning'); // Close current block if any @@ -872,9 +885,9 @@ export class GlmtTransformer { content_block: { type: 'tool_use', id: toolCall?.id || `tool_${toolCallDelta.index}`, - name: toolCall?.function?.name || '' - } - } + name: toolCall?.function?.name || '', + }, + }, }); } @@ -889,9 +902,9 @@ export class GlmtTransformer { index: currentToolBlock.index, delta: { type: 'input_json_delta', - partial_json: toolCallDelta.function.arguments - } - } + partial_json: toolCallDelta.function.arguments, + }, + }, }); } } @@ -910,7 +923,10 @@ export class GlmtTransformer { // Debug logging for generated events if (this.debugLog && events.length > 0) { - this.writeDebugLog('delta-anthropic-events', { events, accumulator: accumulator.getSummary() }); + this.writeDebugLog('delta-anthropic-events', { + events, + accumulator: accumulator.getSummary(), + }); } return events; @@ -945,21 +961,21 @@ export class GlmtTransformer { data: { type: 'message_delta', delta: { - stop_reason: this.mapStopReason(accumulator.getFinishReason() || 'stop') + stop_reason: this.mapStopReason(accumulator.getFinishReason() || 'stop'), }, usage: { input_tokens: accumulator.getInputTokens(), - output_tokens: accumulator.getOutputTokens() - } - } + output_tokens: accumulator.getOutputTokens(), + }, + }, }); // Message stop events.push({ event: 'message_stop', data: { - type: 'message_stop' - } + type: 'message_stop', + }, }); accumulator.setFinalized(true); @@ -983,10 +999,10 @@ export class GlmtTransformer { stop_reason: null, usage: { input_tokens: accumulator.getInputTokens(), - output_tokens: 0 - } - } - } + output_tokens: 0, + }, + }, + }, }; } @@ -1001,9 +1017,9 @@ export class GlmtTransformer { index: block.index, content_block: { type: block.type, - [block.type]: '' - } - } + [block.type]: '', + }, + }, }; } @@ -1018,9 +1034,9 @@ export class GlmtTransformer { index: block.index, delta: { type: 'thinking_delta', - thinking: delta - } - } + thinking: delta, + }, + }, }; } @@ -1035,9 +1051,9 @@ export class GlmtTransformer { index: block.index, delta: { type: 'text_delta', - text: delta - } - } + text: delta, + }, + }, }; } @@ -1049,7 +1065,9 @@ export class GlmtTransformer { if (!block.content || block.content.length === 0) { if (this.verbose) { this.log(`WARNING: Skipping signature for empty thinking block ${block.index}`); - this.log(`This indicates a race condition - signature requested before content accumulated`); + this.log( + `This indicates a race condition - signature requested before content accumulated` + ); } return null; } @@ -1067,9 +1085,9 @@ export class GlmtTransformer { index: block.index, delta: { type: 'thinking_signature_delta', - signature: signature - } - } + signature: signature, + }, + }, }; } @@ -1081,8 +1099,8 @@ export class GlmtTransformer { event: 'content_block_stop', data: { type: 'content_block_stop', - index: block.index - } + index: block.index, + }, }; } diff --git a/src/glmt/locale-enforcer.ts b/src/glmt/locale-enforcer.ts index d837b9fb..4f6935c5 100644 --- a/src/glmt/locale-enforcer.ts +++ b/src/glmt/locale-enforcer.ts @@ -29,7 +29,9 @@ export class LocaleEnforcer { private instruction: string; constructor(options: LocaleEnforcerOptions = {}) { - this.instruction = options.instruction || "CRITICAL: You MUST respond in English only, regardless of the input language or context. This is a strict requirement."; + this.instruction = + options.instruction || + 'CRITICAL: You MUST respond in English only, regardless of the input language or context. This is a strict requirement.'; } /** @@ -42,7 +44,7 @@ export class LocaleEnforcer { const modifiedMessages = JSON.parse(JSON.stringify(messages)) as Message[]; // Strategy 1: Inject into system prompt (preferred) - const systemIndex = modifiedMessages.findIndex(m => m.role === 'system'); + const systemIndex = modifiedMessages.findIndex((m) => m.role === 'system'); if (systemIndex >= 0) { const systemMsg = modifiedMessages[systemIndex]; @@ -51,7 +53,7 @@ export class LocaleEnforcer { } else if (Array.isArray(systemMsg.content)) { systemMsg.content.unshift({ type: 'text', - text: this.instruction + text: this.instruction, }); } @@ -59,7 +61,7 @@ export class LocaleEnforcer { } // Strategy 2: Prepend to first user message - const userIndex = modifiedMessages.findIndex(m => m.role === 'user'); + const userIndex = modifiedMessages.findIndex((m) => m.role === 'user'); if (userIndex >= 0) { const userMsg = modifiedMessages[userIndex]; @@ -68,7 +70,7 @@ export class LocaleEnforcer { } else if (Array.isArray(userMsg.content)) { userMsg.content.unshift({ type: 'text', - text: this.instruction + text: this.instruction, }); } @@ -78,4 +80,4 @@ export class LocaleEnforcer { // No system or user messages found (edge case) return modifiedMessages; } -} \ No newline at end of file +} diff --git a/src/glmt/reasoning-enforcer.ts b/src/glmt/reasoning-enforcer.ts index d77282ad..a86c4fa3 100644 --- a/src/glmt/reasoning-enforcer.ts +++ b/src/glmt/reasoning-enforcer.ts @@ -63,7 +63,7 @@ export class ReasoningEnforcer { const prompt = this.selectPrompt(effort); // Strategy 1: Inject into system prompt (preferred) - const systemIndex = modifiedMessages.findIndex(m => m.role === 'system'); + const systemIndex = modifiedMessages.findIndex((m) => m.role === 'system'); if (systemIndex >= 0) { const systemMsg = modifiedMessages[systemIndex]; @@ -72,7 +72,7 @@ export class ReasoningEnforcer { } else if (Array.isArray(systemMsg.content)) { systemMsg.content.unshift({ type: 'text', - text: prompt + text: prompt, }); } @@ -80,7 +80,7 @@ export class ReasoningEnforcer { } // Strategy 2: Prepend to first user message - const userIndex = modifiedMessages.findIndex(m => m.role === 'user'); + const userIndex = modifiedMessages.findIndex((m) => m.role === 'user'); if (userIndex >= 0) { const userMsg = modifiedMessages[userIndex]; @@ -89,7 +89,7 @@ export class ReasoningEnforcer { } else if (Array.isArray(userMsg.content)) { userMsg.content.unshift({ type: 'text', - text: prompt + text: prompt, }); } @@ -179,7 +179,7 @@ OUTPUT FORMAT: - Final conclusion with rigorous justification) -(Write your final answer here based on your exhaustive reasoning above)` +(Write your final answer here based on your exhaustive reasoning above)`, }; } } diff --git a/src/glmt/sse-parser.ts b/src/glmt/sse-parser.ts index c066d10b..970fad90 100644 --- a/src/glmt/sse-parser.ts +++ b/src/glmt/sse-parser.ts @@ -72,7 +72,7 @@ export class SSEParser { events.push({ event: 'done', data: null, - index: this.eventCount + index: this.eventCount, }); currentEvent = { event: 'message', data: '' }; } else { @@ -85,7 +85,12 @@ export class SSEParser { } catch (e) { // H-01 Fix: Log parse errors for debugging if (typeof console !== 'undefined' && console.error) { - console.error('[SSEParser] Malformed JSON event:', (e as Error).message, 'Data:', data.substring(0, 100)); + console.error( + '[SSEParser] Malformed JSON event:', + (e as Error).message, + 'Data:', + data.substring(0, 100) + ); } } } @@ -107,4 +112,4 @@ export class SSEParser { this.buffer = ''; this.eventCount = 0; } -} \ No newline at end of file +} diff --git a/src/management/doctor.ts b/src/management/doctor.ts index 094b8652..1b61feff 100644 --- a/src/management/doctor.ts +++ b/src/management/doctor.ts @@ -28,15 +28,15 @@ try { ora = oraModule.default || oraModule; } catch (e) { // ora not available, create fallback spinner that uses console.log - ora = function(text: string): Spinner { + ora = function (text: string): Spinner { return { start: () => ({ succeed: (msg?: string) => console.log(msg || `[OK] ${text}`), fail: (msg?: string) => console.log(msg || `[X] ${text}`), warn: (msg?: string) => console.log(msg || `[!] ${text}`), info: (msg?: string) => console.log(msg || `[i] ${text}`), - text: '' - }) + text: '', + }), }; }; } @@ -71,7 +71,13 @@ class HealthCheck { public errors: HealthIssue[] = []; public details: Record = {}; - addCheck(name: string, status: 'success' | 'error' | 'warning', message = '', fix: string | undefined = undefined, details: HealthCheckDetails | undefined = undefined): void { + addCheck( + name: string, + status: 'success' | 'error' | 'warning', + message = '', + fix: string | undefined = undefined, + details: HealthCheckDetails | undefined = undefined + ): void { this.checks.push({ name, status, message, fix }); if (status === 'error') this.errors.push({ name, message, fix }); @@ -179,12 +185,12 @@ class Doctor { const result = await new Promise((resolve, reject) => { const child = spawn(claudeCli, ['--version'], { stdio: 'pipe', - timeout: 5000 + timeout: 5000, }); let output = ''; - child.stdout?.on('data', (data: Buffer) => output += data); - child.stderr?.on('data', (data: Buffer) => output += data); + child.stdout?.on('data', (data: Buffer) => (output += data)); + child.stderr?.on('data', (data: Buffer) => (output += data)); child.on('close', (code: number | null) => { if (code === 0) resolve(output); @@ -198,13 +204,17 @@ class Doctor { const versionMatch = result.match(/(\d+\.\d+\.\d+)/); const version = versionMatch ? versionMatch[1] : 'unknown'; - spinner.succeed(` ${'Claude CLI'.padEnd(26)}${colored('[OK]', 'green')} ${claudeCli} (v${version})`); + spinner.succeed( + ` ${'Claude CLI'.padEnd(26)}${colored('[OK]', 'green')} ${claudeCli} (v${version})` + ); this.results.addCheck('Claude CLI', 'success', `Found: ${claudeCli}`, undefined, { status: 'OK', - info: `v${version} (${claudeCli})` + info: `v${version} (${claudeCli})`, }); } catch (err) { - spinner.fail(` ${'Claude CLI'.padEnd(26)}${colored('[X]', 'red')} Not found or not working`); + spinner.fail( + ` ${'Claude CLI'.padEnd(26)}${colored('[X]', 'red')} Not found or not working` + ); this.results.addCheck( 'Claude CLI', 'error', @@ -225,7 +235,7 @@ class Doctor { spinner.succeed(` ${'CCS Directory'.padEnd(26)}${colored('[OK]', 'green')} ~/.ccs/`); this.results.addCheck('CCS Directory', 'success', undefined, undefined, { status: 'OK', - info: '~/.ccs/' + info: '~/.ccs/', }); } else { spinner.fail(` ${'CCS Directory'.padEnd(26)}${colored('[X]', 'red')} Not found`); @@ -245,8 +255,18 @@ class Doctor { private checkConfigFiles(): void { const files = [ { path: path.join(this.ccsDir, 'config.json'), name: 'config.json', key: 'config.json' }, - { path: path.join(this.ccsDir, 'glm.settings.json'), name: 'glm.settings.json', key: 'GLM Settings', profile: 'glm' }, - { path: path.join(this.ccsDir, 'kimi.settings.json'), name: 'kimi.settings.json', key: 'Kimi Settings', profile: 'kimi' } + { + path: path.join(this.ccsDir, 'glm.settings.json'), + name: 'glm.settings.json', + key: 'GLM Settings', + profile: 'glm', + }, + { + path: path.join(this.ccsDir, 'kimi.settings.json'), + name: 'kimi.settings.json', + key: 'Kimi Settings', + profile: 'kimi', + }, ]; const { DelegationValidator } = require('../utils/delegation-validator'); @@ -299,10 +319,16 @@ class Doctor { spinner.succeed(` ${file.name.padEnd(26)}${statusIcon} ${info}`); } - this.results.addCheck(file.name, status === 'OK' ? 'success' : 'warning', undefined, undefined, { - status: status, - info: info - }); + this.results.addCheck( + file.name, + status === 'OK' ? 'success' : 'warning', + undefined, + undefined, + { + status: status, + info: info, + } + ); } catch (e) { spinner.fail(` ${file.name.padEnd(26)}${colored('[X]', 'red')} Invalid JSON`); this.results.addCheck( @@ -324,7 +350,9 @@ class Doctor { const settingsPath = path.join(this.claudeDir, 'settings.json'); if (!fs.existsSync(settingsPath)) { - spinner.warn(` ${'~/.claude/settings.json'.padEnd(26)}${colored('[!]', 'yellow')} Not found`); + spinner.warn( + ` ${'~/.claude/settings.json'.padEnd(26)}${colored('[!]', 'yellow')} Not found` + ); this.results.addCheck( 'Claude Settings', 'warning', @@ -341,7 +369,9 @@ class Doctor { spinner.succeed(` ${'~/.claude/settings.json'.padEnd(26)}${colored('[OK]', 'green')}`); this.results.addCheck('Claude Settings', 'success'); } catch (e) { - spinner.warn(` ${'~/.claude/settings.json'.padEnd(26)}${colored('[!]', 'yellow')} Invalid JSON`); + spinner.warn( + ` ${'~/.claude/settings.json'.padEnd(26)}${colored('[!]', 'yellow')} Invalid JSON` + ); this.results.addCheck( 'Claude Settings', 'warning', @@ -381,16 +411,24 @@ class Doctor { const profileCount = Object.keys(config.profiles).length; const profileNames = Object.keys(config.profiles).join(', '); - spinner.succeed(` ${'Profiles'.padEnd(26)}${colored('[OK]', 'green')} ${profileCount} configured (${profileNames})`); - this.results.addCheck('Profiles', 'success', `${profileCount} profiles configured`, undefined, { - status: 'OK', - info: `${profileCount} configured (${profileNames.length > 30 ? profileNames.substring(0, 27) + '...' : profileNames})` - }); + spinner.succeed( + ` ${'Profiles'.padEnd(26)}${colored('[OK]', 'green')} ${profileCount} configured (${profileNames})` + ); + this.results.addCheck( + 'Profiles', + 'success', + `${profileCount} profiles configured`, + undefined, + { + status: 'OK', + info: `${profileCount} configured (${profileNames.length > 30 ? profileNames.substring(0, 27) + '...' : profileNames})`, + } + ); } catch (e) { spinner.fail(` ${'Profiles'.padEnd(26)}${colored('[X]', 'red')} ${(e as Error).message}`); this.results.addCheck('Profiles', 'error', (e as Error).message, undefined, { status: 'ERROR', - info: (e as Error).message + info: (e as Error).message, }); } } @@ -408,7 +446,7 @@ class Doctor { return; } - const instances = fs.readdirSync(instancesDir).filter(name => { + const instances = fs.readdirSync(instancesDir).filter((name) => { return fs.statSync(path.join(instancesDir, name)).isDirectory(); }); @@ -418,7 +456,9 @@ class Doctor { return; } - spinner.succeed(` ${'Instances'.padEnd(26)}${colored('[OK]', 'green')} ${instances.length} account profiles`); + spinner.succeed( + ` ${'Instances'.padEnd(26)}${colored('[OK]', 'green')} ${instances.length} account profiles` + ); this.results.addCheck('Instances', 'success', `${instances.length} account profiles`); } @@ -468,7 +508,9 @@ class Doctor { return; } - spinner.succeed(` ${'Delegation'.padEnd(26)}${colored('[OK]', 'green')} ${readyProfiles.length} profiles ready (${readyProfiles.join(', ')})`); + spinner.succeed( + ` ${'Delegation'.padEnd(26)}${colored('[OK]', 'green')} ${readyProfiles.length} profiles ready (${readyProfiles.join(', ')})` + ); this.results.addCheck( 'Delegation', 'success', @@ -488,13 +530,17 @@ class Doctor { try { fs.writeFileSync(testFile, 'test', 'utf8'); fs.unlinkSync(testFile); - spinner.succeed(` ${'Permissions'.padEnd(26)}${colored('[OK]', 'green')} Write access verified`); + spinner.succeed( + ` ${'Permissions'.padEnd(26)}${colored('[OK]', 'green')} Write access verified` + ); this.results.addCheck('Permissions', 'success', undefined, undefined, { status: 'OK', - info: 'Write access verified' + info: 'Write access verified', }); } catch (e) { - spinner.fail(` ${'Permissions'.padEnd(26)}${colored('[X]', 'red')} Cannot write to ~/.ccs/`); + spinner.fail( + ` ${'Permissions'.padEnd(26)}${colored('[X]', 'red')} Cannot write to ~/.ccs/` + ); this.results.addCheck( 'Permissions', 'error', @@ -518,13 +564,23 @@ class Doctor { if (health.healthy) { const itemCount = manager.ccsItems.length; - spinner.succeed(` ${'CCS Symlinks'.padEnd(26)}${colored('[OK]', 'green')} ${itemCount}/${itemCount} items linked`); - this.results.addCheck('CCS Symlinks', 'success', 'All CCS items properly symlinked', undefined, { - status: 'OK', - info: `${itemCount}/${itemCount} items synced` - }); + spinner.succeed( + ` ${'CCS Symlinks'.padEnd(26)}${colored('[OK]', 'green')} ${itemCount}/${itemCount} items linked` + ); + this.results.addCheck( + 'CCS Symlinks', + 'success', + 'All CCS items properly symlinked', + undefined, + { + status: 'OK', + info: `${itemCount}/${itemCount} items synced`, + } + ); } else { - spinner.warn(` ${'CCS Symlinks'.padEnd(26)}${colored('[!]', 'yellow')} ${health.issues.length} issues found`); + spinner.warn( + ` ${'CCS Symlinks'.padEnd(26)}${colored('[!]', 'yellow')} ${health.issues.length} issues found` + ); this.results.addCheck( 'CCS Symlinks', 'warning', @@ -558,7 +614,9 @@ class Doctor { // Check shared settings exists and points to ~/.claude/ if (!fs.existsSync(sharedSettings)) { - spinner.warn(` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Not found`); + spinner.warn( + ` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Not found` + ); this.results.addCheck( 'Settings Symlinks', 'warning', @@ -570,7 +628,9 @@ class Doctor { const sharedStats = fs.lstatSync(sharedSettings); if (!sharedStats.isSymbolicLink()) { - spinner.warn(` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Not a symlink`); + spinner.warn( + ` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Not a symlink` + ); this.results.addCheck( 'Settings Symlinks', 'warning', @@ -584,7 +644,9 @@ class Doctor { const resolvedShared = path.resolve(path.dirname(sharedSettings), sharedTarget); if (resolvedShared !== claudeSettings) { - spinner.warn(` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Wrong target`); + spinner.warn( + ` ${'settings.json (shared)'.padEnd(26)}${colored('[!]', 'yellow')} Wrong target` + ); this.results.addCheck( 'Settings Symlinks', 'warning', @@ -597,15 +659,17 @@ class Doctor { // Check each instance const instancesDir = path.join(this.ccsDir, 'instances'); if (!fs.existsSync(instancesDir)) { - spinner.succeed(` ${'settings.json'.padEnd(26)}${colored('[OK]', 'green')} Shared symlink valid`); + spinner.succeed( + ` ${'settings.json'.padEnd(26)}${colored('[OK]', 'green')} Shared symlink valid` + ); this.results.addCheck('Settings Symlinks', 'success', 'Shared symlink valid', undefined, { status: 'OK', - info: 'Shared symlink valid' + info: 'Shared symlink valid', }); return; } - const instances = fs.readdirSync(instancesDir).filter(name => { + const instances = fs.readdirSync(instancesDir).filter((name) => { return fs.statSync(path.join(instancesDir, name)).isDirectory(); }); @@ -638,7 +702,9 @@ class Doctor { } if (broken > 0) { - spinner.warn(` ${'settings.json'.padEnd(26)}${colored('[!]', 'yellow')} ${broken} broken instance(s)`); + spinner.warn( + ` ${'settings.json'.padEnd(26)}${colored('[!]', 'yellow')} ${broken} broken instance(s)` + ); this.results.addCheck( 'Settings Symlinks', 'warning', @@ -647,13 +713,20 @@ class Doctor { { status: 'WARN', info: `${broken} broken instance(s)` } ); } else { - spinner.succeed(` ${'settings.json'.padEnd(26)}${colored('[OK]', 'green')} ${instances.length} instance(s) valid`); - this.results.addCheck('Settings Symlinks', 'success', 'All instance symlinks valid', undefined, { - status: 'OK', - info: `${instances.length} instance(s) valid` - }); + spinner.succeed( + ` ${'settings.json'.padEnd(26)}${colored('[OK]', 'green')} ${instances.length} instance(s) valid` + ); + this.results.addCheck( + 'Settings Symlinks', + 'success', + 'All instance symlinks valid', + undefined, + { + status: 'OK', + info: `${instances.length} instance(s) valid`, + } + ); } - } catch (err) { spinner.warn(` ${'settings.json'.padEnd(26)}${colored('[!]', 'yellow')} Check failed`); this.results.addCheck( @@ -687,21 +760,29 @@ class Doctor { colWidths: [20, 10, 35], wordWrap: true, chars: { - 'top': '═', 'top-mid': '╤', 'top-left': '╔', 'top-right': '╗', - 'bottom': '═', 'bottom-mid': '╧', 'bottom-left': '╚', 'bottom-right': '╝', - 'left': '║', 'left-mid': '╟', 'mid': '─', 'mid-mid': '┼', - 'right': '║', 'right-mid': '╢', 'middle': '│' - } + top: '═', + 'top-mid': '╤', + 'top-left': '╔', + 'top-right': '╗', + bottom: '═', + 'bottom-mid': '╧', + 'bottom-left': '╚', + 'bottom-right': '╝', + left: '║', + 'left-mid': '╟', + mid: '─', + 'mid-mid': '┼', + right: '║', + 'right-mid': '╢', + middle: '│', + }, }); // Populate table with collected details for (const [component, detail] of Object.entries(this.results.details)) { - const statusColor = detail.status === 'OK' ? 'green' : detail.status === 'ERROR' ? 'red' : 'yellow'; - table.push([ - component, - colored(detail.status, statusColor), - detail.info || '' - ]); + const statusColor = + detail.status === 'OK' ? 'green' : detail.status === 'ERROR' ? 'red' : 'yellow'; + table.push([component, colored(detail.status, statusColor), detail.info || '']); } console.log(table.toString()); @@ -710,7 +791,7 @@ class Doctor { // Show errors and warnings if present if (this.results.hasErrors()) { console.log(colored('Errors:', 'red')); - this.results.errors.forEach(err => { + this.results.errors.forEach((err) => { console.log(` [X] ${err.name}: ${err.message}`); if (err.fix) { console.log(` Fix: ${err.fix}`); @@ -721,7 +802,7 @@ class Doctor { if (this.results.hasWarnings()) { console.log(colored('Warnings:', 'yellow')); - this.results.warnings.forEach(warn => { + this.results.warnings.forEach((warn) => { console.log(` [!] ${warn.name}: ${warn.message}`); if (warn.fix) { console.log(` Fix: ${warn.fix}`); @@ -747,16 +828,20 @@ class Doctor { * Generate JSON report */ generateJsonReport(): string { - return JSON.stringify({ - timestamp: new Date().toISOString(), - platform: process.platform, - nodeVersion: process.version, - ccsVersion: packageJson.version, - checks: this.results.checks, - errors: this.results.errors, - warnings: this.results.warnings, - healthy: this.results.isHealthy() - }, null, 2); + return JSON.stringify( + { + timestamp: new Date().toISOString(), + platform: process.platform, + nodeVersion: process.version, + ccsVersion: packageJson.version, + checks: this.results.checks, + errors: this.results.errors, + warnings: this.results.warnings, + healthy: this.results.isHealthy(), + }, + null, + 2 + ); } /** @@ -767,4 +852,4 @@ class Doctor { } } -export default Doctor; \ No newline at end of file +export default Doctor; diff --git a/src/management/instance-manager.ts b/src/management/instance-manager.ts index 14ca238e..27ecd8d2 100644 --- a/src/management/instance-manager.ts +++ b/src/management/instance-manager.ts @@ -64,10 +64,10 @@ class InstanceManager { 'file-history', 'shell-snapshots', 'debug', - '.anthropic' + '.anthropic', ]; - subdirs.forEach(dir => { + subdirs.forEach((dir) => { const dirPath = path.join(instancePath, dir); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 }); @@ -80,7 +80,9 @@ class InstanceManager { // Copy global configs if exist (settings.json only) this.copyGlobalConfigs(instancePath); } catch (error) { - throw new Error(`Failed to initialize instance for ${profileName}: ${(error as Error).message}`); + throw new Error( + `Failed to initialize instance for ${profileName}: ${(error as Error).message}` + ); } } @@ -96,7 +98,7 @@ class InstanceManager { 'file-history', 'shell-snapshots', 'debug', - '.anthropic' + '.anthropic', ]; for (const dir of requiredDirs) { @@ -132,11 +134,10 @@ class InstanceManager { return []; } - return fs.readdirSync(this.instancesDir) - .filter(name => { - const instancePath = path.join(this.instancesDir, name); - return fs.statSync(instancePath).isDirectory(); - }); + return fs.readdirSync(this.instancesDir).filter((name) => { + const instancePath = path.join(this.instancesDir, name); + return fs.statSync(instancePath).isDirectory(); + }); } /** @@ -190,4 +191,4 @@ class InstanceManager { } export { InstanceManager }; -export default InstanceManager; \ No newline at end of file +export default InstanceManager; diff --git a/src/management/recovery-manager.ts b/src/management/recovery-manager.ts index 35c10493..58cb8abf 100644 --- a/src/management/recovery-manager.ts +++ b/src/management/recovery-manager.ts @@ -59,8 +59,8 @@ class RecoveryManager { profiles: { glm: '~/.ccs/glm.settings.json', kimi: '~/.ccs/kimi.settings.json', - default: '~/.claude/settings.json' - } + default: '~/.claude/settings.json', + }, }; const tmpPath = `${configPath}.tmp`; @@ -124,10 +124,10 @@ class RecoveryManager { console.log(''); console.log('[i] Auto-recovery completed:'); - this.recovered.forEach(msg => console.log(` - ${msg}`)); + this.recovered.forEach((msg) => console.log(` - ${msg}`)); // Show login hint if created Claude settings - if (this.recovered.some(msg => msg.includes('settings.json'))) { + if (this.recovered.some((msg) => msg.includes('settings.json'))) { console.log(''); console.log('[i] Next step: Login to Claude CLI'); console.log(' Run: claude /login'); @@ -137,4 +137,4 @@ class RecoveryManager { } } -export default RecoveryManager; \ No newline at end of file +export default RecoveryManager; diff --git a/src/management/shared-manager.ts b/src/management/shared-manager.ts index ecb1d435..0c4dec67 100644 --- a/src/management/shared-manager.ts +++ b/src/management/shared-manager.ts @@ -35,7 +35,7 @@ class SharedManager { { name: 'skills', type: 'directory' }, { name: 'agents', type: 'directory' }, { name: 'plugins', type: 'directory' }, - { name: 'settings.json', type: 'file' } + { name: 'settings.json', type: 'file' }, ]; } @@ -144,7 +144,9 @@ class SharedManager { } else if (item.type === 'file') { fs.copyFileSync(claudePath, sharedPath); } - console.log(`[!] Symlink failed for ${item.name}, copied instead (enable Developer Mode)`); + console.log( + `[!] Symlink failed for ${item.name}, copied instead (enable Developer Mode)` + ); } else { throw err; } @@ -183,7 +185,9 @@ class SharedManager { } else if (item.type === 'file') { fs.copyFileSync(targetPath, linkPath); } - console.log(`[!] Symlink failed for ${item.name}, copied instead (enable Developer Mode)`); + console.log( + `[!] Symlink failed for ${item.name}, copied instead (enable Developer Mode)` + ); } else { throw err; } @@ -319,7 +323,7 @@ class SharedManager { return; } - const instances = fs.readdirSync(this.instancesDir).filter(name => { + const instances = fs.readdirSync(this.instancesDir).filter((name) => { const instancePath = path.join(this.instancesDir, name); return fs.statSync(instancePath).isDirectory(); }); @@ -367,7 +371,6 @@ class SharedManager { throw err; } } - } catch (err) { console.log(`[!] Failed to migrate ${instance}: ${(err as Error).message}`); } @@ -404,4 +407,4 @@ class SharedManager { } } -export default SharedManager; \ No newline at end of file +export default SharedManager; diff --git a/src/types/cli.ts b/src/types/cli.ts index e1a737ae..baf1870b 100644 --- a/src/types/cli.ts +++ b/src/types/cli.ts @@ -8,10 +8,10 @@ import { SpawnOptions as NodeSpawnOptions } from 'child_process'; * Parsed CLI arguments */ export interface ParsedArgs { - profile?: string; // Profile name (glm, kimi, work, etc.) - prompt?: string; // -p/--prompt flag value - isDelegation: boolean; // -p flag present - isContinue: boolean; // :continue suffix detected + profile?: string; // Profile name (glm, kimi, work, etc.) + prompt?: string; // -p/--prompt flag value + isDelegation: boolean; // -p flag present + isContinue: boolean; // :continue suffix detected remainingArgs: string[]; // Args to pass to Claude CLI } @@ -50,4 +50,4 @@ export enum ExitCode { CONFIG_ERROR = 2, DELEGATION_ERROR = 3, TIMEOUT = 124, -} \ No newline at end of file +} diff --git a/src/types/config.ts b/src/types/config.ts index 6bf788f3..fe6a3282 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -55,10 +55,7 @@ export interface ProfilesRegistry { */ export function isConfig(obj: unknown): obj is Config { return ( - typeof obj === 'object' && - obj !== null && - 'profiles' in obj && - typeof obj.profiles === 'object' + typeof obj === 'object' && obj !== null && 'profiles' in obj && typeof obj.profiles === 'object' ); } @@ -67,5 +64,5 @@ export function isSettings(obj: unknown): obj is Settings { if (!('env' in obj)) return true; // env is optional if (typeof obj.env !== 'object' || obj.env === null) return false; // Validate all env values are strings - return Object.values(obj.env).every(v => typeof v === 'string'); -} \ No newline at end of file + return Object.values(obj.env).every((v) => typeof v === 'string'); +} diff --git a/src/types/delegation.ts b/src/types/delegation.ts index ed756d0e..25efaa79 100644 --- a/src/types/delegation.ts +++ b/src/types/delegation.ts @@ -6,14 +6,14 @@ * Session metadata for delegation tracking */ export interface SessionMetadata { - id: string; // Unique session ID - profile: string; // Target profile (glm, kimi) - prompt: string; // Initial prompt - workingDir: string; // CWD at execution time - startTime: number; // Unix timestamp (ms) - endTime?: number; // Unix timestamp (ms) - exitCode?: number; // Process exit code - duration?: number; // Execution duration (seconds) + id: string; // Unique session ID + profile: string; // Target profile (glm, kimi) + prompt: string; // Initial prompt + workingDir: string; // CWD at execution time + startTime: number; // Unix timestamp (ms) + endTime?: number; // Unix timestamp (ms) + exitCode?: number; // Process exit code + duration?: number; // Execution duration (seconds) } /** @@ -21,8 +21,8 @@ export interface SessionMetadata { */ export interface DelegationSession { metadata: SessionMetadata; - turns: number; // Conversation turns - lastPrompt?: string; // Last user prompt (for :continue) + turns: number; // Conversation turns + lastPrompt?: string; // Last user prompt (for :continue) } /** @@ -31,7 +31,7 @@ export interface DelegationSession { */ export interface DelegationSessionsRegistry { sessions: Record; // sessionId → session - lastSessionId?: string; // Most recent session ID + lastSessionId?: string; // Most recent session ID } /** @@ -39,12 +39,12 @@ export interface DelegationSessionsRegistry { */ export interface ExecutionResult { exitCode: number; - duration: number; // Seconds + duration: number; // Seconds workingDir: string; sessionId: string; profile: string; - model?: string; // Model name from settings - cost?: number; // Estimated cost (if available) + model?: string; // Model name from settings + cost?: number; // Estimated cost (if available) turns: number; } @@ -53,8 +53,8 @@ export interface ExecutionResult { */ export interface ToolEvent { type: 'tool'; - tool: string; // Tool name (Write, Edit, Bash, etc.) - args: string; // Simplified args (file path, command, etc.) + tool: string; // Tool name (Write, Edit, Bash, etc.) + args: string; // Simplified args (file path, command, etc.) timestamp: number; } @@ -64,4 +64,4 @@ export interface OutputEvent { timestamp: number; } -export type DelegationEvent = ToolEvent | OutputEvent; \ No newline at end of file +export type DelegationEvent = ToolEvent | OutputEvent; diff --git a/src/types/external.d.ts b/src/types/external.d.ts index b0cadeba..ba86374a 100644 --- a/src/types/external.d.ts +++ b/src/types/external.d.ts @@ -44,4 +44,4 @@ declare module 'ora' { function ora(options?: string | Options): Ora; export = ora; -} \ No newline at end of file +} diff --git a/src/types/glmt.ts b/src/types/glmt.ts index 57ff77a5..56747e85 100644 --- a/src/types/glmt.ts +++ b/src/types/glmt.ts @@ -102,4 +102,4 @@ export interface TransformationContext { verbose: boolean; debugLog: boolean; streaming: boolean; -} \ No newline at end of file +} diff --git a/src/types/index.ts b/src/types/index.ts index 63ccd09d..30fec139 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -16,12 +16,7 @@ export type { export { isConfig, isSettings } from './config'; // CLI types -export type { - ParsedArgs, - ClaudeSpawnOptions, - Platform, - ClaudeCliInfo, -} from './cli'; +export type { ParsedArgs, ClaudeSpawnOptions, Platform, ClaudeCliInfo } from './cli'; export { ExitCode } from './cli'; // Delegation types @@ -52,8 +47,4 @@ export type { // Utility types export { ErrorCode, LogLevel } from './utils'; -export type { - ColorName, - TerminalInfo, - Result, -} from './utils'; \ No newline at end of file +export type { ColorName, TerminalInfo, Result } from './utils'; diff --git a/src/types/utils.ts b/src/types/utils.ts index 119838d5..8fdf68b4 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -32,6 +32,4 @@ export interface TerminalInfo { /** * Helper result types */ -export type Result = - | { ok: true; value: T } - | { ok: false; error: E }; \ No newline at end of file +export type Result = { ok: true; value: T } | { ok: false; error: E }; diff --git a/src/utils/claude-detector.ts b/src/utils/claude-detector.ts index 2df8f640..a087b160 100644 --- a/src/utils/claude-detector.ts +++ b/src/utils/claude-detector.ts @@ -29,16 +29,19 @@ export function detectClaudeCli(): string | null { const result = execSync(cmd, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], - timeout: 5000 // 5 second timeout to prevent hangs + timeout: 5000, // 5 second timeout to prevent hangs }).trim(); // where.exe may return multiple lines (all matches in PATH order) - const matches = result.split('\n').map(p => p.trim()).filter(p => p); + const matches = result + .split('\n') + .map((p) => p.trim()) + .filter((p) => p); if (isWindows) { // On Windows, prefer executables with extensions (.exe, .cmd, .bat) // where.exe often returns file without extension first, then the actual .cmd wrapper - const withExtension = matches.find(p => /\.(exe|cmd|bat|ps1)$/i.test(p)); + const withExtension = matches.find((p) => /\.(exe|cmd|bat|ps1)$/i.test(p)); const claudePath = withExtension || matches[0]; if (claudePath && fs.existsSync(claudePath)) { @@ -77,7 +80,7 @@ export function getClaudeCliInfo(): ClaudeCliInfo | null { return { path: claudePath, isWindows, - needsShell + needsShell, }; } @@ -88,4 +91,4 @@ export function showClaudeNotFoundError(): never { console.error('ERROR: Claude CLI not found in PATH'); console.error('Install from: https://docs.claude.com/en/docs/claude-code/installation'); process.exit(1); -} \ No newline at end of file +} diff --git a/src/utils/claude-dir-installer.ts b/src/utils/claude-dir-installer.ts index 78e9d3d9..0d72ce91 100644 --- a/src/utils/claude-dir-installer.ts +++ b/src/utils/claude-dir-installer.ts @@ -29,15 +29,15 @@ try { ora = oraModule.default || oraModule; } catch { // ora not available, create fallback spinner that uses console.log - ora = function(text: string): OraInstance { + ora = function (text: string): OraInstance { return { start: () => ({ succeed: (msg?: string) => console.log(msg || `[OK] ${text}`), fail: (msg?: string) => console.log(msg || `[X] ${text}`), warn: (msg?: string) => console.log(msg || `[!] ${text}`), info: (msg?: string) => console.log(msg || `[i] ${text}`), - text: '' - }) + text: '', + }), }; }; } @@ -71,7 +71,8 @@ export class ClaudeDirInstaller { * @param silent - Suppress spinner output */ install(packageDir?: string, silent = false): boolean { - const spinner = (silent || !ora) ? null : ora('Copying .claude/ items to ~/.ccs/.claude/').start(); + const spinner = + silent || !ora ? null : ora('Copying .claude/ items to ~/.ccs/.claude/').start(); try { // Auto-detect package directory if not provided @@ -197,7 +198,12 @@ export class ClaudeDirInstaller { cleanupDeprecated(silent = false): CleanupResult { const deprecatedFile = path.join(this.ccsClaudeDir, 'agents', 'ccs-delegator.md'); const userSymlinkFile = path.join(this.homeDir, '.claude', 'agents', 'ccs-delegator.md'); - const migrationMarker = path.join(this.homeDir, '.ccs', '.migrations', 'v435-delegator-cleanup'); + const migrationMarker = path.join( + this.homeDir, + '.ccs', + '.migrations', + 'v435-delegator-cleanup' + ); const cleanedFiles: string[] = []; @@ -239,7 +245,8 @@ export class ClaudeDirInstaller { const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0]; const backupPath = `${deprecatedFile}.backup-${timestamp}`; fs.renameSync(deprecatedFile, backupPath); - if (!silent) console.log(`[i] Backed up modified deprecated file to ${path.basename(backupPath)}`); + if (!silent) + console.log(`[i] Backed up modified deprecated file to ${path.basename(backupPath)}`); } else { fs.rmSync(deprecatedFile, { force: true }); } diff --git a/src/utils/claude-symlink-manager.ts b/src/utils/claude-symlink-manager.ts index 72f6406c..08b9f2ee 100644 --- a/src/utils/claude-symlink-manager.ts +++ b/src/utils/claude-symlink-manager.ts @@ -38,15 +38,15 @@ try { ora = oraModule.default || oraModule; } catch { // ora not available, create fallback spinner that uses console.log - ora = function(text: string): OraInstance { + ora = function (text: string): OraInstance { return { start: () => ({ succeed: (msg?: string) => console.log(msg || `[OK] ${text}`), fail: (msg?: string) => console.log(msg || `[X] ${text}`), warn: (msg?: string) => console.log(msg || `[!] ${text}`), info: (msg?: string) => console.log(msg || `[i] ${text}`), - text: '' - }) + text: '', + }), }; }; } @@ -80,7 +80,7 @@ export class ClaudeSymlinkManager { this.ccsItems = [ { source: 'commands/ccs.md', target: 'commands/ccs.md', type: 'file' }, { source: 'commands/ccs', target: 'commands/ccs', type: 'directory' }, - { source: 'skills/ccs-delegation', target: 'skills/ccs-delegation', type: 'directory' } + { source: 'skills/ccs-delegation', target: 'skills/ccs-delegation', type: 'directory' }, ]; } @@ -89,7 +89,7 @@ export class ClaudeSymlinkManager { * Safe: backs up existing files before creating symlinks */ install(silent = false): void { - const spinner = (silent || !ora) ? null : ora('Installing CCS items to ~/.claude/').start(); + const spinner = silent || !ora ? null : ora('Installing CCS items to ~/.claude/').start(); // Ensure ~/.ccs/.claude/ exists (should be shipped with package) if (!fs.existsSync(this.ccsClaudeDir)) { diff --git a/src/utils/config-manager.ts b/src/utils/config-manager.ts index dff576ac..5049e587 100644 --- a/src/utils/config-manager.ts +++ b/src/utils/config-manager.ts @@ -92,7 +92,7 @@ export function getSettingsPath(profile: string): string { if (!settingsPath) { const availableProfiles = Object.keys(config.profiles); - const profileList = availableProfiles.map(p => ` - ${p}`); + const profileList = availableProfiles.map((p) => ` - ${p}`); error(`Profile '${profile}' not found. Available profiles:\n${profileList.join('\n')}`); } @@ -126,4 +126,4 @@ export function getSettingsPath(profile: string): string { } return expandedPath; -} \ No newline at end of file +} diff --git a/src/utils/delegation-validator.ts b/src/utils/delegation-validator.ts index aec7623a..f6f52cb9 100644 --- a/src/utils/delegation-validator.ts +++ b/src/utils/delegation-validator.ts @@ -31,11 +31,12 @@ export class DelegationValidator { return { valid: false, error: `Profile not found: ${profileName}`, - suggestion: `Profile settings missing at: ${settingsPath}\n\n` + - `To set up ${profileName} profile:\n` + - ` 1. Copy base settings: cp config/base-${profileName}.settings.json ~/.ccs/${profileName}.settings.json\n` + - ` 2. Edit settings: Edit ~/.ccs/${profileName}.settings.json\n` + - ` 3. Set your API key in ANTHROPIC_AUTH_TOKEN field` + suggestion: + `Profile settings missing at: ${settingsPath}\n\n` + + `To set up ${profileName} profile:\n` + + ` 1. Copy base settings: cp config/base-${profileName}.settings.json ~/.ccs/${profileName}.settings.json\n` + + ` 2. Edit settings: Edit ~/.ccs/${profileName}.settings.json\n` + + ` 3. Set your API key in ANTHROPIC_AUTH_TOKEN field`, }; } @@ -48,11 +49,12 @@ export class DelegationValidator { return { valid: false, error: `Failed to parse settings.json for ${profileName}`, - suggestion: `Settings file is corrupted or invalid JSON.\n\n` + - `Location: ${settingsPath}\n` + - `Parse error: ${(error as Error).message}\n\n` + - `Fix: Restore from base config:\n` + - ` cp config/base-${profileName}.settings.json ~/.ccs/${profileName}.settings.json` + suggestion: + `Settings file is corrupted or invalid JSON.\n\n` + + `Location: ${settingsPath}\n` + + `Parse error: ${(error as Error).message}\n\n` + + `Fix: Restore from base config:\n` + + ` cp config/base-${profileName}.settings.json ~/.ccs/${profileName}.settings.json`, }; } @@ -63,9 +65,10 @@ export class DelegationValidator { return { valid: false, error: `API key not configured for ${profileName}`, - suggestion: `Missing ANTHROPIC_AUTH_TOKEN in settings.\n\n` + - `Edit: ${settingsPath}\n` + - `Set: env.ANTHROPIC_AUTH_TOKEN to your API key` + suggestion: + `Missing ANTHROPIC_AUTH_TOKEN in settings.\n\n` + + `Edit: ${settingsPath}\n` + + `Set: env.ANTHROPIC_AUTH_TOKEN to your API key`, }; } @@ -75,20 +78,21 @@ export class DelegationValidator { 'YOUR_KIMI_API_KEY_HERE', 'YOUR_API_KEY_HERE', 'your-api-key-here', - 'PLACEHOLDER' + 'PLACEHOLDER', ]; - if (defaultPlaceholders.some(placeholder => apiKey.includes(placeholder))) { + if (defaultPlaceholders.some((placeholder) => apiKey.includes(placeholder))) { return { valid: false, error: `Default API key placeholder detected for ${profileName}`, - suggestion: `API key is still set to default placeholder.\n\n` + - `To configure your profile:\n` + - ` 1. Edit: ${settingsPath}\n` + - ` 2. Replace ANTHROPIC_AUTH_TOKEN with your actual API key\n\n` + - `Get API key:\n` + - ` GLM: https://z.ai/manage-apikey/apikey-list\n` + - ` Kimi: https://platform.moonshot.cn/console/api-keys` + suggestion: + `API key is still set to default placeholder.\n\n` + + `To configure your profile:\n` + + ` 1. Edit: ${settingsPath}\n` + + ` 2. Replace ANTHROPIC_AUTH_TOKEN with your actual API key\n\n` + + `Get API key:\n` + + ` GLM: https://z.ai/manage-apikey/apikey-list\n` + + ` Kimi: https://platform.moonshot.cn/console/api-keys`, }; } @@ -96,7 +100,7 @@ export class DelegationValidator { return { valid: true, settingsPath, - apiKey: apiKey.substring(0, 8) + '...' // Show first 8 chars for verification + apiKey: apiKey.substring(0, 8) + '...', // Show first 8 chars for verification }; } @@ -156,4 +160,4 @@ export class DelegationValidator { return profiles; } -} \ No newline at end of file +} diff --git a/src/utils/error-codes.ts b/src/utils/error-codes.ts index 83bb166e..6bfaf132 100644 --- a/src/utils/error-codes.ts +++ b/src/utils/error-codes.ts @@ -34,10 +34,10 @@ export const ERROR_CODES = { // Internal Errors (E900-E999) INTERNAL_ERROR: 'E900', - INVALID_STATE: 'E901' + INVALID_STATE: 'E901', } as const; -export type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES]; +export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES]; /** * Error code documentation URL generator @@ -58,4 +58,4 @@ export function getErrorCategory(errorCode: ErrorCode): string { if (code >= 500 && code < 600) return 'File System'; if (code >= 900 && code < 1000) return 'Internal'; return 'Unknown'; -} \ No newline at end of file +} diff --git a/src/utils/error-manager.ts b/src/utils/error-manager.ts index 2091aa26..d26572b3 100644 --- a/src/utils/error-manager.ts +++ b/src/utils/error-manager.ts @@ -10,10 +10,10 @@ export const ErrorTypes = { INVALID_CONFIG: 'INVALID_CONFIG', UNKNOWN_PROFILE: 'UNKNOWN_PROFILE', PERMISSION_DENIED: 'PERMISSION_DENIED', - GENERIC: 'GENERIC' + GENERIC: 'GENERIC', } as const; -export type ErrorType = typeof ErrorTypes[keyof typeof ErrorTypes]; +export type ErrorType = (typeof ErrorTypes)[keyof typeof ErrorTypes]; /** * Enhanced error manager with context-aware messages @@ -55,7 +55,8 @@ export class ErrorManager { * Show settings file not found error */ static showSettingsNotFound(settingsPath: string): void { - const isClaudeSettings = settingsPath.includes('.claude') && settingsPath.endsWith('settings.json'); + const isClaudeSettings = + settingsPath.includes('.claude') && settingsPath.endsWith('settings.json'); console.error(''); console.error(colored('[X] Settings file not found', 'red')); @@ -118,12 +119,12 @@ export class ErrorManager { if (suggestions && suggestions.length > 0) { console.error(colored('Did you mean:', 'yellow')); - suggestions.forEach(s => console.error(` ${s}`)); + suggestions.forEach((s) => console.error(` ${s}`)); console.error(''); } console.error(colored('Available profiles:', 'cyan')); - availableProfiles.forEach(line => console.error(` ${line}`)); + availableProfiles.forEach((line) => console.error(` ${line}`)); console.error(''); console.error(colored('Solutions:', 'yellow')); console.error(' # Use existing profile'); @@ -156,4 +157,4 @@ export class ErrorManager { console.error(''); this.showErrorCode(ERROR_CODES.FS_CANNOT_WRITE_FILE); } -} \ No newline at end of file +} diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index f6504bf1..6c4c6858 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -8,7 +8,7 @@ import { ColorName, TerminalInfo } from '../types'; function getColors(): Record { const forcedColors = process.env.FORCE_COLOR; const noColor = process.env.NO_COLOR; - const isTTY = process.stdout.isTTY === true; // Must be explicitly true + const isTTY = process.stdout.isTTY === true; // Must be explicitly true const useColors = !!forcedColors || (isTTY && !noColor); @@ -20,7 +20,7 @@ function getColors(): Record { green: '\x1b[0;32m', blue: '\x1b[0;34m', bold: '\x1b[1m', - reset: '\x1b[0m' + reset: '\x1b[0m', }; } @@ -106,8 +106,8 @@ export function levenshteinDistance(a: string, b: string): number { } else { matrix[i][j] = Math.min( matrix[i - 1][j - 1] + 1, // substitution - matrix[i][j - 1] + 1, // insertion - matrix[i - 1][j] + 1 // deletion + matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j] + 1 // deletion ); } } @@ -127,14 +127,14 @@ export function findSimilarStrings( const targetLower = target.toLowerCase(); const matches = candidates - .map(candidate => ({ + .map((candidate) => ({ name: candidate, - distance: levenshteinDistance(targetLower, candidate.toLowerCase()) + distance: levenshteinDistance(targetLower, candidate.toLowerCase()), })) - .filter(item => item.distance <= maxDistance && item.distance > 0) + .filter((item) => item.distance <= maxDistance && item.distance > 0) .sort((a, b) => a.distance - b.distance) - .slice(0, 3) // Show at most 3 suggestions - .map(item => item.name); + .slice(0, 3) // Show at most 3 suggestions + .map((item) => item.name); return matches; -} \ No newline at end of file +} diff --git a/src/utils/prompt.ts b/src/utils/prompt.ts index 35a9bae8..88785ff0 100644 --- a/src/utils/prompt.ts +++ b/src/utils/prompt.ts @@ -28,7 +28,11 @@ export class InteractivePrompt { const { default: defaultValue = false } = options; // Check for --yes flag (automation) - always returns true - if (process.env.CCS_YES === '1' || process.argv.includes('--yes') || process.argv.includes('-y')) { + if ( + process.env.CCS_YES === '1' || + process.argv.includes('--yes') || + process.argv.includes('-y') + ) { return true; } @@ -46,12 +50,10 @@ export class InteractivePrompt { const rl = readline.createInterface({ input: process.stdin, output: process.stderr, - terminal: true + terminal: true, }); - const promptText = defaultValue - ? `${message} [Y/n]: ` - : `${message} [y/N]: `; + const promptText = defaultValue ? `${message} [Y/n]: ` : `${message} [y/N]: `; return new Promise((resolve) => { rl.question(promptText, (answer: string) => { @@ -100,12 +102,10 @@ export class InteractivePrompt { const rl = readline.createInterface({ input: process.stdin, output: process.stderr, - terminal: true + terminal: true, }); - const promptText = defaultValue - ? `${message} [${defaultValue}]: ` - : `${message}: `; + const promptText = defaultValue ? `${message} [${defaultValue}]: ` : `${message}: `; return new Promise((resolve) => { rl.question(promptText, (answer: string) => { @@ -127,4 +127,4 @@ export class InteractivePrompt { }); }); } -} \ No newline at end of file +} diff --git a/src/utils/shell-completion.ts b/src/utils/shell-completion.ts index 103aabd2..eef7919a 100644 --- a/src/utils/shell-completion.ts +++ b/src/utils/shell-completion.ts @@ -53,7 +53,7 @@ export class ShellCompletionInstaller { // Copy completion scripts const files = ['ccs.bash', 'ccs.zsh', 'ccs.fish', 'ccs.ps1']; - files.forEach(file => { + files.forEach((file) => { const src = path.join(this.scriptsDir, file); const dest = path.join(this.completionDir, file); @@ -72,7 +72,7 @@ export class ShellCompletionInstaller { if (!stat.isDirectory()) { throw new Error( `Cannot create directory: ${dirPath} exists but is a file.\n` + - `Please remove or rename this file and try again.` + `Please remove or rename this file and try again.` ); } // Directory exists, nothing to do @@ -118,7 +118,7 @@ export class ShellCompletionInstaller { return { success: true, message: `Added to ${rcFile}`, - reload: 'source ~/.bashrc' + reload: 'source ~/.bashrc', }; } @@ -142,10 +142,7 @@ export class ShellCompletionInstaller { fs.copyFileSync(completionPath, destFile); const marker = '# CCS shell completion'; - const setupCmds = [ - 'fpath=(~/.zsh/completion $fpath)', - 'autoload -Uz compinit && compinit' - ]; + const setupCmds = ['fpath=(~/.zsh/completion $fpath)', 'autoload -Uz compinit && compinit']; const block = `\n${marker}\n${setupCmds.join('\n')}\n`; // Check if already installed @@ -162,7 +159,7 @@ export class ShellCompletionInstaller { return { success: true, message: `Added to ${rcFile}`, - reload: 'source ~/.zshrc' + reload: 'source ~/.zshrc', }; } @@ -187,7 +184,7 @@ export class ShellCompletionInstaller { return { success: true, message: `Installed to ${destFile}`, - reload: 'Fish auto-loads completions (no reload needed)' + reload: 'Fish auto-loads completions (no reload needed)', }; } @@ -195,12 +192,9 @@ export class ShellCompletionInstaller { * Install PowerShell completion */ private installPowerShell(): InstallResult { - const profilePath = process.env.PROFILE || path.join( - this.homeDir, - 'Documents', - 'PowerShell', - 'Microsoft.PowerShell_profile.ps1' - ); + const profilePath = + process.env.PROFILE || + path.join(this.homeDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'); const completionPath = path.join(this.completionDir, 'ccs.ps1'); if (!fs.existsSync(completionPath)) { @@ -229,7 +223,7 @@ export class ShellCompletionInstaller { return { success: true, message: `Added to ${profilePath}`, - reload: '. $PROFILE' + reload: '. $PROFILE', }; } @@ -240,7 +234,9 @@ export class ShellCompletionInstaller { const targetShell = shell || this.detectShell(); if (!targetShell) { - throw new Error('Could not detect shell. Please specify: --bash, --zsh, --fish, or --powershell'); + throw new Error( + 'Could not detect shell. Please specify: --bash, --zsh, --fish, or --powershell' + ); } // Ensure completion files exist @@ -260,4 +256,4 @@ export class ShellCompletionInstaller { throw new Error(`Unsupported shell: ${targetShell}`); } } -} \ No newline at end of file +} diff --git a/src/utils/update-checker.ts b/src/utils/update-checker.ts index c31c1829..067dc043 100644 --- a/src/utils/update-checker.ts +++ b/src/utils/update-checker.ts @@ -50,31 +50,35 @@ export function compareVersions(v1: string, v2: string): number { */ function fetchLatestVersionFromGitHub(): Promise { return new Promise((resolve) => { - const req = https.get(GITHUB_API_URL, { - headers: { 'User-Agent': 'CCS-Update-Checker' }, - timeout: REQUEST_TIMEOUT - }, (res) => { - let data = ''; + const req = https.get( + GITHUB_API_URL, + { + headers: { 'User-Agent': 'CCS-Update-Checker' }, + timeout: REQUEST_TIMEOUT, + }, + (res) => { + let data = ''; - res.on('data', (chunk: Buffer) => { - data += chunk.toString(); - }); + res.on('data', (chunk: Buffer) => { + data += chunk.toString(); + }); - res.on('end', () => { - try { - if (res.statusCode !== 200) { + res.on('end', () => { + try { + if (res.statusCode !== 200) { + resolve(null); + return; + } + + const release = JSON.parse(data) as { tag_name?: string }; + const version = release.tag_name?.replace(/^v/, '') || null; + resolve(version); + } catch { resolve(null); - return; } - - const release = JSON.parse(data) as { tag_name?: string }; - const version = release.tag_name?.replace(/^v/, '') || null; - resolve(version); - } catch { - resolve(null); - } - }); - }); + }); + } + ); req.on('error', () => resolve(null)); req.on('timeout', () => { @@ -89,31 +93,35 @@ function fetchLatestVersionFromGitHub(): Promise { */ function fetchLatestVersionFromNpm(): Promise { return new Promise((resolve) => { - const req = https.get(NPM_REGISTRY_URL, { - headers: { 'User-Agent': 'CCS-Update-Checker' }, - timeout: REQUEST_TIMEOUT - }, (res) => { - let data = ''; + const req = https.get( + NPM_REGISTRY_URL, + { + headers: { 'User-Agent': 'CCS-Update-Checker' }, + timeout: REQUEST_TIMEOUT, + }, + (res) => { + let data = ''; - res.on('data', (chunk: Buffer) => { - data += chunk.toString(); - }); + res.on('data', (chunk: Buffer) => { + data += chunk.toString(); + }); - res.on('end', () => { - try { - if (res.statusCode !== 200) { + res.on('end', () => { + try { + if (res.statusCode !== 200) { + resolve(null); + return; + } + + const packageData = JSON.parse(data) as { version?: string }; + const version = packageData.version || null; + resolve(version); + } catch { resolve(null); - return; } - - const packageData = JSON.parse(data) as { version?: string }; - const version = packageData.version || null; - resolve(version); - } catch { - resolve(null); - } - }); - }); + }); + } + ); req.on('error', () => resolve(null)); req.on('timeout', () => { @@ -170,7 +178,7 @@ export async function checkForUpdates( const now = Date.now(); // Check if we should check for updates - if (!force && (now - cache.last_check < CHECK_INTERVAL)) { + if (!force && now - cache.last_check < CHECK_INTERVAL) { // Use cached result if available if (cache.latest_version && compareVersions(cache.latest_version, currentVersion) > 0) { // Don't show if user dismissed this version @@ -206,7 +214,7 @@ export async function checkForUpdates( return { status: 'check_failed', reason: fetchError, - message: `Failed to check for updates: ${fetchError.replace(/_/g, ' ')}` + message: `Failed to check for updates: ${fetchError.replace(/_/g, ' ')}`, }; } @@ -228,7 +236,9 @@ export async function checkForUpdates( export function showUpdateNotification(updateInfo: { current: string; latest: string }): void { console.log(''); console.log(colored('═══════════════════════════════════════════════════════', 'cyan')); - console.log(colored(` Update available: ${updateInfo.current} → ${updateInfo.latest}`, 'yellow')); + console.log( + colored(` Update available: ${updateInfo.current} → ${updateInfo.latest}`, 'yellow') + ); console.log(colored('═══════════════════════════════════════════════════════', 'cyan')); console.log(''); console.log(` Run ${colored('ccs update', 'yellow')} to update`);