Skip to content

Commit a110b98

Browse files
authored
Merge pull request #61 from cahaseler/60-task_058-fix-code-review-error-convert-string-prompts-to-asynciterable-format
feat: complete TASK_058 - TASK_058: Fix Code Review Error: Convert String Prompts to AsyncIterable Format
2 parents 9cf7102 + 9501c54 commit a110b98

File tree

7 files changed

+241
-5
lines changed

7 files changed

+241
-5
lines changed

.claude/backlog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@
2222
- [2025-09-15] Clean up SDK type usage across codebase: Import proper types from @anthropic-ai/claude-code for all SDK interactions. Fix internal prompt() function to use typed options and messages. Add canUseTool restrictions where appropriate (e.g., createValidationAgent should probably restrict Write). Replace type assertions with proper type guards or document why they're safe. See capture-plan.ts lines 224-248 for correct implementation pattern.
2323
- [2025-09-15] if complete-task has already been run once and there's already a PR, don't squash
2424
- [2025-09-15] ability to create a new task based on an existing github issue. Not sure how best to do this though.
25-
- [2025-09-15] Fix code review in prepare-completion - getting 'canUseTool callback requires --input-format stream-json' error. Need to update Claude SDK usage to pass prompt as AsyncIterable when using tool restrictions.
25+
- [2025-09-15] Enable interactive multi-turn code reviews with persistent sessions - allow Claude to reply to reviewer feedback, push back on criticisms, and have a dialogue about the changes

.claude/decision_log.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ Bug fixes, typo corrections, and fixing incorrect implementations are NOT decisi
272272
- **Implications:** Simpler codebase with one less hook to maintain, faster compaction process, no duplicate task updates
273273
- **Reversibility:** Easy - all changes are in git history and could be restored if needed
274274

