Compare commits
5 Commits
extension@
...
ralph/fix/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d482bee358 | ||
|
|
fa5de356da | ||
|
|
ade9f94e73 | ||
|
|
89335578ff | ||
|
|
781b8ef2af |
8
.changeset/quiet-owls-fix-cli-sdk.md
Normal file
8
.changeset/quiet-owls-fix-cli-sdk.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
fix(claude-code): prevent crash/hang when the optional `@anthropic-ai/claude-code` SDK is missing by guarding `AbortError instanceof` checks and adding explicit SDK presence checks in `doGenerate`/`doStream`. Also bump the optional dependency to `^1.0.88` for improved export consistency.
|
||||
|
||||
Related to JSON truncation handling in #920; this change addresses a separate error-path crash reported in #1142.
|
||||
|
||||
5
.changeset/rich-emus-say.md
Normal file
5
.changeset/rich-emus-say.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": patch
|
||||
---
|
||||
|
||||
Temporarily disable streaming for improved model compatibility - will be re-enabled in upcoming release
|
||||
@@ -33,6 +33,6 @@ This issue will be automatically closed as a duplicate in 3 days.
|
||||
- If your issue is a duplicate, please close it and 👍 the existing issue instead
|
||||
- To prevent auto-closure, add a comment or 👎 this comment
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
🤖 Generated with \[Task Master Bot\]
|
||||
|
||||
---
|
||||
@@ -9,17 +9,9 @@
|
||||
"engines": {
|
||||
"vscode": "^1.93.0"
|
||||
},
|
||||
"categories": [
|
||||
"AI",
|
||||
"Visualization",
|
||||
"Education",
|
||||
"Other"
|
||||
],
|
||||
"categories": ["AI", "Visualization", "Education", "Other"],
|
||||
"main": "./dist/extension.js",
|
||||
"activationEvents": [
|
||||
"onStartupFinished",
|
||||
"workspaceContains:.taskmaster/**"
|
||||
],
|
||||
"activationEvents": ["onStartupFinished", "workspaceContains:.taskmaster/**"],
|
||||
"contributes": {
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
@@ -147,11 +139,7 @@
|
||||
},
|
||||
"taskmaster.ui.theme": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"auto",
|
||||
"light",
|
||||
"dark"
|
||||
],
|
||||
"enum": ["auto", "light", "dark"],
|
||||
"default": "auto",
|
||||
"description": "UI theme preference"
|
||||
},
|
||||
@@ -212,12 +200,7 @@
|
||||
},
|
||||
"taskmaster.debug.logLevel": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"error",
|
||||
"warn",
|
||||
"info",
|
||||
"debug"
|
||||
],
|
||||
"enum": ["error", "warn", "info", "debug"],
|
||||
"default": "info",
|
||||
"description": "Logging level"
|
||||
},
|
||||
|
||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "task-master-ai",
|
||||
"version": "0.24.0",
|
||||
"version": "0.25.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "task-master-ai",
|
||||
"version": "0.24.0",
|
||||
"version": "0.25.0",
|
||||
"license": "MIT WITH Commons-Clause",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
@@ -81,21 +81,21 @@
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@anthropic-ai/claude-code": "^1.0.25",
|
||||
"@anthropic-ai/claude-code": "^1.0.88",
|
||||
"@biomejs/cli-linux-x64": "^1.9.4",
|
||||
"ai-sdk-provider-gemini-cli": "^0.1.1"
|
||||
}
|
||||
},
|
||||
"apps/docs": {
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.1",
|
||||
"devDependencies": {
|
||||
"mintlify": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"apps/extension": {
|
||||
"version": "0.23.1",
|
||||
"version": "0.24.0",
|
||||
"dependencies": {
|
||||
"task-master-ai": "0.24.0"
|
||||
"task-master-ai": "0.25.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
@@ -2046,10 +2046,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-code": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-1.0.34.tgz",
|
||||
"integrity": "sha512-9mQd8hodE5/RxZnsWUCdLzqGUKuCzBczrfc2QfxrNSlvUFpOgTzjT1Zlww2vW9v0K1e5K9g1o08apqPl/QPmpw==",
|
||||
"hasInstallScript": true,
|
||||
"version": "1.0.88",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-1.0.88.tgz",
|
||||
"integrity": "sha512-Np6H4EjkbmNolUpx98DvqLXV/iJrw2y7dz2rDJ7av9ajMz6HZfB8bdJV5D75+jO+Gk1pvA54HCIm0c65lDrzcw==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
|
||||
@@ -9,10 +9,7 @@
|
||||
"task-master-mcp": "mcp-server/server.js",
|
||||
"task-master-ai": "mcp-server/server.js"
|
||||
},
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"."
|
||||
],
|
||||
"workspaces": ["apps/*", "."],
|
||||
"scripts": {
|
||||
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
||||
"test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",
|
||||
@@ -89,7 +86,7 @@
|
||||
"zod-to-json-schema": "^3.24.5"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@anthropic-ai/claude-code": "^1.0.25",
|
||||
"@anthropic-ai/claude-code": "^1.0.88",
|
||||
"@biomejs/cli-linux-x64": "^1.9.4",
|
||||
"ai-sdk-provider-gemini-cli": "^0.1.1"
|
||||
},
|
||||
|
||||
@@ -63,8 +63,15 @@ export class PrdParseConfig {
|
||||
this.targetTag = this.tag || getCurrentTag(this.projectRoot) || 'master';
|
||||
this.isMCP = !!this.mcpLog;
|
||||
this.outputFormat = this.isMCP && !this.reportProgress ? 'json' : 'text';
|
||||
|
||||
// Feature flag: Temporarily disable streaming, use generateObject instead
|
||||
// TODO: Re-enable streaming once issues are resolved
|
||||
const ENABLE_STREAMING = false;
|
||||
|
||||
this.useStreaming =
|
||||
typeof this.reportProgress === 'function' || this.outputFormat === 'text';
|
||||
ENABLE_STREAMING &&
|
||||
(typeof this.reportProgress === 'function' ||
|
||||
this.outputFormat === 'text');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -169,6 +169,11 @@ export class ClaudeCodeLanguageModel {
|
||||
const warnings = this.generateUnsupportedWarnings(options);
|
||||
|
||||
try {
|
||||
if (!query) {
|
||||
throw new Error(
|
||||
"Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."
|
||||
);
|
||||
}
|
||||
const response = query({
|
||||
prompt: messagesPrompt,
|
||||
options: queryOptions
|
||||
@@ -227,7 +232,7 @@ export class ClaudeCodeLanguageModel {
|
||||
finishReason = 'truncated';
|
||||
// Skip re-throwing: fall through so the caller receives usable data
|
||||
} else {
|
||||
if (error instanceof AbortError) {
|
||||
if (AbortError && error instanceof AbortError) {
|
||||
throw options.abortSignal?.aborted
|
||||
? options.abortSignal.reason
|
||||
: error;
|
||||
@@ -335,6 +340,11 @@ export class ClaudeCodeLanguageModel {
|
||||
const stream = new ReadableStream({
|
||||
start: async (controller) => {
|
||||
try {
|
||||
if (!query) {
|
||||
throw new Error(
|
||||
"Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."
|
||||
);
|
||||
}
|
||||
const response = query({
|
||||
prompt: messagesPrompt,
|
||||
options: queryOptions
|
||||
@@ -478,7 +488,7 @@ export class ClaudeCodeLanguageModel {
|
||||
} catch (error) {
|
||||
let errorToEmit;
|
||||
|
||||
if (error instanceof AbortError) {
|
||||
if (AbortError && error instanceof AbortError) {
|
||||
errorToEmit = options.abortSignal?.aborted
|
||||
? options.abortSignal.reason
|
||||
: error;
|
||||
|
||||
@@ -795,7 +795,7 @@ describe('parsePRD', () => {
|
||||
});
|
||||
|
||||
describe('Streaming vs Non-Streaming Modes', () => {
|
||||
test('should use streaming when reportProgress function is provided', async () => {
|
||||
test('should use non-streaming when reportProgress function is provided (streaming disabled)', async () => {
|
||||
// Setup mocks to simulate normal conditions (no existing output file)
|
||||
fs.default.existsSync.mockImplementation((path) => {
|
||||
if (path === 'tasks/tasks.json') return false; // Output file doesn't exist
|
||||
@@ -815,23 +815,20 @@ describe('parsePRD', () => {
|
||||
};
|
||||
JSONParser.mockReturnValue(mockParser);
|
||||
|
||||
// Call the function with reportProgress to trigger streaming path
|
||||
// Call the function with reportProgress - with streaming disabled, should use non-streaming
|
||||
const result = await parsePRD('path/to/prd.txt', 'tasks/tasks.json', 3, {
|
||||
reportProgress: mockReportProgress
|
||||
});
|
||||
|
||||
// Verify streamObjectService was called (streaming path)
|
||||
expect(streamObjectService).toHaveBeenCalled();
|
||||
// With streaming disabled, should use generateObjectService instead
|
||||
expect(generateObjectService).toHaveBeenCalled();
|
||||
|
||||
// Verify generateObjectService was NOT called (non-streaming path)
|
||||
expect(generateObjectService).not.toHaveBeenCalled();
|
||||
// Verify streamObjectService was NOT called (streaming is disabled)
|
||||
expect(streamObjectService).not.toHaveBeenCalled();
|
||||
|
||||
// Verify progress reporting was called
|
||||
// Verify progress reporting was still called
|
||||
expect(mockReportProgress).toHaveBeenCalled();
|
||||
|
||||
// We no longer use parseStream with streamObject
|
||||
// expect(parseStream).toHaveBeenCalled();
|
||||
|
||||
// Verify result structure
|
||||
expect(result).toEqual({
|
||||
success: true,
|
||||
@@ -840,7 +837,7 @@ describe('parsePRD', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should fallback to non-streaming when streaming fails with specific errors', async () => {
|
||||
test.skip('should fallback to non-streaming when streaming fails with specific errors (streaming disabled)', async () => {
|
||||
// Setup mocks to simulate normal conditions (no existing output file)
|
||||
fs.default.existsSync.mockImplementation((path) => {
|
||||
if (path === 'tasks/tasks.json') return false; // Output file doesn't exist
|
||||
@@ -954,7 +951,7 @@ describe('parsePRD', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle research flag with streaming', async () => {
|
||||
test('should handle research flag with non-streaming (streaming disabled)', async () => {
|
||||
// Setup mocks to simulate normal conditions
|
||||
fs.default.existsSync.mockImplementation((path) => {
|
||||
if (path === 'tasks/tasks.json') return false; // Output file doesn't exist
|
||||
@@ -965,19 +962,19 @@ describe('parsePRD', () => {
|
||||
// Mock progress reporting function
|
||||
const mockReportProgress = jest.fn(() => Promise.resolve());
|
||||
|
||||
// Call with streaming + research
|
||||
// Call with reportProgress + research - with streaming disabled, should use non-streaming
|
||||
await parsePRD('path/to/prd.txt', 'tasks/tasks.json', 3, {
|
||||
reportProgress: mockReportProgress,
|
||||
research: true
|
||||
});
|
||||
|
||||
// Verify streaming path was used with research role
|
||||
expect(streamObjectService).toHaveBeenCalledWith(
|
||||
// With streaming disabled, should use generateObjectService with research role
|
||||
expect(generateObjectService).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
role: 'research'
|
||||
})
|
||||
);
|
||||
expect(generateObjectService).not.toHaveBeenCalled();
|
||||
expect(streamObjectService).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should handle research flag with non-streaming', async () => {
|
||||
@@ -1009,7 +1006,7 @@ describe('parsePRD', () => {
|
||||
expect(streamObjectService).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should use streaming for CLI text mode even without reportProgress', async () => {
|
||||
test('should use non-streaming for CLI text mode (streaming disabled)', async () => {
|
||||
// Setup mocks to simulate normal conditions
|
||||
fs.default.existsSync.mockImplementation((path) => {
|
||||
if (path === 'tasks/tasks.json') return false; // Output file doesn't exist
|
||||
@@ -1020,13 +1017,12 @@ describe('parsePRD', () => {
|
||||
// Call without mcpLog and without reportProgress (CLI text mode)
|
||||
const result = await parsePRD('path/to/prd.txt', 'tasks/tasks.json', 3);
|
||||
|
||||
// Verify streaming path was used (no mcpLog means CLI text mode, which should use streaming)
|
||||
expect(streamObjectService).toHaveBeenCalled();
|
||||
expect(generateObjectService).not.toHaveBeenCalled();
|
||||
// With streaming disabled, should use generateObjectService even in CLI text mode
|
||||
expect(generateObjectService).toHaveBeenCalled();
|
||||
expect(streamObjectService).not.toHaveBeenCalled();
|
||||
|
||||
// Verify progress tracker components were called for CLI mode
|
||||
expect(createParsePrdTracker).toHaveBeenCalled();
|
||||
expect(displayParsePrdStart).toHaveBeenCalled();
|
||||
// Progress tracker components may still be called for CLI mode display
|
||||
// but the actual parsing uses non-streaming
|
||||
|
||||
expect(result).toEqual({
|
||||
success: true,
|
||||
|
||||
Reference in New Issue
Block a user