#!/usr/bin/env node /** * Fake claude CLI for companion B3 tests. * * Mimics stream-json output of claude CLI 2.1.159 for a turn that: * 1. Emits text deltas "Drafted a task." * 2. Calls the mcp__void__propose_change tool * 3. Receives a tool_result whose content carries a pending_change_id * in BOTH shapes that the route must handle defensively: * - content[].text (the real shape from companion-stdio.js) * - structuredContent on the bare tool_result event (hypothetical future shape) * * The tool_result shape mirrors what companion-stdio.js actually emits: * MCP response: { content: [{ type:'text', text: JSON.stringify(result) }], structuredContent: result } * The CLI surfaces that as a bare top-level event: * { type:'tool_result', tool_use_id:'...', content:[{ type:'text', text:'...' }] } * (structuredContent is NOT in the CLI's bare event in practice — the route * must parse from the text block.) */ const TOOL_USE_ID = 'toolu_draft_b3_01'; const DRAFT_ID = 'pc-test-1'; const resultPayload = { pending_change_id: DRAFT_ID, applied: false, summary: 'create task' }; const lines = [ // system init (ignored) { type: 'system', subtype: 'init', session_id: 'fake-session-b3', tools: [], cwd: '/tmp' }, // --- text block --- { type: 'stream_event', event: { type: 'content_block_start', index: 0, content_block: { type: 'text', text: '' } } }, { type: 'stream_event', event: { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: 'Drafted ' } } }, { type: 'stream_event', event: { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: 'a task.' } } }, { type: 'stream_event', event: { type: 'content_block_stop', index: 0 } }, // --- tool_use block for mcp__void__propose_change --- { type: 'stream_event', event: { type: 'content_block_start', index: 1, content_block: { type: 'tool_use', id: TOOL_USE_ID, name: 'mcp__void__propose_change', input: {} } } }, { type: 'stream_event', event: { type: 'content_block_delta', index: 1, delta: { type: 'input_json_delta', partial_json: '{"entity_type":"task","action":"create","payload":{"title":"Validate CSV"}}' } } }, // assistant snapshot (ignored) { type: 'assistant', message: { role: 'assistant', content: [ { type: 'text', text: 'Drafted a task.' }, { type: 'tool_use', id: TOOL_USE_ID, name: 'mcp__void__propose_change', input: { entity_type: 'task', action: 'create', payload: { title: 'Validate CSV' } } } ] } }, // content_block_stop for tool — this triggers the 'tool' event in claude_cli.js { type: 'stream_event', event: { type: 'content_block_stop', index: 1 } }, // --- tool_result (bare top-level event) --- // This is the shape from companion-stdio.js: // content: [{ type:'text', text: JSON.stringify(result) }] // ALSO include structuredContent for defensive parsing test coverage. { type: 'tool_result', tool_use_id: TOOL_USE_ID, content: [ { type: 'text', text: JSON.stringify(resultPayload) } ], // structuredContent mirrors what companion-stdio.js returns — included here // so the route's structuredContent branch is exercised if the CLI ever forwards it. structuredContent: resultPayload }, // --- final result --- { type: 'result', subtype: 'success', is_error: false, result: 'Drafted a task.', stop_reason: 'end_turn', session_id: 'fake-session-b3', total_cost_usd: 0.0005, usage: { input_tokens: 80, output_tokens: 8, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 } } ]; for (const line of lines) { process.stdout.write(JSON.stringify(line) + '\n'); } process.exit(0);