275+
275276
### Template Entry
276277
```
277278
[YYYY-MM-DD HH:MM] - [Decision Summary]

.claude/plans/058.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Plan: 058
2+
3+
Captured: 2025-09-15T19:36:13.186Z
4+
5+
## Fix Code Review Error: Convert String Prompts to AsyncIterable Format
6+
7+
### Problem
8+
The code review feature is failing with the error "canUseTool callback requires --input-format stream-json" because we're passing string prompts to `query()` when using `canUseTool`. According to the Claude Code TypeScript SDK documentation, when using `canUseTool`, the prompt must be an `AsyncIterable<SDKUserMessage>`.
9+
10+
### Affected Code
11+
1. **src/lib/claude-sdk.ts - performCodeReview()** (line 428): Uses string prompt with canUseTool
12+
2. **src/hooks/capture-plan.ts - task enrichment** (line 216): Uses string prompt with canUseTool
13+
14+
### Solution
15+
Create a helper function to convert string prompts to the required streaming format, then update both affected functions to use it.
16+
17+
### Implementation Steps
18+
1. Add a helper function in `src/lib/claude-sdk.ts` to convert strings to AsyncIterable<SDKUserMessage>
19+
2. Update `performCodeReview()` to use the streaming format
20+
3. Update capture-plan hook's task enrichment to use the streaming format
21+
4. Test the fix by running `/prepare-completion` on a task with code review enabled
22+
23+
### Code Changes Required
24+
- Add `createMessageStream()` helper function
25+
- Modify `performCodeReview()` to use `prompt: createMessageStream(p)` instead of `prompt: p`
26+
- Modify capture-plan hook to use the same pattern
27+
- Import the necessary SDKUserMessage type from '@anthropic-ai/claude-code'
28+
29+
This will fix the immediate error and allow code reviews to work properly again.

.claude/tasks/TASK_058.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# TASK_058: Fix Code Review Error: Convert String Prompts to AsyncIterable Format
2+
3+
## Purpose
4+
Fix the code review feature that's failing with "canUseTool callback requires --input-format stream-json" error by converting string prompts to the required AsyncIterable<SDKUserMessage> format when using canUseTool.
5+
6+
## Status
7+
**in_progress** - Started: 2025-09-15 15:36
8+
9+
## Requirements
10+
- [x] Add helper function in `src/lib/claude-sdk.ts` to convert strings to AsyncIterable<SDKUserMessage>
11+
- [x] Update `performCodeReview()` function (line 428) to use streaming format
12+
- [x] Update capture-plan hook's task enrichment (line 216) to use streaming format
13+
- [x] Import necessary SDKUserMessage type from '@anthropic-ai/claude-code'
14+
- [x] Test the fix by running `/prepare-completion` on a task with code review enabled
15+
16+
## Success Criteria
17+
- Code review feature executes without "canUseTool callback requires --input-format stream-json" error
18+
- Both affected functions (`performCodeReview()` and capture-plan task enrichment) use proper streaming format
19+
- No regression in existing functionality
20+
- `/prepare-completion` command works successfully with code review enabled
21+
22+
## Technical Approach
23+
1. **Create Helper Function**: Implement `createMessageStream()` in `src/lib/claude-sdk.ts` that converts string prompts to AsyncIterable<SDKUserMessage>
24+
2. **Update performCodeReview()**: Replace direct string prompt usage with `createMessageStream(p)`
25+
3. **Update Capture-Plan Hook**: Modify task enrichment logic to use the same streaming pattern
26+
4. **Type Safety**: Ensure proper TypeScript imports and type annotations
27+
28+
## Recent Progress
29+
- Successfully identified the root cause: Claude Code SDK requires AsyncIterable<SDKUserMessage> format when using canUseTool
30+
- Implemented `createMessageStream()` helper function with proper message structure including required `role: 'user'` field
31+
- Updated `performCodeReview()` to use streaming format
32+
- Updated capture-plan hook's task enrichment to use streaming format
33+
- Refactored to eliminate code duplication by exporting helper from claude-sdk.ts
34+
- Added documentation explaining session_id and parent_tool_use_id values
35+
- Verified fix works - code review ran successfully and generated comprehensive review
36+
- Removed completed item from backlog
37+
38+
<!-- github_issue: 60 -->
39+
<!-- github_url: https://github.com/cahaseler/cc-track/issues/60 -->
40+
<!-- issue_branch: 60-task_058-fix-code-review-error-convert-string-prompts-to-asynciterable-format -->
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Code Review: TASK_058 - Fix Code Review Error: Convert String Prompts to AsyncIterable Format
2+
3+
**Date**: 2025-09-15 15:36 UTC
4+
**Reviewer**: Claude (Automated Code Review)
5+
**Task**: TASK_058 - Fix Code Review Error: Convert String Prompts to AsyncIterable Format
6+
**Branch**: `60-task_058-fix-code-review-error-convert-string-prompts-to-asynciterable-format`
7+
8+
## Executive Summary
9+
10+
**APPROVED** - The implementation successfully addresses the core requirement by implementing proper string-to-stream conversion for Claude Code SDK usage with `canUseTool`. However, there are architectural concerns about code duplication that should be addressed before completion.
11+
12+
## Requirements Alignment Analysis
13+
14+
### ✅ Requirements Met
15+
- [x] **Helper function created**: `createMessageStream()` implemented in both required files
16+
- [x] **performCodeReview() updated**: Line 447 now uses `createMessageStream(p)` instead of direct string
17+
- [x] **capture-plan hook updated**: Line 232 now uses `createMessageStream(prompt)`
18+
- [x] **Type imports added**: `SDKUserMessage` properly imported from '@anthropic-ai/claude-code'
19+
- [x] **Function signature correct**: Returns `AsyncIterable<SDKUserMessage>` as required
20+
21+
### ⚠️ Partially Met
22+
- [ ] **Testing requirement**: Cannot verify `/prepare-completion` functionality without execution, but implementation appears correct
23+
24+
## Code Quality Assessment
25+
26+
### 🔴 **Critical Issues**
27+
28+
#### 1. Code Duplication (High Priority)
29+
**Location**: `src/lib/claude-sdk.ts:33-43` and `src/hooks/capture-plan.ts:32-42`
30+
**Issue**: Identical `createMessageStream` function duplicated in two files
31+
**Impact**: Maintenance burden, potential for drift between implementations
32+
**Recommendation**:
33+
```typescript
34+
// Export from claude-sdk.ts
35+
export async function* createMessageStream(text: string): AsyncIterable<SDKUserMessage> {
36+
// implementation
37+
}
38+
39+
// Import in capture-plan.ts
40+
import { createMessageStream } from '../lib/claude-sdk';
41+
```
42+
43+
### 🟡 **Minor Issues**
44+
45+
#### 2. Type Safety Concerns
46+
**Location**: `src/lib/claude-sdk.ts:42` and `src/hooks/capture-plan.ts:41`
47+
**Issue**: Type assertion `as SDKUserMessage` masks potential type mismatches
48+
**Impact**: Runtime errors if type structure changes
49+
**Recommendation**: Consider using proper type guards or validate shape more explicitly
50+
51+
#### 3. Hard-coded Values
52+
**Location**: Both implementations use `session_id: 'temp-session'` and `parent_tool_use_id: null`
53+
**Issue**: Magic strings and assumptions about session context
54+
**Impact**: Potential confusion about session handling
55+
**Recommendation**: Document why these values are safe/appropriate for this use case
56+
57+
### **Positive Aspects**
58+
59+
1. **Clear Documentation**: Excellent JSDoc comments explaining the purpose and requirement
60+
2. **Minimal Change Scope**: Changes are surgical and focused only on the problem
61+
3. **Type Safety**: Proper TypeScript typing with imported interfaces
62+
4. **Consistent Pattern**: Both implementations follow identical approach
63+
64+
## Security Analysis
65+
66+
### ✅ No Security Concerns Identified
67+
- Implementation only handles string-to-stream conversion
68+
- No user input validation required (strings are already validated upstream)
69+
- No external network calls or file system access in conversion logic
70+
- `canUseTool` restrictions remain properly configured in both functions
71+
72+
## Performance Analysis
73+
74+
### ✅ Minimal Performance Impact
75+
- Generator function creates minimal overhead
76+
- Single yield operation per conversion
77+
- No memory leaks (generator handles cleanup automatically)
78+
- Lazy evaluation appropriate for this use case
79+
80+
## Architecture & Patterns
81+
82+
### 🟡 **Pattern Compliance**
83+
- **Follows project pattern**: Uses TypeScript, proper imports, JSDoc
84+
- **Violates DRY principle**: Code duplication issue noted above
85+
- **Type safety**: Consistent with project's TypeScript usage
86+
87+
### **Integration Quality**
88+
- **Minimal surface area**: Changes only affect the specific error condition
89+
- **Backward compatible**: No changes to public interfaces
90+
- **Error handling**: Inherits error handling from SDK query() function
91+
92+
## Testing Assessment
93+
94+
### ⚠️ **Testing Gaps**
95+
Since I cannot execute `/prepare-completion`, I cannot verify:
96+
1. That the actual error is resolved
97+
2. That code review functionality works end-to-end
98+
3. That there are no runtime type mismatches
99+
100+
**Recommendation**: Manual testing should verify:
101+
```bash
102+
# Test the fix
103+
/prepare-completion # Should not show "canUseTool callback requires --input-format stream-json"
104+
```
105+
106+
### **TypeScript Validation**
107+
- Code compiles without errors (`bunx tsc --noEmit` passed)
108+
- Type imports are correctly resolved
109+
- Function signatures match expected interfaces
110+
111+
## Documentation Review
112+
113+
### **Code Documentation**
114+
- Excellent JSDoc comments explaining the purpose
115+
- Clear function naming that describes behavior
116+
- Type annotations provide good developer experience
117+
118+
### ⚠️ **Missing Documentation**
119+
- No explanation of why `temp-session` and `null` values are appropriate
120+
- Could benefit from example usage in comments
121+
122+
## Recommendations
123+
124+
### 🔴 **Must Fix Before Completion**
125+
1. **Eliminate code duplication** by creating shared utility function
126+
2. **Test the actual fix** by running `/prepare-completion`
127+
128+
### 🟡 **Should Consider**
129+
1. Add inline comments explaining the session_id and parent_tool_use_id values
130+
2. Consider adding unit tests for the helper function
131+
132+
### 💡 **Future Improvements**
133+
1. Extract to a more generic utility that could handle other SDK conversions
134+
2. Consider if this pattern should be documented in system patterns
135+
136+
## Summary
137+
138+
The implementation correctly addresses the core technical requirement and should resolve the "canUseTool callback requires --input-format stream-json" error. The approach is sound and follows project conventions. However, the code duplication issue should be resolved before task completion to maintain code quality standards.
139+
140+
**Approval**: ✅ Approved with required changes
141+
**Next Steps**:
142+
1. Refactor to eliminate code duplication
143+
2. Test with `/prepare-completion`
144+
3. Update task file with results
145+
146+
---
147+
*Generated by automated code review system*

src/hooks/capture-plan.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFile
33
import { join, resolve } from 'node:path';
44
import type { CanUseTool, PermissionResult } from '@anthropic-ai/claude-code';
55
import { ClaudeMdHelpers } from '../lib/claude-md';
6-
import { findClaudeCodeExecutable } from '../lib/claude-sdk';
6+
import { createMessageStream, findClaudeCodeExecutable } from '../lib/claude-sdk';
77
import { getGitHubConfig, isGitHubIntegrationEnabled, isHookEnabled } from '../lib/config';
88
import type { ClaudeSDKInterface } from '../lib/git-helpers';
99
import { GitHelpers } from '../lib/git-helpers';
@@ -213,7 +213,7 @@ export async function enrichPlanWithResearch(
213213

214214
// Create the multi-turn stream with research and write capabilities
215215
const stream = query({
216-
prompt,
216+
prompt: createMessageStream(prompt),
217217
options: {
218218
model: 'sonnet',
219219
maxTurns: 20,

src/lib/claude-sdk.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { execSync } from 'node:child_process';
88
import { existsSync } from 'node:fs';
99
import { tmpdir } from 'node:os';
1010
import { join, resolve } from 'node:path';
11-
import type { CanUseTool, PermissionResult } from '@anthropic-ai/claude-code';
11+
import type { CanUseTool, PermissionResult, SDKUserMessage } from '@anthropic-ai/claude-code';
1212
import { createLogger } from './logger';
1313

1414
// Defer importing '@anthropic-ai/claude-code' until needed to avoid any
@@ -26,6 +26,25 @@ export interface ClaudeResponse {
2626
};
2727
}
2828

29+
/**
30+
* Helper function to convert a string prompt into an AsyncIterable<SDKUserMessage>
31+
* Required when using canUseTool callback with the Claude Code SDK
32+
*
33+
* Note: session_id is set to 'temp-session' as these are single-use query sessions,
34+
* not persistent conversations. parent_tool_use_id is null as these are initial messages.
35+
*/
36+
export async function* createMessageStream(text: string): AsyncIterable<SDKUserMessage> {
37+
yield {
38+
type: 'user',
39+
session_id: 'temp-session',
40+
parent_tool_use_id: null,
41+
message: {
42+
role: 'user',
43+
content: [{ type: 'text', text }],
44+
},
45+
} as SDKUserMessage;
46+
}
47+
2948
// Find the Claude Code executable cross-platform
3049
export function findClaudeCodeExecutable(): string | undefined {
3150
// Prefer system-installed claude (often a compiled binary and faster)
@@ -428,7 +447,7 @@ Your review should be thorough, actionable, and constructive. Include specific f
428447
logger.info('Starting code review', { taskId, timeout: 600000, maxTurns: 30 });
429448

430449
const stream = query({
431-
prompt: p,
450+
prompt: createMessageStream(p),
432451
options: {
433452
model: 'sonnet',
434453
maxTurns: 30,

0 commit comments

Comments
 (0)