feat: upgrade ai sdk provider claude code (#1398)

This commit is contained in:
Ralph Khreish
2025-11-12 15:43:52 +01:00
committed by GitHub
parent 9883e83b78
commit e59c16c707
5 changed files with 361 additions and 27 deletions

View File

@@ -0,0 +1,24 @@
---
"task-master-ai": minor
---
Claude Code provider now respects your global, project, and local Claude Code configuration files.
When using the Claude Code AI provider, Task Master now automatically loads your Claude Code settings from:
- **Global config** (`~/.claude/` directory) - Your personal preferences across all projects
- **Project config** (`.claude/` directory) - Project-specific settings like CLAUDE.md instructions
- **Local config** - Workspace-specific overrides
This means your CLAUDE.md files, custom instructions, and Claude Code settings will now be properly applied when Task Master uses Claude Code as an AI provider. Previously, these settings were being ignored.
**What's improved:**
- ✅ CLAUDE.md files are now automatically loaded and applied (global and local)
- ✅ Your custom Claude Code settings are respected
- ✅ Project-specific instructions work as expected
- ✅ No manual configuration needed - works out of the box
**Issues:**
- Resolves #1391
- Resolves #1315

213
package-lock.json generated
View File

@@ -33,7 +33,7 @@
"@streamparser/json": "^0.0.22", "@streamparser/json": "^0.0.22",
"@supabase/supabase-js": "^2.57.4", "@supabase/supabase-js": "^2.57.4",
"ai": "^5.0.51", "ai": "^5.0.51",
"ai-sdk-provider-claude-code": "^1.1.4", "ai-sdk-provider-claude-code": "^2.1.0",
"ai-sdk-provider-codex-cli": "^0.3.0", "ai-sdk-provider-codex-cli": "^0.3.0",
"ai-sdk-provider-gemini-cli": "^1.1.1", "ai-sdk-provider-gemini-cli": "^1.1.1",
"ajv": "^8.17.1", "ajv": "^8.17.1",
@@ -68,7 +68,7 @@
"steno": "^4.0.2", "steno": "^4.0.2",
"undici": "^7.16.0", "undici": "^7.16.0",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"zod": "^4.1.11" "zod": "^4.1.12"
}, },
"bin": { "bin": {
"task-master": "dist/task-master.js", "task-master": "dist/task-master.js",
@@ -4110,6 +4110,28 @@
"@img/sharp-libvips-darwin-arm64": "1.0.4" "@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": { "node_modules/@img/sharp-libvips-darwin-arm64": {
"version": "1.0.4", "version": "1.0.4",
"cpu": [ "cpu": [
@@ -4124,6 +4146,155 @@
"url": "https://opencollective.com/libvips" "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/ansi": { "node_modules/@inquirer/ansi": {
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
@@ -10289,12 +10460,14 @@
} }
}, },
"node_modules/ai-sdk-provider-claude-code": { "node_modules/ai-sdk-provider-claude-code": {
"version": "1.1.4", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/ai-sdk-provider-claude-code/-/ai-sdk-provider-claude-code-2.1.0.tgz",
"integrity": "sha512-g+Nb+TKQs7RMP8PEn7NBjEHwVyIqHthXKSUqADO37oGPwOeMATuWCcuYAFmbeSdpIirV6iKRXRhdFipv2yTzqQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@ai-sdk/provider": "2.0.0", "@ai-sdk/provider": "2.0.0",
"@ai-sdk/provider-utils": "3.0.3", "@ai-sdk/provider-utils": "3.0.9",
"@anthropic-ai/claude-code": "1.0.94", "@anthropic-ai/claude-agent-sdk": "^0.1.20",
"jsonc-parser": "^3.3.1" "jsonc-parser": "^3.3.1"
}, },
"engines": { "engines": {
@@ -10305,13 +10478,14 @@
} }
}, },
"node_modules/ai-sdk-provider-claude-code/node_modules/@ai-sdk/provider-utils": { "node_modules/ai-sdk-provider-claude-code/node_modules/@ai-sdk/provider-utils": {
"version": "3.0.3", "version": "3.0.9",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.9.tgz",
"integrity": "sha512-Pm571x5efqaI4hf9yW4KsVlDBDme8++UepZRnq+kqVBWWjgvGhQlzU8glaFq0YJEB9kkxZHbRRyVeHoV2sRYaQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@ai-sdk/provider": "2.0.0", "@ai-sdk/provider": "2.0.0",
"@standard-schema/spec": "^1.0.0", "@standard-schema/spec": "^1.0.0",
"eventsource-parser": "^3.0.3", "eventsource-parser": "^3.0.5"
"zod-to-json-schema": "^3.24.1"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
@@ -10320,19 +10494,11 @@
"zod": "^3.25.76 || ^4" "zod": "^3.25.76 || ^4"
} }
}, },
"node_modules/ai-sdk-provider-claude-code/node_modules/@ai-sdk/provider-utils/node_modules/zod-to-json-schema": { "node_modules/ai-sdk-provider-claude-code/node_modules/@anthropic-ai/claude-agent-sdk": {
"version": "3.24.6", "version": "0.1.22",
"license": "ISC", "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.1.22.tgz",
"peerDependencies": { "integrity": "sha512-lDwbcwh5goRVUHRRqvX9v6Vj38VME1L+QIaLWzdqbK3Lwkkz9PVLNQFRDEyUOSNd9Fm4cAlfQZ0RCVe8tuo9TA==",
"zod": "^3.24.1"
}
},
"node_modules/ai-sdk-provider-claude-code/node_modules/@anthropic-ai/claude-code": {
"version": "1.0.94",
"license": "SEE LICENSE IN README.md", "license": "SEE LICENSE IN README.md",
"bin": {
"claude": "cli.js"
},
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
}, },
@@ -10343,6 +10509,9 @@
"@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5",
"@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5",
"@img/sharp-win32-x64": "^0.33.5" "@img/sharp-win32-x64": "^0.33.5"
},
"peerDependencies": {
"zod": "^3.24.1"
} }
}, },
"node_modules/ai-sdk-provider-codex-cli": { "node_modules/ai-sdk-provider-codex-cli": {
@@ -27843,7 +28012,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/zod": { "node_modules/zod": {
"version": "4.1.11", "version": "4.1.12",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"

View File

@@ -71,7 +71,7 @@
"@streamparser/json": "^0.0.22", "@streamparser/json": "^0.0.22",
"@supabase/supabase-js": "^2.57.4", "@supabase/supabase-js": "^2.57.4",
"ai": "^5.0.51", "ai": "^5.0.51",
"ai-sdk-provider-claude-code": "^1.1.4", "ai-sdk-provider-claude-code": "^2.1.0",
"ai-sdk-provider-codex-cli": "^0.3.0", "ai-sdk-provider-codex-cli": "^0.3.0",
"ai-sdk-provider-gemini-cli": "^1.1.1", "ai-sdk-provider-gemini-cli": "^1.1.1",
"ajv": "^8.17.1", "ajv": "^8.17.1",
@@ -106,7 +106,7 @@
"steno": "^4.0.2", "steno": "^4.0.2",
"undici": "^7.16.0", "undici": "^7.16.0",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"zod": "^4.1.11" "zod": "^4.1.12"
}, },
"optionalDependencies": { "optionalDependencies": {
"@anthropic-ai/claude-code": "^1.0.88", "@anthropic-ai/claude-code": "^1.0.88",

View File

@@ -2,12 +2,13 @@
* src/ai-providers/claude-code.js * src/ai-providers/claude-code.js
* *
* Claude Code provider implementation using the ai-sdk-provider-claude-code package. * Claude Code provider implementation using the ai-sdk-provider-claude-code package.
* This provider uses the local Claude Code CLI with OAuth token authentication. * This provider uses the local Claude Agent SDK (via Claude Code CLI) with OAuth token authentication.
* *
* Authentication: * Authentication:
* - Uses CLAUDE_CODE_OAUTH_TOKEN managed by Claude Code CLI * - Uses CLAUDE_CODE_OAUTH_TOKEN managed by Claude Code CLI
* - Token is set up via: claude setup-token * - Token is set up via: claude setup-token
* - No manual API key configuration required * - No manual API key configuration required
*
*/ */
import { createClaudeCode } from 'ai-sdk-provider-claude-code'; import { createClaudeCode } from 'ai-sdk-provider-claude-code';
@@ -104,9 +105,42 @@ export class ClaudeCodeProvider extends BaseAIProvider {
const settings = const settings =
getClaudeCodeSettingsForCommand(params.commandName) || {}; getClaudeCodeSettingsForCommand(params.commandName) || {};
return createClaudeCode({ // Environment variable isolation to prevent API key conflicts
defaultSettings: settings // The ai-sdk-provider-claude-code SDK automatically picks up ANTHROPIC_API_KEY,
}); // which can cause conflicts if that key is intended for the Anthropic provider.
const originalAnthropicKey = process.env.ANTHROPIC_API_KEY;
const claudeCodeKey = process.env.CLAUDE_CODE_API_KEY;
try {
// If CLAUDE_CODE_API_KEY is set, use it exclusively
if (claudeCodeKey) {
process.env.ANTHROPIC_API_KEY = claudeCodeKey;
} else if (originalAnthropicKey) {
// If only ANTHROPIC_API_KEY exists, temporarily unset it to force OAuth mode
delete process.env.ANTHROPIC_API_KEY;
}
return createClaudeCode({
defaultSettings: {
// Restore previous default behavior from pre-2.0 versions
// These must be inside defaultSettings to be applied by the provider
systemPrompt: {
type: 'preset',
preset: 'claude_code'
},
// Enable loading of CLAUDE.md and settings.json files
settingSources: ['user', 'project', 'local'],
...settings
}
});
} finally {
// Restore original environment state
if (originalAnthropicKey) {
process.env.ANTHROPIC_API_KEY = originalAnthropicKey;
} else {
delete process.env.ANTHROPIC_API_KEY;
}
}
} catch (error) { } catch (error) {
// Provide more helpful error message // Provide more helpful error message
const msg = String(error?.message || ''); const msg = String(error?.message || '');

View File

@@ -99,6 +99,111 @@ describe('ClaudeCodeProvider', () => {
expect(client.chat).toBeDefined(); expect(client.chat).toBeDefined();
expect(client.chat).toBe(client.languageModel); expect(client.chat).toBe(client.languageModel);
}); });
it('should pass systemPrompt configuration to createClaudeCode', async () => {
const { createClaudeCode } = await import('ai-sdk-provider-claude-code');
provider.getClient({});
expect(createClaudeCode).toHaveBeenCalledWith(
expect.objectContaining({
defaultSettings: expect.objectContaining({
systemPrompt: {
type: 'preset',
preset: 'claude_code'
}
})
})
);
});
it('should pass settingSources configuration to createClaudeCode', async () => {
const { createClaudeCode } = await import('ai-sdk-provider-claude-code');
provider.getClient({});
expect(createClaudeCode).toHaveBeenCalledWith(
expect.objectContaining({
defaultSettings: expect.objectContaining({
settingSources: ['user', 'project', 'local']
})
})
);
});
it('should pass defaultSettings from config to createClaudeCode', async () => {
const { createClaudeCode } = await import('ai-sdk-provider-claude-code');
const { getClaudeCodeSettingsForCommand } = await import(
'../../../scripts/modules/config-manager.js'
);
const mockSettings = { maxTokens: 4096, temperature: 0.7 };
getClaudeCodeSettingsForCommand.mockReturnValueOnce(mockSettings);
provider.getClient({ commandName: 'test-command' });
expect(createClaudeCode).toHaveBeenCalledWith(
expect.objectContaining({
defaultSettings: expect.objectContaining({
...mockSettings,
systemPrompt: {
type: 'preset',
preset: 'claude_code'
},
settingSources: ['user', 'project', 'local']
})
})
);
});
it('should pass complete configuration object to createClaudeCode', async () => {
const { createClaudeCode } = await import('ai-sdk-provider-claude-code');
const { getClaudeCodeSettingsForCommand } = await import(
'../../../scripts/modules/config-manager.js'
);
const mockSettings = { maxTokens: 2048 };
getClaudeCodeSettingsForCommand.mockReturnValueOnce(mockSettings);
provider.getClient({ commandName: 'analyze' });
// Verify the complete configuration structure matches v2.0 migration requirements
expect(createClaudeCode).toHaveBeenCalledWith({
defaultSettings: {
...mockSettings,
// Restores pre-2.0 behavior: explicit system prompt preset
systemPrompt: {
type: 'preset',
preset: 'claude_code'
},
// Restores pre-2.0 behavior: enables loading of CLAUDE.md and settings.json
settingSources: ['user', 'project', 'local']
}
});
});
it('should pass empty defaultSettings when config returns null', async () => {
const { createClaudeCode } = await import('ai-sdk-provider-claude-code');
const { getClaudeCodeSettingsForCommand } = await import(
'../../../scripts/modules/config-manager.js'
);
getClaudeCodeSettingsForCommand.mockReturnValueOnce(null);
provider.getClient({});
expect(createClaudeCode).toHaveBeenCalledWith(
expect.objectContaining({
defaultSettings: expect.objectContaining({
systemPrompt: {
type: 'preset',
preset: 'claude_code'
},
settingSources: ['user', 'project', 'local']
})
})
);
});
}); });
describe('model support', () => { describe('model support', () => {