diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..e1e719e2 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +coverage/ +*.min.js +*.config.js +.github/ diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..e330adde --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module", + "project": "./tsconfig.json" + }, + "plugins": ["@typescript-eslint"], + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], + "env": { + "node": true, + "es2020": true + }, + "rules": { + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_" + } + ], + "no-console": "off", + "no-constant-condition": "off", + "default-case": "error", + "prefer-const": "error", + "no-var": "error", + "eqeqeq": ["error", "always"], + "curly": ["error", "all"] + }, + "ignorePatterns": ["dist", "node_modules", "*.js"] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27062825..0eed4664 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,9 @@ jobs: - name: Check formatting run: npm run format:check + - name: Run linter + run: npm run lint + - name: Build run: npm run build diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..1d43a318 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "printWidth": 100, + "tabWidth": 2 +} diff --git a/package-lock.json b/package-lock.json index 189ee67d..9a164c97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@zed-industries/claude-code-acp", - "version": "0.1.7", + "version": "0.1.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@zed-industries/claude-code-acp", - "version": "0.1.7", + "version": "0.1.8", "license": "Apache-2.0", "dependencies": { "@anthropic-ai/claude-code": "^1.0.100", @@ -24,7 +24,11 @@ "@types/minimist": "^1.2.5", "@types/node": "^24.3.0", "@types/uuid": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^8.42.0", + "@typescript-eslint/parser": "^8.42.0", "@vitest/ui": "^3.2.4", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", "prettier": "^3.6.2", "ts-node": "^10.9.2", "typescript": "^5.4.0", @@ -506,6 +510,155 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -766,6 +919,44 @@ "node": ">=18" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -1215,6 +1406,258 @@ "dev": true, "license": "MIT" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz", + "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/type-utils": "8.42.0", + "@typescript-eslint/utils": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.42.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz", + "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz", + "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.42.0", + "@typescript-eslint/types": "^8.42.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz", + "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz", + "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz", + "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0", + "@typescript-eslint/utils": "8.42.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz", + "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz", + "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.42.0", + "@typescript-eslint/tsconfig-utils": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/visitor-keys": "8.42.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz", + "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.42.0", + "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/typescript-estree": "8.42.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz", + "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.42.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/@vitest/expect": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", @@ -1387,6 +1830,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", @@ -1416,6 +1869,32 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "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/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/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1423,6 +1902,13 @@ "dev": true, "license": "MIT" }, + "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/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -1433,6 +1919,13 @@ "node": ">=12" } }, + "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/body-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", @@ -1453,6 +1946,29 @@ "node": ">=18" } }, + "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/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1501,6 +2017,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/chai": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", @@ -1518,6 +2044,23 @@ "node": ">=18" } }, + "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/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -1528,6 +2071,33 @@ "node": ">= 16" } }, + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -1628,6 +2198,13 @@ "node": ">=6" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1647,6 +2224,19 @@ "node": ">=0.3.1" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1761,6 +2351,197 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "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/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -1771,6 +2552,16 @@ "@types/estree": "^1.0.0" } }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1874,12 +2665,59 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "license": "MIT" }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -1905,6 +2743,32 @@ "dev": true, "license": "MIT" }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", @@ -1922,6 +2786,38 @@ "node": ">= 0.8" } }, + "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-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/flatted": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", @@ -1947,6 +2843,13 @@ "node": ">= 0.8" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2008,6 +2911,81 @@ "node": ">= 0.4" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -2020,6 +2998,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "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/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -2081,6 +3076,55 @@ "node": ">=0.10.0" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2096,6 +3140,49 @@ "node": ">= 0.10" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "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-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -2115,12 +3202,86 @@ "dev": true, "license": "MIT" }, + "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/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "license": "MIT" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -2175,6 +3336,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -2196,6 +3394,22 @@ "node": ">= 0.6" } }, + "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/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -2240,6 +3454,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -2291,6 +3512,69 @@ "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2300,6 +3584,26 @@ "node": ">= 0.8" } }, + "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-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2393,6 +3697,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", @@ -2446,6 +3760,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2470,6 +3805,44 @@ "node": ">= 0.8" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rollup": { "version": "4.49.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz", @@ -2526,6 +3899,30 @@ "node": ">= 18" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2552,6 +3949,19 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", @@ -2743,6 +4153,32 @@ "dev": true, "license": "MIT" }, + "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-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/strip-literal": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", @@ -2756,6 +4192,26 @@ "url": "https://github.com/sponsors/antfu" } }, + "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/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -2817,6 +4273,19 @@ "node": ">=14.0.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -2836,6 +4305,19 @@ "node": ">=6" } }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -2880,6 +4362,32 @@ } } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -3164,6 +4672,16 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3180,6 +4698,19 @@ "node": ">=6" } }, + "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/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", diff --git a/package.json b/package.json index c022e94d..d5e2ae4a 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,11 @@ "build": "tsc", "start": "node dist/index.js", "dev": "npm run build && npm run start", + "lint": "eslint src --ext .ts", + "lint:fix": "eslint src --ext .ts --fix", "format": "prettier --write .", "format:check": "prettier --check .", + "check": "npm run lint && npm run format:check", "test": "vitest", "test:integration": "RUN_INTEGRATION_TESTS=true vitest run", "test:run": "vitest run", @@ -46,7 +49,11 @@ "@types/minimist": "^1.2.5", "@types/node": "^24.3.0", "@types/uuid": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^8.42.0", + "@typescript-eslint/parser": "^8.42.0", "@vitest/ui": "^3.2.4", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", "prettier": "^3.6.2", "ts-node": "^10.9.2", "typescript": "^5.4.0", diff --git a/src/acp-agent.ts b/src/acp-agent.ts index e0134611..41fa69d7 100644 --- a/src/acp-agent.ts +++ b/src/acp-agent.ts @@ -31,21 +31,11 @@ import * as fs from "node:fs"; import * as path from "node:path"; import * as os from "node:os"; import { v7 as uuidv7 } from "uuid"; -import { - nodeToWebReadable, - nodeToWebWritable, - Pushable, - sleep, - unreachable, -} from "./utils.js"; +import { nodeToWebReadable, nodeToWebWritable, Pushable, sleep, unreachable } from "./utils.js"; import { SessionNotification } from "@zed-industries/agent-client-protocol"; import { createMcpServer } from "./mcp-server.js"; import { AddressInfo } from "node:net"; -import { - toolInfoFromToolUse, - planEntries, - toolUpdateFromToolResult, -} from "./tools.js"; +import { toolInfoFromToolUse, planEntries, toolUpdateFromToolResult } from "./tools.js"; type Session = { query: Query; @@ -64,14 +54,18 @@ type BackgroundTerminal = pendingOutput: TerminalOutputResponse; }; +type ToolUseCache = { + [key: string]: { type: "tool_use"; id: string; name: string; input: any }; +}; + // Implement the ACP Agent interface export class ClaudeAcpAgent implements Agent { sessions: { [key: string]: Session; }; client: AgentSideConnection; - toolUseCache: { [key: string]: any }; - fileContentCache: { [key: string]: any }; + toolUseCache: ToolUseCache; + fileContentCache: { [key: string]: string }; backgroundTerminals: { [key: string]: BackgroundTerminal } = {}; clientCapabilities?: ClientCapabilities; @@ -108,8 +102,8 @@ export class ClaudeAcpAgent implements Agent { }; } async newSession(params: NewSessionRequest): Promise { - let sessionId = uuidv7(); - let input = new Pushable(); + const sessionId = uuidv7(); + const input = new Pushable(); const mcpServers: Record = {}; if (Array.isArray(params.mcpServers)) { @@ -125,12 +119,8 @@ export class ClaudeAcpAgent implements Agent { } } - let server = await createMcpServer( - this, - sessionId, - this.clientCapabilities, - ); - let address = server.address() as AddressInfo; + const server = await createMcpServer(this, sessionId, this.clientCapabilities); + const address = server.address() as AddressInfo; mcpServers["acp"] = { type: "http", url: "http://127.0.0.1:" + address.port + "/mcp", @@ -139,7 +129,7 @@ export class ClaudeAcpAgent implements Agent { }, }; - let options: Options = { + const options: Options = { cwd: params.cwd, mcpServers, permissionPromptToolName: "mcp__acp__permission", @@ -200,7 +190,7 @@ export class ClaudeAcpAgent implements Agent { input.push(promptToClaude(params)); while (true) { - let { value: message, done } = await query.next(); + const { value: message, done } = await query.next(); if (done || !message) { if (this.sessions[params.sessionId].cancelled) { return { stopReason: "cancelled" }; @@ -238,8 +228,8 @@ export class ClaudeAcpAgent implements Agent { } if ( - message.message.model == "" && - message.message.content.length == 1 && + message.message.model === "" && + message.message.content.length === 1 && message.message.content[0].text.includes("Please run /login") ) { throw RequestError.authRequired(); @@ -269,28 +259,22 @@ export class ClaudeAcpAgent implements Agent { await this.sessions[params.sessionId].query.interrupt(); } - async readTextFile( - params: ReadTextFileRequest, - ): Promise { - let response = await this.client.readTextFile(params); + async readTextFile(params: ReadTextFileRequest): Promise { + const response = await this.client.readTextFile(params); if (!params.limit && !params.line) { this.fileContentCache[params.path] = response.content; } return response; } - async writeTextFile( - params: WriteTextFileRequest, - ): Promise { - let response = await this.client.writeTextFile(params); + async writeTextFile(params: WriteTextFileRequest): Promise { + const response = await this.client.writeTextFile(params); this.fileContentCache[params.path] = params.content; return response; } } -async function availableSlashCommands( - query: Query, -): Promise { +async function availableSlashCommands(query: Query): Promise { const UNSUPPORTED_COMMANDS = [ "add-dir", "agents", // Modal @@ -342,28 +326,17 @@ async function availableSlashCommands( ]); return commands - .map( - (command: { - name: string; - description: string; - argumentHint: string; - }) => { - const input = command.argumentHint - ? { hint: command.argumentHint } - : null; - return { - name: command.name, - description: command.description || "", - input, - }; - }, - ) + .map((command: { name: string; description: string; argumentHint: string }) => { + const input = command.argumentHint ? { hint: command.argumentHint } : null; + return { + name: command.name, + description: command.description || "", + input, + }; + }) .filter( (command: AvailableCommand) => - !( - command.name.match(/\(MCP\)/) || - UNSUPPORTED_COMMANDS.includes(command.name) - ), + !(command.name.match(/\(MCP\)/) || UNSUPPORTED_COMMANDS.includes(command.name)), ); } @@ -462,13 +435,11 @@ function promptToClaude(prompt: PromptRequest): SDKUserMessage { export function toAcpNotifications( message: SDKAssistantMessage | SDKUserMessage, sessionId: string, - toolUseCache: { - [key: string]: { type: "tool_use"; id: string; name: string; input: any }; - }, + toolUseCache: ToolUseCache, fileContentCache: { [key: string]: string }, ): SessionNotification[] { - let chunks = message.message.content as ContentChunk[]; - let output = []; + const chunks = message.message.content as ContentChunk[]; + const output = []; // Only handle the first chunk for streaming; extend as needed for batching for (const chunk of chunks) { let update: SessionNotification["update"] | null = null; @@ -488,8 +459,7 @@ export function toAcpNotifications( content: { type: "image", data: chunk.source.type === "base64" ? chunk.source.data : "", - mimeType: - chunk.source.type === "base64" ? chunk.source.media_type : "", + mimeType: chunk.source.type === "base64" ? chunk.source.media_type : "", uri: chunk.source.type === "url" ? chunk.source.url : undefined, }, }; @@ -505,7 +475,7 @@ export function toAcpNotifications( break; case "tool_use": toolUseCache[chunk.id] = chunk; - if (chunk.name == "TodoWrite") { + if (chunk.name === "TodoWrite") { update = { sessionUpdate: "plan", entries: planEntries(chunk.input), @@ -562,7 +532,7 @@ export function runAcp() { type ContentChunk = | { type: "text"; text: string } - | { type: "tool_use"; id: string; name: string; input: any } // input is serde_json::Value, so use any or unknown + | { type: "tool_use"; id: string; name: string; input: any } | { type: "tool_result"; content: string; diff --git a/src/mcp-server.ts b/src/mcp-server.ts index 827feb6f..1bd31ee4 100644 --- a/src/mcp-server.ts +++ b/src/mcp-server.ts @@ -1,16 +1,13 @@ import express from "express"; -import { randomUUID } from "node:crypto"; + import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { z } from "zod"; import { Server } from "node:http"; import { ClaudeAcpAgent } from "./acp-agent.js"; -import { - ClientCapabilities, - TerminalOutputResponse, -} from "@zed-industries/agent-client-protocol"; -import { tool } from "@anthropic-ai/claude-code"; -import { sleep } from "./utils.js"; +import { ClientCapabilities, TerminalOutputResponse } from "@zed-industries/agent-client-protocol"; + +import { sleep, unreachable } from "./utils.js"; export const SYSTEM_REMINDER = ` @@ -40,19 +37,12 @@ Never attempt to read a path that hasn't been previously mentioned. In sessions with mcp__acp__read always use it instead of Read as it contains the most up-to-date contents.`, inputSchema: { - abs_path: z - .string() - .describe("The absolute path to the file to read."), + abs_path: z.string().describe("The absolute path to the file to read."), offset: z .number() .optional() - .describe( - "Which line to start reading from. Omit to start from the beginning.", - ), - limit: z - .number() - .optional() - .describe("How many lines to read. Omit for the whole file."), + .describe("Which line to start reading from. Omit to start from the beginning."), + limit: z.number().optional().describe("How many lines to read. Omit for the whole file."), }, annotations: { title: "Read file", @@ -75,7 +65,7 @@ In sessions with mcp__acp__read always use it instead of Read as it contains the ], }; } - let content = await agent.readTextFile({ + const content = await agent.readTextFile({ sessionId, path: input.abs_path, limit: input.limit, @@ -114,9 +104,7 @@ In sessions with mcp__acp__read always use it instead of Read as it contains the In sessions with mcp__acp__write always use it instead of Write as it will allow the user to conveniently review changes.`, inputSchema: { - abs_path: z - .string() - .describe("The absolute path to the file to write"), + abs_path: z.string().describe("The absolute path to the file to write"), content: z.string().describe("The full content to write"), }, annotations: { @@ -140,7 +128,7 @@ allow the user to conveniently review changes.`, ], }; } - let content = await agent.writeTextFile({ + await agent.writeTextFile({ sessionId, path: input.abs_path, content: input.content, @@ -181,12 +169,8 @@ File editing instructions: - Do not escape quotes, newlines, or other characters. - Only edit the specified file.`, inputSchema: { - abs_path: z - .string() - .describe("The absolute path to the file to read."), - old_string: z - .string() - .describe("The old text to replace (must be unique in the file)"), + abs_path: z.string().describe("The absolute path to the file to read."), + old_string: z.string().describe("The old text to replace (must be unique in the file)"), new_string: z.string().describe("The new text."), }, annotations: { @@ -211,21 +195,18 @@ File editing instructions: }; } - let { content } = await agent.readTextFile({ + const { content } = await agent.readTextFile({ sessionId, path: input.abs_path, }); - const { newContent, lineNumbers } = replaceAndCalculateLocation( - content, - [ - { - oldText: input.old_string, - newText: input.new_string, - replaceAll: false, - }, - ], - ); + const { newContent, lineNumbers } = replaceAndCalculateLocation(content, [ + { + oldText: input.old_string, + newText: input.new_string, + replaceAll: false, + }, + ]); await agent.writeTextFile({ sessionId, @@ -260,9 +241,7 @@ File editing instructions: title: "Multi Edit", description: `Edit a file with multiple sequential edits.`, inputSchema: { - file_path: z - .string() - .describe("The absolute path to the file to modify"), + file_path: z.string().describe("The absolute path to the file to modify"), edits: z .array( z.object({ @@ -271,15 +250,11 @@ File editing instructions: replace_all: z .boolean() .optional() - .describe( - "Replace all occurrences of old_string (default false)", - ), + .describe("Replace all occurrences of old_string (default false)"), }), ) .min(1) - .describe( - "Array of edit operations to perform sequentially on the file", - ), + .describe("Array of edit operations to perform sequentially on the file"), }, annotations: { title: "Multi Edit file", @@ -302,7 +277,7 @@ File editing instructions: }; } - let { content } = await agent.readTextFile({ + const { content } = await agent.readTextFile({ sessionId, path: input.file_path, }); @@ -341,9 +316,7 @@ File editing instructions: title: "Bash", description: "Executes a bash command", inputSchema: { - command: z - .string() - .describe("The bash command to execute as a one-liner"), + command: z.string().describe("The bash command to execute as a one-liner"), timeout_ms: z .number() .default(2 * 60 * 1000) @@ -375,10 +348,7 @@ File editing instructions: throw new Error("No tool call ID found"); } - if ( - !agent.clientCapabilities?.terminal || - !agent.client.createTerminal - ) { + if (!agent.clientCapabilities?.terminal || !agent.client.createTerminal) { throw new Error("unreachable"); } @@ -439,10 +409,7 @@ File editing instructions: status, pendingOutput: { ...currentOutput, - output: stripCommonPrefix( - bgTerm.lastOutput?.output ?? "", - currentOutput.output, - ), + output: stripCommonPrefix(bgTerm.lastOutput?.output ?? "", currentOutput.output), }, }; @@ -486,9 +453,7 @@ File editing instructions: inputSchema: { id: z .string() - .describe( - "The id of the background bash command as returned by `mcp__acp__Bash`", - ), + .describe("The id of the background bash command as returned by `mcp__acp__Bash`"), }, }, async (input) => { @@ -538,9 +503,7 @@ File editing instructions: inputSchema: { id: z .string() - .describe( - "The id of the background bash command as returned by `mcp__acp__Bash`", - ), + .describe("The id of the background bash command as returned by `mcp__acp__Bash`"), }, }, async (input) => { @@ -551,17 +514,14 @@ File editing instructions: } switch (bgTerm.status) { - case "started": + case "started": { await bgTerm.handle.kill(); const currentOutput = await bgTerm.handle.currentOutput(); agent.backgroundTerminals[bgTerm.handle.id] = { status: "killed", pendingOutput: { ...currentOutput, - output: stripCommonPrefix( - bgTerm.lastOutput?.output ?? "", - currentOutput.output, - ), + output: stripCommonPrefix(bgTerm.lastOutput?.output ?? "", currentOutput.output), }, }; await bgTerm.handle.release(); @@ -569,6 +529,7 @@ File editing instructions: return { content: [{ type: "text", text: "Command killed successfully." }], }; + } case "aborted": return { content: [{ type: "text", text: "Command aborted by user." }], @@ -586,69 +547,14 @@ File editing instructions: content: [{ type: "text", text: "Command killed by timeout." }], }; default: { - const unreachable: never = bgTerm; - return unreachable; + return unreachable(bgTerm); } } }, ); - - function stripCommonPrefix(a: string, b: string): string { - let i = 0; - while (i < a.length && i < b.length && a[i] === b[i]) i++; - return b.slice(i); - } - - function toolCommandOutput( - status: "started" | "aborted" | "exited" | "killed" | "timedOut", - output: TerminalOutputResponse, - ): string { - const { exitStatus, output: commandOutput, truncated } = output; - - let toolOutput = ""; - - switch (status) { - case "started": { - if (exitStatus?.exitCode == null) { - toolOutput += `Interrupted. `; - } - break; - } - case "killed": - toolOutput += `Killed. `; - break; - case "timedOut": - toolOutput += `Timed out. `; - break; - case "exited": - case "aborted": - break; - default: { - const unreachable: never = status; - return unreachable; - } - } - - if (exitStatus?.exitCode != null && exitStatus.exitCode !== 0) { - toolOutput += `Failed with exit code ${exitStatus.exitCode}.`; - } - - if (exitStatus?.signal != null) { - toolOutput += `Signal \`${exitStatus.signal}\`. `; - } - - toolOutput += "Output:\n\n"; - toolOutput += commandOutput; - - if (truncated) { - toolOutput += `\n\nCommand output was too long, so it was truncated to ${commandOutput.length} bytes.`; - } - - return toolOutput; - } } - let alwaysAllowedTools: { [key: string]: boolean } = {}; + const alwaysAllowedTools: { [key: string]: boolean } = {}; server.registerTool( "permission", { @@ -688,7 +594,7 @@ File editing instructions: ], }; } - let response = await agent.client.requestPermission({ + const response = await agent.client.requestPermission({ options: [ { kind: "allow_always", @@ -705,11 +611,10 @@ File editing instructions: }, }); if ( - response.outcome?.outcome == "selected" && - (response.outcome.optionId == "allow" || - response.outcome.optionId == "allow_always") + response.outcome?.outcome === "selected" && + (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always") ) { - if (response.outcome.optionId == "allow_always") { + if (response.outcome.optionId === "allow_always") { alwaysAllowedTools[input.tool_name] = true; } return { @@ -744,10 +649,9 @@ File editing instructions: app.post("/mcp", async (req, res) => { try { - const transport: StreamableHTTPServerTransport = - new StreamableHTTPServerTransport({ - sessionIdGenerator: undefined, - }); + const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); res.on("close", () => { transport.close(); server.close(); @@ -760,7 +664,7 @@ File editing instructions: jsonrpc: "2.0", error: { code: -32603, - message: "Internal server error", + message: `Internal server error: ${error}`, }, id: null, }); @@ -779,6 +683,64 @@ File editing instructions: }); } +function stripCommonPrefix(a: string, b: string): string { + let i = 0; + while (i < a.length && i < b.length && a[i] === b[i]) { + i++; + } + return b.slice(i); +} + +function toolCommandOutput( + status: "started" | "aborted" | "exited" | "killed" | "timedOut", + output: TerminalOutputResponse, +): string { + const { exitStatus, output: commandOutput, truncated } = output; + + let toolOutput = ""; + + switch (status) { + case "started": { + if (exitStatus && (exitStatus.exitCode ?? null) === null) { + toolOutput += `Interrupted. `; + } + break; + } + case "killed": + toolOutput += `Killed. `; + break; + case "timedOut": + toolOutput += `Timed out. `; + break; + case "exited": + case "aborted": + break; + default: { + const unreachable: never = status; + return unreachable; + } + } + + if (exitStatus) { + if (typeof exitStatus.exitCode === "number" && exitStatus.exitCode !== 0) { + toolOutput += `Failed with exit code ${exitStatus.exitCode}.`; + } + + if (typeof exitStatus.signal === "string") { + toolOutput += `Signal \`${exitStatus.signal}\`. `; + } + } + + toolOutput += "Output:\n\n"; + toolOutput += commandOutput; + + if (truncated) { + toolOutput += `\n\nCommand output was too long, so it was truncated to ${commandOutput.length} bytes.`; + } + + return toolOutput; +} + /** * Replace text in a file and calculate the line numbers where the edits occurred. * @@ -816,7 +778,9 @@ export function replaceAndCalculateLocation( while (true) { const index = currentContent.indexOf(edit.oldText, searchIndex); - if (index === -1) break; + if (index === -1) { + break; + } // Add content before the match parts.push(currentContent.substring(lastIndex, index)); diff --git a/src/tests/acp-agent.test.ts b/src/tests/acp-agent.test.ts index fd19e79f..64badd06 100644 --- a/src/tests/acp-agent.test.ts +++ b/src/tests/acp-agent.test.ts @@ -14,185 +14,168 @@ import { WriteTextFileResponse, } from "@zed-industries/agent-client-protocol"; import { nodeToWebWritable, nodeToWebReadable } from "../utils.js"; -import { - markdownEscape, - toolInfoFromToolUse, - toolUpdateFromToolResult, -} from "../tools.js"; +import { markdownEscape, toolInfoFromToolUse, toolUpdateFromToolResult } from "../tools.js"; import { toAcpNotifications } from "../acp-agent.js"; import { SDKAssistantMessage } from "@anthropic-ai/claude-code"; -describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)( - "ACP subprocess integration", - () => { - let child: ReturnType; +describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("ACP subprocess integration", () => { + let child: ReturnType; - beforeAll(async () => { - let valid = spawnSync("tsc", { stdio: "inherit" }); - if (valid.status) { - throw new Error("failed to compile"); - } - // Start the subprocess - child = spawn("npm", ["run", "--silent", "dev"], { - stdio: ["pipe", "pipe", "inherit"], - env: process.env, - }); - child.on("error", (error) => { - console.error("Error starting subprocess:", error); - }); - child.on("exit", (exit) => { - console.error("Exited with", exit); - }); + beforeAll(async () => { + const valid = spawnSync("tsc", { stdio: "inherit" }); + if (valid.status) { + throw new Error("failed to compile"); + } + // Start the subprocess + child = spawn("npm", ["run", "--silent", "dev"], { + stdio: ["pipe", "pipe", "inherit"], + env: process.env, }); - - afterAll(() => { - child.kill(); + child.on("error", (error) => { + console.error("Error starting subprocess:", error); }); + child.on("exit", (exit) => { + console.error("Exited with", exit); + }); + }); - class TestClient implements Client { - agent: Agent; - files: Map = new Map(); - receivedText: string = ""; + afterAll(() => { + child.kill(); + }); - constructor(agent: Agent) { - this.agent = agent; - } + class TestClient implements Client { + agent: Agent; + files: Map = new Map(); + receivedText: string = ""; - takeReceivedText() { - const text = this.receivedText; - this.receivedText = ""; - return text; - } + constructor(agent: Agent) { + this.agent = agent; + } - async requestPermission( - params: RequestPermissionRequest, - ): Promise { - const optionId = params.options.find( - (p) => p.kind === "allow_once", - )!.optionId; + takeReceivedText() { + const text = this.receivedText; + this.receivedText = ""; + return text; + } - return { outcome: { outcome: "selected", optionId } }; - } + async requestPermission(params: RequestPermissionRequest): Promise { + const optionId = params.options.find((p) => p.kind === "allow_once")!.optionId; - async sessionUpdate(params: SessionNotification): Promise { - console.error("RECEIVED", JSON.stringify(params, null, 4)); + return { outcome: { outcome: "selected", optionId } }; + } - if ( - params.update.sessionUpdate === "agent_message_chunk" && - params.update.content.type === "text" - ) { - this.receivedText += params.update.content.text; - } - } + async sessionUpdate(params: SessionNotification): Promise { + console.error("RECEIVED", JSON.stringify(params, null, 4)); - async writeTextFile( - params: WriteTextFileRequest, - ): Promise { - this.files.set(params.path, params.content); - return null; - } - - async readTextFile( - params: ReadTextFileRequest, - ): Promise { - const content = this.files.get(params.path) ?? ""; - return { - content, - }; + if ( + params.update.sessionUpdate === "agent_message_chunk" && + params.update.content.type === "text" + ) { + this.receivedText += params.update.content.text; } } - async function setupTestSession(cwd: string): Promise<{ - client: TestClient; - connection: ClientSideConnection; - newSessionResponse: NewSessionResponse; - }> { - let client; - const connection = new ClientSideConnection( - (agent) => { - client = new TestClient(agent); - return client; - }, - nodeToWebWritable(child.stdin!), - nodeToWebReadable(child.stdout!), - ); - - await connection.initialize({ - protocolVersion: 1, - clientCapabilities: { - fs: { - readTextFile: true, - writeTextFile: true, - }, - }, - }); - - const newSessionResponse = await connection.newSession({ - cwd, - mcpServers: [], - }); - - return { client: client!, connection, newSessionResponse }; + async writeTextFile(params: WriteTextFileRequest): Promise { + this.files.set(params.path, params.content); + return null; } - it("should connect to the ACP subprocess", async () => { - const { client, connection, newSessionResponse } = - await setupTestSession("./"); + async readTextFile(params: ReadTextFileRequest): Promise { + const content = this.files.get(params.path) ?? ""; + return { + content, + }; + } + } - await connection.prompt({ - prompt: [ - { - type: "text", - text: "Hello", - }, - ], - sessionId: newSessionResponse.sessionId, - }); + async function setupTestSession(cwd: string): Promise<{ + client: TestClient; + connection: ClientSideConnection; + newSessionResponse: NewSessionResponse; + }> { + let client; + const connection = new ClientSideConnection( + (agent) => { + client = new TestClient(agent); + return client; + }, + nodeToWebWritable(child.stdin!), + nodeToWebReadable(child.stdout!), + ); - expect(client.takeReceivedText()).not.toEqual(""); - }, 30000); + await connection.initialize({ + protocolVersion: 1, + clientCapabilities: { + fs: { + readTextFile: true, + writeTextFile: true, + }, + }, + }); - it("should include available commands", async () => { - const { client, connection, newSessionResponse } = - await setupTestSession(__dirname); + const newSessionResponse = await connection.newSession({ + cwd, + mcpServers: [], + }); - expect(newSessionResponse.availableCommands).toContainEqual({ - name: "quick-math", - description: "10 * 3 = 30 (project)", - input: null, - }); - expect(newSessionResponse.availableCommands).toContainEqual({ - name: "say-hello", - description: "Say hello (project)", - input: { hint: "[name]" }, - }); + return { client: client!, connection, newSessionResponse }; + } - await connection.prompt({ - prompt: [ - { - type: "text", - text: "/quick-math", - }, - ], - sessionId: newSessionResponse.sessionId, - }); + it("should connect to the ACP subprocess", async () => { + const { client, connection, newSessionResponse } = await setupTestSession("./"); - expect(client.takeReceivedText()).toContain("30"); + await connection.prompt({ + prompt: [ + { + type: "text", + text: "Hello", + }, + ], + sessionId: newSessionResponse.sessionId, + }); - await connection.prompt({ - prompt: [ - { - type: "text", - text: "/say-hello GPT-5", - }, - ], - sessionId: newSessionResponse.sessionId, - }); + expect(client.takeReceivedText()).not.toEqual(""); + }, 30000); - expect(client.takeReceivedText()).toContain("Hello GPT-5"); - }, 30000); - }, -); + it("should include available commands", async () => { + const { client, connection, newSessionResponse } = await setupTestSession(__dirname); + + expect(newSessionResponse.availableCommands).toContainEqual({ + name: "quick-math", + description: "10 * 3 = 30 (project)", + input: null, + }); + expect(newSessionResponse.availableCommands).toContainEqual({ + name: "say-hello", + description: "Say hello (project)", + input: { hint: "[name]" }, + }); + + await connection.prompt({ + prompt: [ + { + type: "text", + text: "/quick-math", + }, + ], + sessionId: newSessionResponse.sessionId, + }); + + expect(client.takeReceivedText()).toContain("30"); + + await connection.prompt({ + prompt: [ + { + type: "text", + text: "/say-hello GPT-5", + }, + ], + sessionId: newSessionResponse.sessionId, + }); + + expect(client.takeReceivedText()).toContain("Hello GPT-5"); + }, 30000); +}); describe("tool conversions", () => { it("should handle Bash nicely", () => { @@ -279,8 +262,7 @@ describe("tool conversions", () => { expect(toolInfoFromToolUse(tool_use, {})).toStrictEqual({ kind: "search", - title: - "List the `/Users/test/github/claude-code-acp` directory's contents", + title: "List the `/Users/test/github/claude-code-acp` directory's contents", content: [], locations: [], }); @@ -494,7 +476,7 @@ describe("tool conversions", () => { }); it("should handle KillBash entries", () => { - let tool_use = { + const tool_use = { type: "tool_use", id: "toolu_01PhLms5fuvmdjy2bb6dfUKT", name: "KillBash", @@ -511,7 +493,7 @@ describe("tool conversions", () => { }); it("should handle BashOutput entries", () => { - let tool_use = { + const tool_use = { type: "tool_use", id: "toolu_01SJUWPtj1QspgANgtpqGPuN", name: "BashOutput", @@ -568,8 +550,7 @@ describe("tool conversions", () => { activeForm: "Adding concurrent access tests", }, { - content: - "Add tests for Each function with various data types", + content: "Add tests for Each function with various data types", status: "pending", activeForm: "Adding Each function tests", }, @@ -708,7 +689,7 @@ describe("tool conversions", () => { ], }, }; - let fileCache: { [key: string]: string } = { + const fileCache: { [key: string]: string } = { "/Users/test/project/config.json": JSON.stringify( { version: 1, @@ -724,14 +705,14 @@ describe("tool conversions", () => { content: [ { newText: `{ - \"version\": 2, - \"filler\": \"filler\", - \"enabled\": true + "version": 2, + "filler": "filler", + "enabled": true }`, oldText: `{ - \"version\": 1, - \"filler\": \"filler\", - \"enabled\": false + "version": 1, + "filler": "filler", + "enabled": false }`, path: "/Users/test/project/config.json", type: "diff", @@ -811,9 +792,7 @@ describe("tool conversions", () => { const update = toolUpdateFromToolResult(toolResult, toolUse); - expect(update.locations).toEqual([ - { path: "/Users/test/project/main.py", line: 42 }, - ]); + expect(update.locations).toEqual([{ path: "/Users/test/project/main.py", line: 42 }]); }); it("should handle empty locations from edit tools", () => { @@ -878,8 +857,6 @@ describe("escape markdown", () => { text = "for example:\n```markdown\nHello *world*!\n```\n"; escaped = markdownEscape(text); - expect(escaped).toEqual( - "````\nfor example:\n```markdown\nHello *world*!\n```\n````", - ); + expect(escaped).toEqual("````\nfor example:\n```markdown\nHello *world*!\n```\n````"); }); }); diff --git a/src/tests/replace-and-calculate-location.test.ts b/src/tests/replace-and-calculate-location.test.ts index 5f590d13..87b68f40 100644 --- a/src/tests/replace-and-calculate-location.test.ts +++ b/src/tests/replace-and-calculate-location.test.ts @@ -4,13 +4,9 @@ import { replaceAndCalculateLocation } from "../mcp-server.js"; describe("replaceAndCalculateLocation", () => { it("should replace first occurrence and return correct line number", () => { const content = "line 1\nline 2 with text\nline 3 with text\nline 4"; - const result = replaceAndCalculateLocation(content, [ - { oldText: "text", newText: "replaced" }, - ]); + const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]); - expect(result.newContent).toBe( - "line 1\nline 2 with replaced\nline 3 with text\nline 4", - ); + expect(result.newContent).toBe("line 1\nline 2 with replaced\nline 3 with text\nline 4"); expect(result.lineNumbers).toEqual([1]); // Line 2 (0-based indexing) }); @@ -20,9 +16,7 @@ describe("replaceAndCalculateLocation", () => { { oldText: "text", newText: "replaced", replaceAll: true }, ]); - expect(result.newContent).toBe( - "line 1\nline 2 with replaced\nline 3 with replaced\nline 4", - ); + expect(result.newContent).toBe("line 1\nline 2 with replaced\nline 3 with replaced\nline 4"); expect(result.lineNumbers).toEqual([1, 2]); // Lines 2 and 3 (0-based) }); @@ -54,9 +48,7 @@ describe("replaceAndCalculateLocation", () => { { oldText: "text", newText: "replaced", replaceAll: true }, ]); - expect(result.newContent).toBe( - "line 1\rline 2 with replaced\rline 3 with replaced\rline 4", - ); + expect(result.newContent).toBe("line 1\rline 2 with replaced\rline 3 with replaced\rline 4"); expect(result.lineNumbers).toEqual([1, 2]); }); @@ -66,17 +58,13 @@ describe("replaceAndCalculateLocation", () => { { oldText: "text", newText: "replaced", replaceAll: true }, ]); - expect(result.newContent).toBe( - "line 1\nline 2 with replaced\r\nline 3 with replaced\rline 4", - ); + expect(result.newContent).toBe("line 1\nline 2 with replaced\r\nline 3 with replaced\rline 4"); expect(result.lineNumbers).toEqual([1, 2]); }); it("should handle text at the beginning of file", () => { const content = "text at start\nline 2\nline 3"; - const result = replaceAndCalculateLocation(content, [ - { oldText: "text", newText: "replaced" }, - ]); + const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]); expect(result.newContent).toBe("replaced at start\nline 2\nline 3"); expect(result.lineNumbers).toEqual([0]); // First line (0-based) @@ -84,9 +72,7 @@ describe("replaceAndCalculateLocation", () => { it("should handle text at the end of file", () => { const content = "line 1\nline 2\nlast line with text"; - const result = replaceAndCalculateLocation(content, [ - { oldText: "text", newText: "replaced" }, - ]); + const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]); expect(result.newContent).toBe("line 1\nline 2\nlast line with replaced"); expect(result.lineNumbers).toEqual([2]); // Last line (0-based) @@ -98,17 +84,13 @@ describe("replaceAndCalculateLocation", () => { { oldText: "text", newText: "replaced", replaceAll: true }, ]); - expect(result.newContent).toBe( - "line 1\nreplaced replaced replaced\nline 3", - ); + expect(result.newContent).toBe("line 1\nreplaced replaced replaced\nline 3"); expect(result.lineNumbers).toEqual([1]); // All on line 2 (0-based), deduplicated }); it("should handle empty file content", () => { const content = ""; - const result = replaceAndCalculateLocation(content, [ - { oldText: "text", newText: "replaced" }, - ]); + const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]); expect(result.newContent).toBe(""); expect(result.lineNumbers).toEqual([]); @@ -117,9 +99,7 @@ describe("replaceAndCalculateLocation", () => { it("should handle empty search text", () => { // Test with replaceAll false (default) const content1 = "line 1\nline 2\nline 3"; - const result1 = replaceAndCalculateLocation(content1, [ - { oldText: "", newText: "replaced" }, - ]); + const result1 = replaceAndCalculateLocation(content1, [{ oldText: "", newText: "replaced" }]); expect(result1.newContent).toBe(content1); expect(result1.lineNumbers).toEqual([]); @@ -142,9 +122,7 @@ describe("replaceAndCalculateLocation", () => { it("should handle single line content", () => { const content = "single line with text here"; - const result = replaceAndCalculateLocation(content, [ - { oldText: "text", newText: "replaced" }, - ]); + const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]); expect(result.newContent).toBe("single line with replaced here"); expect(result.lineNumbers).toEqual([0]); // Line 1 (0-based) @@ -183,9 +161,7 @@ describe("replaceAndCalculateLocation", () => { it("should handle very long lines", () => { const longLine = "a".repeat(10000) + "text" + "b".repeat(10000); const content = `line 1\n${longLine}\nline 3`; - const result = replaceAndCalculateLocation(content, [ - { oldText: "text", newText: "replaced" }, - ]); + const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]); expect(result.newContent).toBe( `line 1\n${"a".repeat(10000)}replaced${"b".repeat(10000)}\nline 3`, @@ -198,9 +174,7 @@ describe("replaceAndCalculateLocation", () => { lines[500] = "line with text"; const content = lines.join("\n"); - const result = replaceAndCalculateLocation(content, [ - { oldText: "text", newText: "replaced" }, - ]); + const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]); expect(result.lineNumbers).toEqual([500]); expect(result.newContent).includes("line with replaced"); @@ -224,9 +198,7 @@ describe("replaceAndCalculateLocation", () => { const result1 = replaceAndCalculateLocation(content, [ { oldText: "short", newText: "very very long text" }, ]); - expect(result1.newContent).toBe( - "very very long text\nmedium line\nlong line here", - ); + expect(result1.newContent).toBe("very very long text\nmedium line\nlong line here"); expect(result1.lineNumbers).toEqual([0]); // Replacing with shorter text @@ -260,9 +232,7 @@ describe("replaceAndCalculateLocation", () => { it("should handle replacement at exact end of line", () => { const content = "line ends with text\nnext line"; - const result = replaceAndCalculateLocation(content, [ - { oldText: "text", newText: "replaced" }, - ]); + const result = replaceAndCalculateLocation(content, [{ oldText: "text", newText: "replaced" }]); expect(result.newContent).toBe("line ends with replaced\nnext line"); expect(result.lineNumbers).toEqual([0]); @@ -302,9 +272,7 @@ describe("replaceAndCalculateLocation", () => { // - All "foo" occurrences (lines 1, 3, 5 in original) // - First "bar" occurrence (line 1 after first edit) // - "line 3" (line 2 after previous edits) - expect(result.newContent).toBe( - "line 1\nFOO BAR\nLINE THREE\nFOO baz\nline 5\nbar FOO\nline 7", - ); + expect(result.newContent).toBe("line 1\nFOO BAR\nLINE THREE\nFOO baz\nline 5\nbar FOO\nline 7"); // Line numbers reflect position in final content, deduplicated and sorted expect(result.lineNumbers).toEqual([ @@ -326,9 +294,7 @@ describe("replaceAndCalculateLocation", () => { // First edit replaces first "hello" only // Second edit replaces all "world" in the modified content // Third edit replaces remaining "hello"s in the modified content - expect(result.newContent).toBe( - "hi earth\ngreetings greetings\nearth greetings earth", - ); + expect(result.newContent).toBe("hi earth\ngreetings greetings\nearth greetings earth"); // Line numbers reflect position in final content, deduplicated and sorted expect(result.lineNumbers).toEqual([ @@ -340,17 +306,14 @@ describe("replaceAndCalculateLocation", () => { it("should handle complex scenarios with marker-like text", () => { // Test that our marker approach doesn't get confused by similar text - const content = - "line 1\n__MARKER__text\nline 3\n__REPLACE_MARKER_abc__\nline 5"; + const content = "line 1\n__MARKER__text\nline 3\n__REPLACE_MARKER_abc__\nline 5"; const result = replaceAndCalculateLocation(content, [ { oldText: "__MARKER__", newText: "REPLACED", replaceAll: true }, { oldText: "text", newText: "content" }, { oldText: "__REPLACE_MARKER_abc__", newText: "DONE" }, ]); - expect(result.newContent).toBe( - "line 1\nREPLACEDcontent\nline 3\nDONE\nline 5", - ); + expect(result.newContent).toBe("line 1\nREPLACEDcontent\nline 3\nDONE\nline 5"); // Line numbers should be correct even with marker-like text expect(result.lineNumbers).toEqual([ diff --git a/src/tools.ts b/src/tools.ts index cdaed7c0..868944d6 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -1,13 +1,10 @@ -import { ContentBlock } from "@modelcontextprotocol/sdk/types.js"; import { - Client, PlanEntry, ToolCallContent, ToolCallLocation, ToolKind, } from "@zed-industries/agent-client-protocol"; import { replaceAndCalculateLocation, SYSTEM_REMINDER } from "./mcp-server.js"; -import { tool } from "@anthropic-ai/claude-code"; interface ToolInfo { title: string; @@ -47,9 +44,7 @@ export function toolInfoFromToolUse( case "NotebookRead": return { - title: input?.notebook_path - ? `Read Notebook ${input.notebook_path}` - : "Read Notebook", + title: input?.notebook_path ? `Read Notebook ${input.notebook_path}` : "Read Notebook", kind: "read", content: [], locations: input?.notebook_path ? [{ path: input.notebook_path }] : [], @@ -57,9 +52,7 @@ export function toolInfoFromToolUse( case "NotebookEdit": return { - title: input?.notebook_path - ? `Edit Notebook ${input.notebook_path}` - : "Edit Notebook", + title: input?.notebook_path ? `Edit Notebook ${input.notebook_path}` : "Edit Notebook", kind: "edit", content: input && input.new_source @@ -76,9 +69,7 @@ export function toolInfoFromToolUse( case "Bash": case "mcp__acp__Bash": return { - title: input?.command - ? "`" + input.command.replaceAll("`", "\\`") + "`" - : "Terminal", + title: input?.command ? "`" + input.command.replaceAll("`", "\\`") + "`" : "Terminal", kind: "execute", content: input && input.description @@ -111,11 +102,7 @@ export function toolInfoFromToolUse( let limit = ""; if (input.limit) { limit = - " (" + - ((input.offset ?? 0) + 1) + - " - " + - ((input.offset ?? 0) + input.limit) + - ")"; + " (" + ((input.offset ?? 0) + 1) + " - " + ((input.offset ?? 0) + input.limit) + ")"; } else if (input.offset) { limit = " (from line " + (input.offset + 1) + ")"; } @@ -157,8 +144,8 @@ export function toolInfoFromToolUse( if (path && input.old_text) { try { - let oldContent = cachedFileContent[path] || ""; - let newContent = replaceAndCalculateLocation(oldContent, [ + const oldContent = cachedFileContent[path] || ""; + const newContent = replaceAndCalculateLocation(oldContent, [ { oldText: input.old_string, newText: input.new_string, @@ -191,8 +178,8 @@ export function toolInfoFromToolUse( } case "mcp__acp__multi-edit": - case "MultiEdit": - let multiInput = input as { + case "MultiEdit": { + const multiInput = input as { file_path: string; edits: { old_string: string; @@ -200,18 +187,14 @@ export function toolInfoFromToolUse( replace_all?: boolean; }[]; }; - let oldTextMulti = multiInput.edits - .map((edit: any) => edit.old_string) - .join("\n"); - let newTextMulti = multiInput.edits - .map((edit: any) => edit.new_string) - .join("\n"); + let oldTextMulti = multiInput.edits.map((edit: any) => edit.old_string).join("\n"); + let newTextMulti = multiInput.edits.map((edit: any) => edit.new_string).join("\n"); try { if (multiInput.edits && multiInput.file_path) { - let oldContent = + const oldContent = cachedFileContent[multiInput.file_path] || multiInput.edits.map((edit: any) => edit.oldText).join("\n"); - let newContent = replaceAndCalculateLocation( + const newContent = replaceAndCalculateLocation( oldContent, multiInput.edits.map((edit) => ({ oldText: edit.old_string, @@ -240,8 +223,8 @@ export function toolInfoFromToolUse( ], locations: input?.file_path ? [{ path: input.file_path }] : [], }; - - case "mcp__acp__write": + } + case "mcp__acp__write": { let content: ToolCallContent[] = []; if (input && input.abs_path) { content = [ @@ -266,6 +249,7 @@ export function toolInfoFromToolUse( content, locations: input?.abs_path ? [{ path: input.abs_path }] : [], }; + } case "Write": return { @@ -330,6 +314,7 @@ export function toolInfoFromToolUse( label += " -c"; break; case "Content": + default: break; } } @@ -439,10 +424,7 @@ export function toolInfoFromToolUse( } } -export function toolUpdateFromToolResult( - toolResult: any, - toolUse: any | undefined, -): ToolUpdate { +export function toolUpdateFromToolResult(toolResult: any, toolUse: any | undefined): ToolUpdate { switch (toolUse?.name) { case "mcp__acp__edit": case "edit": @@ -453,15 +435,14 @@ export function toolUpdateFromToolResult( const firstContent = toolResult.content[0]; const data = JSON.parse(firstContent.text || firstContent); if (data.lineNumbers && Array.isArray(data.lineNumbers)) { - const locations: ToolCallLocation[] = data.lineNumbers.map( - (line: number) => ({ - path: toolUse?.input?.abs_path || toolUse?.input?.file_path, - line: line, - }), - ); + const locations: ToolCallLocation[] = data.lineNumbers.map((line: number) => ({ + path: toolUse?.input?.abs_path || toolUse?.input?.file_path, + line: line, + })); return { locations }; } - } catch (e) { + } catch (error) { + console.error(`Error parsing edit tool result: ${error}`); // If parsing fails, return empty object return {}; } @@ -477,15 +458,15 @@ export function toolUpdateFromToolResult( const firstContent = toolResult.content[0]; const data = JSON.parse(firstContent.text || firstContent); if (data.lineNumbers && Array.isArray(data.lineNumbers)) { - const locations: ToolCallLocation[] = data.lineNumbers.map( - (line: number) => ({ - path: toolUse?.input?.file_path || toolUse?.input?.abs_path, - line: line, - }), - ); + const locations: ToolCallLocation[] = data.lineNumbers.map((line: number) => ({ + path: toolUse?.input?.file_path || toolUse?.input?.abs_path, + line: line, + })); return { locations }; } - } catch (e) { + } catch (error) { + console.error(`Error parsing edit tool result: ${error}`); + // If parsing fails, return empty object return {}; } @@ -504,29 +485,22 @@ export function toolUpdateFromToolResult( content: toolResult.content.map((content: any) => ({ type: "content", content: - content.type == "text" + content.type === "text" ? { type: "text", - text: markdownEscape( - content.text.replace(SYSTEM_REMINDER, ""), - ), + text: markdownEscape(content.text.replace(SYSTEM_REMINDER, "")), } : content, })), }; - } else if ( - typeof toolResult.content === "string" && - toolResult.content.length > 0 - ) { + } else if (typeof toolResult.content === "string" && toolResult.content.length > 0) { return { content: [ { type: "content", content: { type: "text", - text: markdownEscape( - toolResult.content.replace(SYSTEM_REMINDER, ""), - ), + text: markdownEscape(toolResult.content.replace(SYSTEM_REMINDER, "")), }, }, ], @@ -560,10 +534,7 @@ export function toolUpdateFromToolResult( content, })), }; - } else if ( - typeof toolResult.content === "string" && - toolResult.content.length > 0 - ) { + } else if (typeof toolResult.content === "string" && toolResult.content.length > 0) { return { content: [ { @@ -597,7 +568,7 @@ export function planEntries(input: { todos: ClaudePlanEntry[] }): PlanEntry[] { export function markdownEscape(text: string): string { let escape = "```"; - for (let [m] of text.matchAll(/^```+/gm)) { + for (const [m] of text.matchAll(/^```+/gm)) { while (m.length >= escape.length) { escape += "`"; } diff --git a/src/utils.ts b/src/utils.ts index 0659f716..008ddd12 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -45,24 +45,23 @@ export class Pushable implements AsyncIterable { } // Helper to convert Node.js streams to Web Streams -export function nodeToWebWritable( - nodeStream: Writable, -): WritableStream { +export function nodeToWebWritable(nodeStream: Writable): WritableStream { return new WritableStream({ write(chunk) { return new Promise((resolve, reject) => { nodeStream.write(Buffer.from(chunk), (err) => { - if (err) reject(err); - else resolve(); + if (err) { + reject(err); + } else { + resolve(); + } }); }); }, }); } -export function nodeToWebReadable( - nodeStream: Readable, -): ReadableStream { +export function nodeToWebReadable(nodeStream: Readable): ReadableStream { return new ReadableStream({ start(controller) { nodeStream.on("data", (chunk: Buffer) => {