Compare commits

...

5 Commits

Author SHA1 Message Date
github-actions[bot]
4cae2991d4 Version Packages (#1154)
* Version Packages

* chore: fix changelog

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
2025-08-23 00:44:25 +02:00
Ralph Khreish
0d7ff627c9 Merge pull request #1152 from eyaltoledano/next 2025-08-23 00:34:40 +02:00
Ralph Khreish
db720a954d Fix: disable streaming for parse prd (#1151)
* fix: temporarily disable streaming
2025-08-22 18:33:02 +02:00
Ben Vargas
89335578ff fix(claude-code): prevent "Right-hand side of instanceof is not an object" when SDK missing; improve CLI stability (#1146)
* fix: handle missing @anthropic-ai/claude-code SDK gracefully

Add defensive checks to prevent "Right-hand side of 'instanceof' is not an object" errors when the optional Claude Code SDK is not installed.

Changes:
- Check if AbortError exists before using instanceof
- Check if query function exists before calling it
- Provide clear error messages when SDK is missing

This fixes the issue reported by users in v0.24.0 and v0.25.0 where Task Master would crash with instanceof errors when using the claude-code provider without the SDK installed.

* chore: bump @anthropic-ai/claude-code to ^1.0.88 and regenerate lockfile
2025-08-22 17:01:34 +02:00
Ralph Khreish
781b8ef2af remove claude code mentioning 2025-08-22 17:01:34 +02:00
9 changed files with 70 additions and 41 deletions

View File

@@ -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\]
---

View File

@@ -1,5 +1,15 @@
# task-master-ai
## 0.25.1
### Patch Changes
- [#1152](https://github.com/eyaltoledano/claude-task-master/pull/1152) [`8933557`](https://github.com/eyaltoledano/claude-task-master/commit/89335578ffffc65504b2055c0c85aa7521e5e79b) Thanks [@ben-vargas](https://github.com/ben-vargas)! - 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.
- [#1151](https://github.com/eyaltoledano/claude-task-master/pull/1151) [`db720a9`](https://github.com/eyaltoledano/claude-task-master/commit/db720a954d390bb44838cd021b8813dde8f3d8de) Thanks [@Crunchyman-ralph](https://github.com/Crunchyman-ralph)! - Temporarily disable streaming for improved model compatibility - will be re-enabled in upcoming release
## 0.25.0
### Minor Changes

View File

@@ -1,5 +1,12 @@
# Change Log
## 0.24.1
### Patch Changes
- Updated dependencies [[`8933557`](https://github.com/eyaltoledano/claude-task-master/commit/89335578ffffc65504b2055c0c85aa7521e5e79b), [`db720a9`](https://github.com/eyaltoledano/claude-task-master/commit/db720a954d390bb44838cd021b8813dde8f3d8de)]:
- task-master-ai@0.25.1
## 0.24.0
### Minor Changes

View File

@@ -3,7 +3,7 @@
"private": true,
"displayName": "TaskMaster",
"description": "A visual Kanban board interface for TaskMaster projects in VS Code",
"version": "0.24.0",
"version": "0.24.1",
"publisher": "Hamster",
"icon": "assets/icon.png",
"engines": {
@@ -256,7 +256,7 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
"task-master-ai": "0.25.0"
"task-master-ai": "0.25.1"
},
"devDependencies": {
"@dnd-kit/core": "^6.3.1",

19
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "task-master-ai",
"version": "0.24.0",
"version": "0.25.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "task-master-ai",
"version": "0.24.0",
"version": "0.25.1",
"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.1",
"dependencies": {
"task-master-ai": "0.24.0"
"task-master-ai": "0.25.1"
},
"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": {

View File

@@ -1,6 +1,6 @@
{
"name": "task-master-ai",
"version": "0.25.0",
"version": "0.25.1",
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
"main": "index.js",
"type": "module",
@@ -89,7 +89,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"
},

View File

@@ -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');
}
/**

View File

@@ -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;

View File

@@ -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,