From 72a324075ca24ceca5ee1be1a9066233e36554ea Mon Sep 17 00:00:00 2001 From: Ben Vargas Date: Tue, 17 Jun 2025 11:54:59 -0600 Subject: [PATCH] feat: make @anthropic-ai/claude-code an optional dependency This change makes the Claude Code SDK package optional, preventing installation failures for users who don't need Claude Code functionality. Changes: - Added @anthropic-ai/claude-code to optionalDependencies in package.json - Implemented lazy loading in language-model.js to only import the SDK when actually used - Updated documentation to explain the optional installation requirement - Applied formatting fixes to ensure code consistency Benefits: - Users without Claude Code subscriptions don't need to install the dependency - Reduces package size for users who don't use Claude Code - Prevents installation failures if the package is unavailable - Provides clear error messages when the package is needed but not installed The implementation uses dynamic imports to load the SDK only when doGenerate() or doStream() is called, ensuring the provider can be instantiated without the package present. --- docs/examples/claude-code-usage.md | 6 +- package-lock.json | 284 ++++++++++++++++-- package.json | 3 + scripts/init.js | 2 +- scripts/modules/update-config-tokens.js | 20 +- src/ai-providers/claude-code.js | 2 +- .../custom-sdk/claude-code/errors.js | 2 +- .../custom-sdk/claude-code/index.js | 2 +- .../custom-sdk/claude-code/json-extractor.js | 2 +- .../custom-sdk/claude-code/language-model.js | 34 ++- .../claude-code/message-converter.js | 2 +- .../custom-sdk/claude-code/types.js | 2 +- 12 files changed, 321 insertions(+), 40 deletions(-) diff --git a/docs/examples/claude-code-usage.md b/docs/examples/claude-code-usage.md index 3e46a476..f8e6c69c 100644 --- a/docs/examples/claude-code-usage.md +++ b/docs/examples/claude-code-usage.md @@ -60,7 +60,11 @@ task-master set-status --id=task-001 --status=in-progress ## Requirements 1. Claude Code CLI must be installed and authenticated on your system -2. No API key is required in your environment variables or MCP configuration +2. Install the optional `@anthropic-ai/claude-code` package if you enable this provider: + ```bash + npm install @anthropic-ai/claude-code + ``` +3. No API key is required in your environment variables or MCP configuration ## Advanced Settings diff --git a/package-lock.json b/package-lock.json index 4cec0fc9..0e915610 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,6 +68,9 @@ }, "engines": { "node": ">=18.0.0" + }, + "optionalDependencies": { + "@anthropic-ai/claude-code": "^1.0.25" } }, "node_modules/@ai-sdk/amazon-bedrock": { @@ -446,6 +449,28 @@ "node": ">=6.0.0" } }, + "node_modules/@anthropic-ai/claude-code": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-1.0.25.tgz", + "integrity": "sha512-5p4FLlFO4TuRf0zV0axiOxiAkUC8eer0lqJi/A/pA46LESv31Alw6xaNYgwQVkP6oSbP5PydK36u7YrB9QSaXQ==", + "hasInstallScript": true, + "license": "SEE LICENSE IN README.md", + "optional": true, + "bin": { + "claude": "cli.js" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "^0.33.5", + "@img/sharp-darwin-x64": "^0.33.5", + "@img/sharp-linux-arm": "^0.33.5", + "@img/sharp-linux-arm64": "^0.33.5", + "@img/sharp-linux-x64": "^0.33.5", + "@img/sharp-win32-x64": "^0.33.5" + } + }, "node_modules/@anthropic-ai/sdk": { "version": "0.39.0", "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.39.0.tgz", @@ -2651,6 +2676,215 @@ "node": ">=18" } }, + "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", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@inquirer/checkbox": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.4.tgz", @@ -3868,6 +4102,19 @@ "node": ">= 0.6" } }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3966,6 +4213,16 @@ "node": ">=8.0.0" } }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", @@ -5328,9 +5585,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "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": { @@ -7159,16 +7416,19 @@ } }, "node_modules/formidable": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz", - "integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, "license": "MIT", "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", - "hexoid": "^2.0.0", "once": "^1.4.0" }, + "engines": { + "node": ">=14.0.0" + }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } @@ -7672,16 +7932,6 @@ "node": ">=18.0.0" } }, - "node_modules/hexoid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", - "integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", diff --git a/package.json b/package.json index f0dba5db..91e47874 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,9 @@ "uuid": "^11.1.0", "zod": "^3.23.8" }, + "optionalDependencies": { + "@anthropic-ai/claude-code": "^1.0.25" + }, "engines": { "node": ">=18.0.0" }, diff --git a/scripts/init.js b/scripts/init.js index 9f938249..f3b4a202 100755 --- a/scripts/init.js +++ b/scripts/init.js @@ -507,7 +507,7 @@ function createProjectStructure( ...replacements } ); - + // Update config.json with correct maxTokens values from supported-models.json const configPath = path.join(targetDir, TASKMASTER_CONFIG_FILE); if (updateConfigMaxTokens(configPath)) { diff --git a/scripts/modules/update-config-tokens.js b/scripts/modules/update-config-tokens.js index 403bf35c..14e68b2d 100644 --- a/scripts/modules/update-config-tokens.js +++ b/scripts/modules/update-config-tokens.js @@ -20,29 +20,33 @@ export function updateConfigMaxTokens(configPath) { try { // Load supported models const supportedModelsPath = path.join(__dirname, 'supported-models.json'); - const supportedModels = JSON.parse(fs.readFileSync(supportedModelsPath, 'utf-8')); - + const supportedModels = JSON.parse( + fs.readFileSync(supportedModelsPath, 'utf-8') + ); + // Load config const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); - + // Update each role's maxTokens if the model exists in supported-models.json const roles = ['main', 'research', 'fallback']; - + for (const role of roles) { if (config.models && config.models[role]) { const provider = config.models[role].provider; const modelId = config.models[role].modelId; - + // Find the model in supported models if (supportedModels[provider]) { - const modelData = supportedModels[provider].find(m => m.id === modelId); + const modelData = supportedModels[provider].find( + (m) => m.id === modelId + ); if (modelData && modelData.max_tokens) { config.models[role].maxTokens = modelData.max_tokens; } } } } - + // Write back the updated config fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); return true; @@ -50,4 +54,4 @@ export function updateConfigMaxTokens(configPath) { console.error('Error updating config maxTokens:', error.message); return false; } -} \ No newline at end of file +} diff --git a/src/ai-providers/claude-code.js b/src/ai-providers/claude-code.js index 842f847e..c84ff439 100644 --- a/src/ai-providers/claude-code.js +++ b/src/ai-providers/claude-code.js @@ -44,4 +44,4 @@ export class ClaudeCodeProvider extends BaseAIProvider { this.handleError('client initialization', error); } } -} \ No newline at end of file +} diff --git a/src/ai-providers/custom-sdk/claude-code/errors.js b/src/ai-providers/custom-sdk/claude-code/errors.js index b2d217e0..a0251f37 100644 --- a/src/ai-providers/custom-sdk/claude-code/errors.js +++ b/src/ai-providers/custom-sdk/claude-code/errors.js @@ -123,4 +123,4 @@ export function getErrorMetadata(error) { return /** @type {ClaudeCodeErrorMetadata} */ (error.data); } return undefined; -} \ No newline at end of file +} diff --git a/src/ai-providers/custom-sdk/claude-code/index.js b/src/ai-providers/custom-sdk/claude-code/index.js index ea116bd5..076a2241 100644 --- a/src/ai-providers/custom-sdk/claude-code/index.js +++ b/src/ai-providers/custom-sdk/claude-code/index.js @@ -80,4 +80,4 @@ export { createAPICallError, createAuthenticationError, createTimeoutError -} from './errors.js'; \ No newline at end of file +} from './errors.js'; diff --git a/src/ai-providers/custom-sdk/claude-code/json-extractor.js b/src/ai-providers/custom-sdk/claude-code/json-extractor.js index ff0d884c..335fff82 100644 --- a/src/ai-providers/custom-sdk/claude-code/json-extractor.js +++ b/src/ai-providers/custom-sdk/claude-code/json-extractor.js @@ -56,4 +56,4 @@ export function extractJson(text) { return text; } } -} \ No newline at end of file +} diff --git a/src/ai-providers/custom-sdk/claude-code/language-model.js b/src/ai-providers/custom-sdk/claude-code/language-model.js index ab1172f7..933127c8 100644 --- a/src/ai-providers/custom-sdk/claude-code/language-model.js +++ b/src/ai-providers/custom-sdk/claude-code/language-model.js @@ -7,7 +7,23 @@ import { generateId } from '@ai-sdk/provider-utils'; import { convertToClaudeCodeMessages } from './message-converter.js'; import { extractJson } from './json-extractor.js'; import { createAPICallError, createAuthenticationError } from './errors.js'; -import { query, AbortError } from '@anthropic-ai/claude-code'; + +let query; +let AbortError; + +async function loadClaudeCodeModule() { + if (!query || !AbortError) { + try { + const mod = await import('@anthropic-ai/claude-code'); + query = mod.query; + AbortError = mod.AbortError; + } catch (err) { + throw new Error( + "Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider." + ); + } + } +} /** * @typedef {import('./types.js').ClaudeCodeSettings} ClaudeCodeSettings @@ -78,7 +94,8 @@ export class ClaudeCodeLanguageModel { const unsupportedParams = []; // Check for unsupported parameters - if (options.temperature !== undefined) unsupportedParams.push('temperature'); + if (options.temperature !== undefined) + unsupportedParams.push('temperature'); if (options.maxTokens !== undefined) unsupportedParams.push('maxTokens'); if (options.topP !== undefined) unsupportedParams.push('topP'); if (options.topK !== undefined) unsupportedParams.push('topK'); @@ -110,6 +127,7 @@ export class ClaudeCodeLanguageModel { * @returns {Promise} */ async doGenerate(options) { + await loadClaudeCodeModule(); const { messagesPrompt } = convertToClaudeCodeMessages( options.prompt, options.mode @@ -188,9 +206,7 @@ export class ClaudeCodeLanguageModel { } } catch (error) { if (error instanceof AbortError) { - throw options.abortSignal?.aborted - ? options.abortSignal.reason - : error; + throw options.abortSignal?.aborted ? options.abortSignal.reason : error; } // Check for authentication errors @@ -256,6 +272,7 @@ export class ClaudeCodeLanguageModel { * @returns {Promise} */ async doStream(options) { + await loadClaudeCodeModule(); const { messagesPrompt } = convertToClaudeCodeMessages( options.prompt, options.mode @@ -368,7 +385,10 @@ export class ClaudeCodeLanguageModel { } } }); - } else if (message.type === 'system' && message.subtype === 'init') { + } else if ( + message.type === 'system' && + message.subtype === 'init' + ) { // Store session ID for future use this.sessionId = message.session_id; @@ -435,4 +455,4 @@ export class ClaudeCodeLanguageModel { } }; } -} \ No newline at end of file +} diff --git a/src/ai-providers/custom-sdk/claude-code/message-converter.js b/src/ai-providers/custom-sdk/claude-code/message-converter.js index a92a3a23..7bad0418 100644 --- a/src/ai-providers/custom-sdk/claude-code/message-converter.js +++ b/src/ai-providers/custom-sdk/claude-code/message-converter.js @@ -136,4 +136,4 @@ Begin your response with { and end with }`; messagesPrompt: finalPrompt, systemPrompt }; -} \ No newline at end of file +} diff --git a/src/ai-providers/custom-sdk/claude-code/types.js b/src/ai-providers/custom-sdk/claude-code/types.js index 4aad25a3..130d1f65 100644 --- a/src/ai-providers/custom-sdk/claude-code/types.js +++ b/src/ai-providers/custom-sdk/claude-code/types.js @@ -70,4 +70,4 @@ * @property {ClaudeCodeSettings} [defaultSettings] - Default settings to use for all models */ -export {}; // This ensures the file is treated as a module \ No newline at end of file +export {}; // This ensures the file is treated as a module