@kosuke-ai/cli 0.0.24 ā 0.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/utils/file-discovery.test.d.ts +5 -0
- package/dist/__tests__/utils/file-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/utils/file-discovery.test.js +51 -0
- package/dist/__tests__/utils/file-discovery.test.js.map +1 -0
- package/dist/__tests__/utils/logger-nologs.test.d.ts +5 -0
- package/dist/__tests__/utils/logger-nologs.test.d.ts.map +1 -0
- package/dist/__tests__/utils/logger-nologs.test.js +34 -0
- package/dist/__tests__/utils/logger-nologs.test.js.map +1 -0
- package/dist/__tests__/utils/logger.test.d.ts +5 -0
- package/dist/__tests__/utils/logger.test.d.ts.map +1 -0
- package/dist/__tests__/utils/logger.test.js +242 -0
- package/dist/__tests__/utils/logger.test.js.map +1 -0
- package/dist/__tests__/utils/validator.test.d.ts +5 -0
- package/dist/__tests__/utils/validator.test.d.ts.map +1 -0
- package/dist/__tests__/utils/validator.test.js +206 -0
- package/dist/__tests__/utils/validator.test.js.map +1 -0
- package/dist/index.js +32 -28
- package/dist/index.js.map +1 -1
- package/dist/kosuke/commands/requirements.d.ts +7 -2
- package/dist/kosuke/commands/requirements.d.ts.map +1 -1
- package/dist/kosuke/commands/requirements.js +266 -212
- package/dist/kosuke/commands/requirements.js.map +1 -1
- package/dist/kosuke/commands/tickets.d.ts +10 -3
- package/dist/kosuke/commands/tickets.d.ts.map +1 -1
- package/dist/kosuke/commands/tickets.js +72 -42
- package/dist/kosuke/commands/tickets.js.map +1 -1
- package/dist/kosuke/types.d.ts +2 -1
- package/dist/kosuke/types.d.ts.map +1 -1
- package/dist/kosuke/utils/claude-agent.d.ts.map +1 -1
- package/dist/kosuke/utils/claude-agent.js +34 -4
- package/dist/kosuke/utils/claude-agent.js.map +1 -1
- package/dist/package.json +2 -1
- package/package.json +2 -1
|
@@ -7,11 +7,49 @@
|
|
|
7
7
|
* - Claude asks clarification questions
|
|
8
8
|
* - User answers questions (iterative until clear)
|
|
9
9
|
* - Generate docs.md with complete requirements
|
|
10
|
+
*
|
|
11
|
+
* Implementation: Uses Anthropic SDK directly with two custom tools:
|
|
12
|
+
* - write_docs: Create new docs.md file
|
|
13
|
+
* - edit_docs: Update existing docs.md file
|
|
10
14
|
*/
|
|
11
|
-
import
|
|
15
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
16
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
12
17
|
import { join } from 'path';
|
|
13
18
|
import * as readline from 'readline';
|
|
14
19
|
import { calculateCost } from '../utils/claude-agent.js';
|
|
20
|
+
/**
|
|
21
|
+
* Tool definitions for docs.md management
|
|
22
|
+
*/
|
|
23
|
+
const REQUIREMENTS_TOOLS = [
|
|
24
|
+
{
|
|
25
|
+
name: 'write_docs',
|
|
26
|
+
description: 'Create a new docs.md file with comprehensive product requirements. Use this when creating the initial requirements document.',
|
|
27
|
+
input_schema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
content: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Full markdown content for docs.md including all sections: Product Description (high-level overview and purpose), Core Functionalities (detailed feature descriptions), and Interface & Design (ASCII wireframes for all major pages/screens with component descriptions and user interactions). Do NOT include technical implementation details like database schemas, API endpoints, or code architecture.',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: ['content'],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'edit_docs',
|
|
40
|
+
description: 'Update an existing docs.md file with revised requirements. Use this when making changes to an existing requirements document.',
|
|
41
|
+
input_schema: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
content: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Full updated markdown content for docs.md with all revisions incorporated.',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
required: ['content'],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
];
|
|
15
53
|
/**
|
|
16
54
|
* Custom system prompt for requirements gathering
|
|
17
55
|
*/
|
|
@@ -84,18 +122,12 @@ For each clarification needed, provide BOTH a question AND a recommended approac
|
|
|
84
122
|
- Always prioritize simplicity and MVP scope
|
|
85
123
|
- Continue the conversation until EVERYTHING is crystal clear
|
|
86
124
|
|
|
87
|
-
3. **Final Deliverable - docs.md**: Once ALL questions are answered and requirements are 100% clear, create the \`docs.md\` file. This is the FINAL DELIVERABLE that the user will review before implementation begins.
|
|
125
|
+
3. **Final Deliverable - docs.md**: Once ALL questions are answered and requirements are 100% clear, use the \`write_docs\` tool to create the \`docs.md\` file. This is the FINAL DELIVERABLE that the user will review before implementation begins.
|
|
88
126
|
|
|
89
127
|
**docs.md MUST contain:**
|
|
90
|
-
- **Product
|
|
91
|
-
- **Core Functionalities** - Detailed feature descriptions
|
|
92
|
-
- **Interface & Design** - ASCII wireframes for ALL major pages/screens
|
|
93
|
-
- **Technical Architecture** - Tech stack, folder structure, key libraries
|
|
94
|
-
- **User Flows** - Step-by-step user journeys for key features
|
|
95
|
-
- **Database Schema** - Tables, fields, relationships, data types
|
|
96
|
-
- **API Endpoints** - Routes, methods, request/response formats (if applicable)
|
|
97
|
-
- **Business Logic** - Key algorithms, calculations, rules
|
|
98
|
-
- **Implementation Notes** - Important technical considerations
|
|
128
|
+
- **Product Description** - High-level description of what will be built, the core concept and purpose
|
|
129
|
+
- **Core Functionalities** - Detailed feature descriptions (what the product should do)
|
|
130
|
+
- **Interface & Design** - ASCII wireframes for ALL major pages/screens with component descriptions and user interactions
|
|
99
131
|
|
|
100
132
|
**Critical Rules:**
|
|
101
133
|
- NEVER start implementation - you only gather requirements
|
|
@@ -107,16 +139,40 @@ For each clarification needed, provide BOTH a question AND a recommended approac
|
|
|
107
139
|
- Focus on WHAT the product should do, not HOW to code it
|
|
108
140
|
- Be conversational and help the user think through edge cases
|
|
109
141
|
- Bias towards simplicity - this is an MVP, not a full-featured product
|
|
142
|
+
- Use the \`write_docs\` tool when creating the initial docs.md file
|
|
143
|
+
- Use the \`edit_docs\` tool if you need to update docs.md after user feedback
|
|
110
144
|
- The docs.md file is your SUCCESS CRITERIA - make it comprehensive and clear
|
|
145
|
+
- NEVER include technical implementation details (database schemas, API endpoints, code architecture, tech stack) in docs.md
|
|
146
|
+
- Keep docs.md focused on user-facing features, functionality, and interface design only
|
|
111
147
|
|
|
112
148
|
**Success = User reviews docs.md and says "Yes, this is exactly what I want to build"**`;
|
|
113
149
|
/**
|
|
114
|
-
*
|
|
115
|
-
* Just returns the user message - all instructions are in the system prompt
|
|
150
|
+
* Execute a tool call (write or edit docs.md)
|
|
116
151
|
*/
|
|
117
|
-
function
|
|
118
|
-
|
|
119
|
-
|
|
152
|
+
function executeToolCall(toolName, toolInput, workspaceRoot) {
|
|
153
|
+
const docsPath = join(workspaceRoot, 'docs.md');
|
|
154
|
+
try {
|
|
155
|
+
if (toolName === 'write_docs') {
|
|
156
|
+
const content = toolInput.content;
|
|
157
|
+
writeFileSync(docsPath, content, 'utf-8');
|
|
158
|
+
console.log('\nāļø Created docs.md with comprehensive requirements');
|
|
159
|
+
return 'Successfully created docs.md file';
|
|
160
|
+
}
|
|
161
|
+
else if (toolName === 'edit_docs') {
|
|
162
|
+
const content = toolInput.content;
|
|
163
|
+
writeFileSync(docsPath, content, 'utf-8');
|
|
164
|
+
console.log('\nāļø Updated docs.md with revised requirements');
|
|
165
|
+
return 'Successfully updated docs.md file';
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
return `Unknown tool: ${toolName}`;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error writing docs.md';
|
|
173
|
+
console.error(`\nā Failed to write docs.md: ${errorMessage}`);
|
|
174
|
+
return `Error: ${errorMessage}`;
|
|
175
|
+
}
|
|
120
176
|
}
|
|
121
177
|
/**
|
|
122
178
|
* Format token usage for display
|
|
@@ -136,212 +192,209 @@ function formatTokenUsage(inputTokens, outputTokens, cacheCreationTokens, cacheR
|
|
|
136
192
|
/**
|
|
137
193
|
* Process a single Claude interaction with streaming support
|
|
138
194
|
*/
|
|
139
|
-
async function processClaudeInteraction(userInput,
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const
|
|
195
|
+
async function processClaudeInteraction(userInput, previousMessages, workspaceRoot, onStream) {
|
|
196
|
+
const anthropic = new Anthropic({
|
|
197
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
198
|
+
});
|
|
199
|
+
// Build message history: previous messages + new user message
|
|
200
|
+
const messages = [
|
|
201
|
+
...previousMessages,
|
|
202
|
+
{
|
|
203
|
+
role: 'user',
|
|
204
|
+
content: userInput,
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
// Stream the response
|
|
208
|
+
const stream = await anthropic.messages.stream({
|
|
209
|
+
model: 'claude-sonnet-4-20250514',
|
|
210
|
+
max_tokens: 8096,
|
|
211
|
+
system: REQUIREMENTS_SYSTEM_PROMPT,
|
|
212
|
+
tools: REQUIREMENTS_TOOLS,
|
|
213
|
+
messages,
|
|
214
|
+
});
|
|
153
215
|
let responseText = '';
|
|
154
|
-
|
|
155
|
-
let inputTokens = 0;
|
|
156
|
-
let outputTokens = 0;
|
|
157
|
-
let cacheCreationTokens = 0;
|
|
158
|
-
let cacheReadTokens = 0;
|
|
216
|
+
const toolUses = [];
|
|
159
217
|
let isFirstOutput = true;
|
|
160
|
-
//
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
218
|
+
// Process stream events
|
|
219
|
+
for await (const event of stream) {
|
|
220
|
+
if (event.type === 'content_block_start') {
|
|
221
|
+
if (event.content_block.type === 'text') {
|
|
222
|
+
if (isFirstOutput) {
|
|
223
|
+
if (!onStream) {
|
|
224
|
+
process.stdout.write('\n> Claude:\n');
|
|
225
|
+
}
|
|
226
|
+
isFirstOutput = false;
|
|
227
|
+
}
|
|
169
228
|
}
|
|
170
229
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const content = message.message.content;
|
|
179
|
-
for (let i = 0; i < content.length; i++) {
|
|
180
|
-
const block = content[i];
|
|
181
|
-
if (block.type === 'text') {
|
|
182
|
-
const currentText = block.text || '';
|
|
183
|
-
const previousText = blockTexts.get(i) || '';
|
|
184
|
-
// Calculate the delta (new text added since last message)
|
|
185
|
-
const delta = currentText.substring(previousText.length);
|
|
186
|
-
// Stream only the delta to console in real-time
|
|
187
|
-
if (delta) {
|
|
188
|
-
if (isFirstOutput) {
|
|
189
|
-
process.stdout.write('\n> Claude:\n');
|
|
190
|
-
isFirstOutput = false;
|
|
191
|
-
}
|
|
192
|
-
process.stdout.write(delta);
|
|
193
|
-
}
|
|
194
|
-
// Update tracked text and full response
|
|
195
|
-
blockTexts.set(i, currentText);
|
|
196
|
-
responseText = currentText;
|
|
230
|
+
else if (event.type === 'content_block_delta') {
|
|
231
|
+
if (event.delta.type === 'text_delta') {
|
|
232
|
+
const delta = event.delta.text;
|
|
233
|
+
responseText += delta;
|
|
234
|
+
// Stream to console or callback
|
|
235
|
+
if (onStream) {
|
|
236
|
+
onStream(delta);
|
|
197
237
|
}
|
|
198
|
-
else
|
|
199
|
-
|
|
200
|
-
if (block.name === 'Write' || block.name === 'Edit') {
|
|
201
|
-
const input = block.input;
|
|
202
|
-
if (input.path === 'docs.md' || input.path?.includes('docs.md')) {
|
|
203
|
-
console.log('\n\nāļø Generating docs.md...');
|
|
204
|
-
}
|
|
205
|
-
}
|
|
238
|
+
else {
|
|
239
|
+
process.stdout.write(delta);
|
|
206
240
|
}
|
|
207
241
|
}
|
|
208
242
|
}
|
|
209
|
-
else if (
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
243
|
+
else if (event.type === 'content_block_stop') {
|
|
244
|
+
// Content block finished
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Get final message from stream
|
|
248
|
+
const finalMessage = await stream.finalMessage();
|
|
249
|
+
// Extract tool uses from the response
|
|
250
|
+
for (const block of finalMessage.content) {
|
|
251
|
+
if (block.type === 'tool_use') {
|
|
252
|
+
toolUses.push({
|
|
253
|
+
id: block.id,
|
|
254
|
+
name: block.name,
|
|
255
|
+
input: block.input,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Execute tools and build tool result messages
|
|
260
|
+
const hasToolCalls = toolUses.length > 0;
|
|
261
|
+
let updatedMessages = messages;
|
|
262
|
+
if (hasToolCalls) {
|
|
263
|
+
// Add assistant message with tool uses
|
|
264
|
+
updatedMessages = [
|
|
265
|
+
...messages,
|
|
266
|
+
{
|
|
267
|
+
role: 'assistant',
|
|
268
|
+
content: finalMessage.content,
|
|
269
|
+
},
|
|
270
|
+
];
|
|
271
|
+
// Execute each tool and collect results
|
|
272
|
+
const toolResults = toolUses.map((tool) => {
|
|
273
|
+
const result = executeToolCall(tool.name, tool.input, workspaceRoot);
|
|
274
|
+
return {
|
|
275
|
+
type: 'tool_result',
|
|
276
|
+
tool_use_id: tool.id,
|
|
277
|
+
content: result,
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
// Add tool results as user message
|
|
281
|
+
updatedMessages = [
|
|
282
|
+
...updatedMessages,
|
|
283
|
+
{
|
|
284
|
+
role: 'user',
|
|
285
|
+
content: toolResults,
|
|
286
|
+
},
|
|
287
|
+
];
|
|
288
|
+
// Continue conversation after tool execution to get final response
|
|
289
|
+
const followupStream = await anthropic.messages.stream({
|
|
290
|
+
model: 'claude-sonnet-4-20250514',
|
|
291
|
+
max_tokens: 8096,
|
|
292
|
+
system: REQUIREMENTS_SYSTEM_PROMPT,
|
|
293
|
+
tools: REQUIREMENTS_TOOLS,
|
|
294
|
+
messages: updatedMessages,
|
|
295
|
+
});
|
|
296
|
+
let followupText = '';
|
|
297
|
+
for await (const event of followupStream) {
|
|
298
|
+
if (event.type === 'content_block_delta') {
|
|
299
|
+
if (event.delta.type === 'text_delta') {
|
|
300
|
+
const delta = event.delta.text;
|
|
301
|
+
followupText += delta;
|
|
302
|
+
// Stream followup response
|
|
303
|
+
if (onStream) {
|
|
304
|
+
onStream(delta);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
process.stdout.write(delta);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
216
310
|
}
|
|
217
311
|
}
|
|
312
|
+
const followupMessage = await followupStream.finalMessage();
|
|
313
|
+
// Update response and messages
|
|
314
|
+
responseText += '\n' + followupText;
|
|
315
|
+
updatedMessages = [
|
|
316
|
+
...updatedMessages,
|
|
317
|
+
{
|
|
318
|
+
role: 'assistant',
|
|
319
|
+
content: followupMessage.content,
|
|
320
|
+
},
|
|
321
|
+
];
|
|
322
|
+
// Combine token usage from both calls
|
|
323
|
+
const finalUsage = finalMessage.usage;
|
|
324
|
+
const followupUsage = followupMessage.usage;
|
|
325
|
+
const combinedUsage = {
|
|
326
|
+
input_tokens: finalUsage.input_tokens + followupUsage.input_tokens,
|
|
327
|
+
output_tokens: finalUsage.output_tokens + followupUsage.output_tokens,
|
|
328
|
+
cache_creation_input_tokens: (finalUsage.cache_creation_input_tokens || 0) +
|
|
329
|
+
(followupUsage.cache_creation_input_tokens || 0),
|
|
330
|
+
cache_read_input_tokens: (finalUsage.cache_read_input_tokens || 0) + (followupUsage.cache_read_input_tokens || 0),
|
|
331
|
+
};
|
|
332
|
+
return {
|
|
333
|
+
response: responseText,
|
|
334
|
+
messages: updatedMessages,
|
|
335
|
+
inputTokens: combinedUsage.input_tokens,
|
|
336
|
+
outputTokens: combinedUsage.output_tokens,
|
|
337
|
+
cacheCreationTokens: combinedUsage.cache_creation_input_tokens,
|
|
338
|
+
cacheReadTokens: combinedUsage.cache_read_input_tokens,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
// No tool calls - just add assistant response to messages
|
|
343
|
+
updatedMessages = [
|
|
344
|
+
...messages,
|
|
345
|
+
{
|
|
346
|
+
role: 'assistant',
|
|
347
|
+
content: finalMessage.content,
|
|
348
|
+
},
|
|
349
|
+
];
|
|
350
|
+
const usage = finalMessage.usage;
|
|
351
|
+
return {
|
|
352
|
+
response: responseText,
|
|
353
|
+
messages: updatedMessages,
|
|
354
|
+
inputTokens: usage.input_tokens,
|
|
355
|
+
outputTokens: usage.output_tokens,
|
|
356
|
+
cacheCreationTokens: usage.cache_creation_input_tokens || 0,
|
|
357
|
+
cacheReadTokens: usage.cache_read_input_tokens || 0,
|
|
358
|
+
};
|
|
218
359
|
}
|
|
219
|
-
return {
|
|
220
|
-
response: responseText,
|
|
221
|
-
sessionId: newSessionId,
|
|
222
|
-
inputTokens,
|
|
223
|
-
outputTokens,
|
|
224
|
-
cacheCreationTokens,
|
|
225
|
-
cacheReadTokens,
|
|
226
|
-
};
|
|
227
360
|
}
|
|
228
361
|
/**
|
|
229
362
|
* Core requirements gathering function for programmatic use
|
|
230
363
|
* This is the non-interactive API that can be used by kosuke-core
|
|
231
364
|
*/
|
|
232
365
|
export async function requirementsCore(options) {
|
|
233
|
-
const { workspaceRoot, userMessage,
|
|
366
|
+
const { workspaceRoot, userMessage, previousMessages = [], isFirstRequest = false, onStream, } = options;
|
|
234
367
|
try {
|
|
235
368
|
// Validate API key
|
|
236
369
|
if (!process.env.ANTHROPIC_API_KEY) {
|
|
237
370
|
throw new Error('ANTHROPIC_API_KEY environment variable is required');
|
|
238
371
|
}
|
|
239
|
-
|
|
240
|
-
const effectivePrompt = buildRequirementsPrompt(userMessage, isFirstRequest);
|
|
241
|
-
// Query options with custom requirements gathering system prompt
|
|
242
|
-
const queryOptions = {
|
|
243
|
-
model: 'claude-sonnet-4-5',
|
|
244
|
-
maxTurns: 20,
|
|
245
|
-
cwd: workspaceRoot,
|
|
246
|
-
permissionMode: 'acceptEdits',
|
|
247
|
-
resume: sessionId || undefined, // Resume previous session if sessionId provided
|
|
248
|
-
allowedTools: ['Read', 'Write', 'Edit', 'LS', 'Grep', 'Glob', 'WebSearch'],
|
|
249
|
-
systemPrompt: REQUIREMENTS_SYSTEM_PROMPT, // SDK accepts string despite TypeScript types
|
|
250
|
-
};
|
|
251
|
-
console.log(`š [RequirementsCore] Starting query with session: ${sessionId || 'NEW'}`);
|
|
372
|
+
console.log(`š [RequirementsCore] Starting query`);
|
|
252
373
|
console.log(`š [RequirementsCore] Is first request: ${isFirstRequest}`);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
let newSessionId = sessionId || '';
|
|
257
|
-
let inputTokens = 0;
|
|
258
|
-
let outputTokens = 0;
|
|
259
|
-
let cacheCreationTokens = 0;
|
|
260
|
-
let cacheReadTokens = 0;
|
|
261
|
-
// Process the async generator with streaming
|
|
262
|
-
// Track accumulated text per block index for delta calculation
|
|
263
|
-
const blockTexts = new Map();
|
|
264
|
-
try {
|
|
265
|
-
for await (const message of responseStream) {
|
|
266
|
-
// Debug: Log ALL message details to understand stream structure
|
|
267
|
-
const messageWithId = message;
|
|
268
|
-
console.log(`šØ [RequirementsCore] Message received:`, JSON.stringify({
|
|
269
|
-
type: message.type,
|
|
270
|
-
subtype: messageWithId.subtype,
|
|
271
|
-
hasSessionId: !!messageWithId.session_id,
|
|
272
|
-
sessionIdValue: messageWithId.session_id || 'N/A',
|
|
273
|
-
keys: Object.keys(message),
|
|
274
|
-
}, null, 2));
|
|
275
|
-
// Capture session ID from system init message (first message in stream)
|
|
276
|
-
if (message.type === 'system' && message.subtype === 'init') {
|
|
277
|
-
if (!newSessionId) {
|
|
278
|
-
newSessionId = message.session_id;
|
|
279
|
-
console.log(`š [RequirementsCore] Captured new session ID from system init: ${newSessionId}`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
// Also try capturing from ANY message that has a session_id
|
|
283
|
-
if (!newSessionId && messageWithId.session_id) {
|
|
284
|
-
newSessionId = messageWithId.session_id;
|
|
285
|
-
console.log(`š [RequirementsCore] Captured session ID from ${message.type} message: ${newSessionId}`);
|
|
286
|
-
}
|
|
287
|
-
if (message.type === 'assistant') {
|
|
288
|
-
const content = message.message.content;
|
|
289
|
-
for (let i = 0; i < content.length; i++) {
|
|
290
|
-
const block = content[i];
|
|
291
|
-
if (block.type === 'text') {
|
|
292
|
-
const currentText = block.text || '';
|
|
293
|
-
const previousText = blockTexts.get(i) || '';
|
|
294
|
-
// Calculate the delta (new text added since last message)
|
|
295
|
-
const delta = currentText.substring(previousText.length);
|
|
296
|
-
// Stream the delta if callback provided and there's new text
|
|
297
|
-
if (delta && onStream) {
|
|
298
|
-
onStream(delta);
|
|
299
|
-
}
|
|
300
|
-
// Update the tracked text for this block
|
|
301
|
-
blockTexts.set(i, currentText);
|
|
302
|
-
// Accumulate full response
|
|
303
|
-
responseText = currentText;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
// Track token usage
|
|
307
|
-
if (message.message.usage) {
|
|
308
|
-
inputTokens += message.message.usage.input_tokens || 0;
|
|
309
|
-
outputTokens += message.message.usage.output_tokens || 0;
|
|
310
|
-
cacheCreationTokens += message.message.usage.cache_creation_input_tokens || 0;
|
|
311
|
-
cacheReadTokens += message.message.usage.cache_read_input_tokens || 0;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
catch (streamError) {
|
|
317
|
-
throw streamError;
|
|
318
|
-
}
|
|
374
|
+
console.log(`š [RequirementsCore] Previous messages: ${previousMessages.length}`);
|
|
375
|
+
// Process interaction
|
|
376
|
+
const result = await processClaudeInteraction(userMessage, previousMessages, workspaceRoot, onStream);
|
|
319
377
|
// Check if docs.md was created
|
|
320
378
|
let docsCreated = false;
|
|
321
379
|
let docsContent;
|
|
322
380
|
const docsPath = join(workspaceRoot, 'docs.md');
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
docsContent = await fs.readFile(docsPath, 'utf-8');
|
|
381
|
+
if (existsSync(docsPath)) {
|
|
382
|
+
docsContent = readFileSync(docsPath, 'utf-8');
|
|
326
383
|
docsCreated = true;
|
|
327
384
|
}
|
|
328
|
-
|
|
329
|
-
// docs.md not yet created
|
|
330
|
-
docsCreated = false;
|
|
331
|
-
}
|
|
332
|
-
console.log(`ā
[RequirementsCore] Returning session ID: ${newSessionId}`);
|
|
385
|
+
console.log(`ā
[RequirementsCore] Interaction complete`);
|
|
333
386
|
console.log(`ā
[RequirementsCore] Docs created: ${docsCreated}`);
|
|
334
387
|
return {
|
|
335
388
|
success: true,
|
|
336
|
-
response:
|
|
337
|
-
|
|
389
|
+
response: result.response,
|
|
390
|
+
messages: result.messages,
|
|
338
391
|
docsCreated,
|
|
339
392
|
docsContent,
|
|
340
393
|
tokenUsage: {
|
|
341
|
-
input: inputTokens,
|
|
342
|
-
output: outputTokens,
|
|
343
|
-
cacheCreation: cacheCreationTokens,
|
|
344
|
-
cacheRead: cacheReadTokens,
|
|
394
|
+
input: result.inputTokens,
|
|
395
|
+
output: result.outputTokens,
|
|
396
|
+
cacheCreation: result.cacheCreationTokens,
|
|
397
|
+
cacheRead: result.cacheReadTokens,
|
|
345
398
|
},
|
|
346
399
|
};
|
|
347
400
|
}
|
|
@@ -349,7 +402,7 @@ export async function requirementsCore(options) {
|
|
|
349
402
|
return {
|
|
350
403
|
success: false,
|
|
351
404
|
response: '',
|
|
352
|
-
|
|
405
|
+
messages: previousMessages,
|
|
353
406
|
docsCreated: false,
|
|
354
407
|
tokenUsage: {
|
|
355
408
|
input: 0,
|
|
@@ -386,10 +439,9 @@ async function interactiveSession() {
|
|
|
386
439
|
});
|
|
387
440
|
const session = {
|
|
388
441
|
productDescription: '',
|
|
389
|
-
|
|
442
|
+
messages: [],
|
|
390
443
|
isFirstRequest: true,
|
|
391
444
|
};
|
|
392
|
-
let sessionId = null;
|
|
393
445
|
let totalInputTokens = 0;
|
|
394
446
|
let totalOutputTokens = 0;
|
|
395
447
|
let totalCacheCreationTokens = 0;
|
|
@@ -503,16 +555,18 @@ async function interactiveSession() {
|
|
|
503
555
|
return;
|
|
504
556
|
}
|
|
505
557
|
session.productDescription = productDescription;
|
|
506
|
-
session.conversationHistory.push(`User: ${productDescription}`);
|
|
507
558
|
// Main conversation loop
|
|
508
559
|
let continueConversation = true;
|
|
509
560
|
while (continueConversation) {
|
|
510
561
|
console.log('\nš¤ Claude is thinking...\n');
|
|
511
|
-
// Get
|
|
512
|
-
const
|
|
562
|
+
// Get current user message
|
|
563
|
+
const userMessage = session.messages.length === 0
|
|
513
564
|
? productDescription
|
|
514
|
-
: session.
|
|
515
|
-
|
|
565
|
+
: session.messages[session.messages.length - 1].content;
|
|
566
|
+
// Get Claude's response with streaming
|
|
567
|
+
const result = await processClaudeInteraction(userMessage, session.messages, process.cwd());
|
|
568
|
+
// Update session messages
|
|
569
|
+
session.messages = result.messages;
|
|
516
570
|
session.isFirstRequest = false;
|
|
517
571
|
// Track costs
|
|
518
572
|
totalInputTokens += result.inputTokens;
|
|
@@ -525,24 +579,17 @@ async function interactiveSession() {
|
|
|
525
579
|
console.log('\n' + 'ā'.repeat(90));
|
|
526
580
|
console.log(formatTokenUsage(result.inputTokens, result.outputTokens, result.cacheCreationTokens, result.cacheReadTokens, batchCost));
|
|
527
581
|
console.log('ā'.repeat(90) + '\n');
|
|
528
|
-
session.conversationHistory.push(`Claude: ${result.response}`);
|
|
529
582
|
// Check if docs.md was created
|
|
530
583
|
const docsPath = join(process.cwd(), 'docs.md');
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
continueConversation = false;
|
|
541
|
-
break;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
catch {
|
|
545
|
-
// docs.md not yet created, continue conversation
|
|
584
|
+
if (existsSync(docsPath)) {
|
|
585
|
+
console.log('\nā
Requirements document created: docs.md');
|
|
586
|
+
console.log('\n' + 'ā'.repeat(90));
|
|
587
|
+
console.log('š Total Session Cost:');
|
|
588
|
+
console.log(formatTokenUsage(totalInputTokens, totalOutputTokens, totalCacheCreationTokens, totalCacheReadTokens, totalCost));
|
|
589
|
+
console.log('ā'.repeat(90));
|
|
590
|
+
console.log('\nš Requirements gathering complete!\n');
|
|
591
|
+
continueConversation = false;
|
|
592
|
+
break;
|
|
546
593
|
}
|
|
547
594
|
// Ask for user response
|
|
548
595
|
console.log('š¬ Your response (type "exit" to quit):\n');
|
|
@@ -560,7 +607,14 @@ async function interactiveSession() {
|
|
|
560
607
|
continueConversation = false;
|
|
561
608
|
break;
|
|
562
609
|
}
|
|
563
|
-
|
|
610
|
+
// Add user response to messages
|
|
611
|
+
session.messages = [
|
|
612
|
+
...session.messages,
|
|
613
|
+
{
|
|
614
|
+
role: 'user',
|
|
615
|
+
content: userResponse,
|
|
616
|
+
},
|
|
617
|
+
];
|
|
564
618
|
}
|
|
565
619
|
}
|
|
566
620
|
catch (error) {
|