create-claude-workspace 1.1.82 → 1.1.84
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.
|
@@ -102,11 +102,11 @@ describe('agent tag integration: real Claude CLI', () => {
|
|
|
102
102
|
console.log(`Event types: ${[...new Set(result.rawEvents.map(e => e.type))].join(', ')}`);
|
|
103
103
|
}, 60_000);
|
|
104
104
|
it.skipIf(!hasClaude)('shows sub-agent name when Agent tool is used', async () => {
|
|
105
|
-
// Use a custom agent defined in .claude/agents/test-dummy.md
|
|
106
|
-
// This mirrors how orchestrator delegates: subagent_type="test-dummy"
|
|
105
|
+
// Use a custom agent defined in .claude/agents/test-dummy.md (model: haiku)
|
|
106
|
+
// This mirrors how orchestrator delegates: subagent_type="test-dummy", model="haiku"
|
|
107
107
|
const prompt = [
|
|
108
108
|
'You MUST use the Agent tool exactly once.',
|
|
109
|
-
'Call it with: subagent_type "test-dummy", description "find ts files",',
|
|
109
|
+
'Call it with: subagent_type "test-dummy", model "haiku", description "find ts files",',
|
|
110
110
|
'prompt "Use Glob with pattern **/*.mts to find TypeScript files. Return the list."',
|
|
111
111
|
'After the Agent tool returns, respond with: done',
|
|
112
112
|
].join(' ');
|
|
@@ -135,10 +135,12 @@ describe('agent tag integration: real Claude CLI', () => {
|
|
|
135
135
|
console.log(`Delegation line: ${agentLinePlain.trim()}`);
|
|
136
136
|
// Delegation line should show orchestrator as the caller
|
|
137
137
|
expect(agentLinePlain).toContain(`orchestrator/${result.detectedModel}`);
|
|
138
|
-
// Should contain
|
|
139
|
-
expect(agentLinePlain, 'expected
|
|
138
|
+
// Should contain agent name "test-dummy" with model "haiku"
|
|
139
|
+
expect(agentLinePlain, 'expected "test-dummy" in delegation line')
|
|
140
140
|
.toContain('test-dummy');
|
|
141
|
-
|
|
141
|
+
expect(agentLinePlain, 'expected "haiku" model in delegation line')
|
|
142
|
+
.toContain('haiku');
|
|
143
|
+
// Verify sub-agent's internal tool calls show sub-agent name + model (not orchestrator)
|
|
142
144
|
const plainLines = result.formattedLines.map(stripAnsi);
|
|
143
145
|
const delegationIdx = plainLines.findIndex(l => l.includes('🤖'));
|
|
144
146
|
const subAgentLines = plainLines.slice(delegationIdx + 1).filter(l => l.includes('▶') || l.includes('💭'));
|
|
@@ -146,9 +148,11 @@ describe('agent tag integration: real Claude CLI', () => {
|
|
|
146
148
|
console.log(`Sub-agent internal tool calls (${subAgentLines.length}):`);
|
|
147
149
|
for (const line of subAgentLines) {
|
|
148
150
|
console.log(` ${line.trim()}`);
|
|
149
|
-
// Internal tool calls should show "test-dummy", NOT "orchestrator"
|
|
150
|
-
expect(line, 'sub-agent tool call should show test-dummy
|
|
151
|
+
// Internal tool calls should show "test-dummy/haiku", NOT "orchestrator"
|
|
152
|
+
expect(line, 'sub-agent tool call should show test-dummy')
|
|
151
153
|
.toContain('test-dummy');
|
|
154
|
+
expect(line, 'sub-agent tool call should show haiku model')
|
|
155
|
+
.toContain('haiku');
|
|
152
156
|
expect(line, 'sub-agent tool call should NOT show orchestrator')
|
|
153
157
|
.not.toContain(`orchestrator/${result.detectedModel}`);
|
|
154
158
|
}
|
|
@@ -104,13 +104,15 @@ function getContentBlocks(event) {
|
|
|
104
104
|
}
|
|
105
105
|
function formatToolUse(block) {
|
|
106
106
|
const input = block.input ?? {};
|
|
107
|
-
// Special formatting for Agent tool — show sub-agent with color
|
|
107
|
+
// Special formatting for Agent tool — show sub-agent with color + model
|
|
108
108
|
if (block.name === 'Agent') {
|
|
109
109
|
const agentType = input.subagent_type ?? '';
|
|
110
110
|
const desc = input.description ?? '';
|
|
111
|
+
const model = input.model ?? '';
|
|
111
112
|
const label = agentType || desc || 'sub-agent';
|
|
112
113
|
const colored = colorizeAgent(label);
|
|
113
|
-
|
|
114
|
+
const modelSuffix = model ? `${TC.dim}/${model}${TC.reset}` : '';
|
|
115
|
+
return `${ts()}🤖 → ${colored}${modelSuffix}${desc && agentType ? ` ${TC.dim}${desc}${TC.reset}` : ''}`;
|
|
114
116
|
}
|
|
115
117
|
const detail = input.command ?? input.file_path ?? input.pattern ?? input.query ?? '';
|
|
116
118
|
const short = detail.slice(0, 120);
|
|
@@ -214,7 +216,10 @@ export function formatStreamEventTracked(event) {
|
|
|
214
216
|
_agentReturnDepths.push(_formatToolDepth);
|
|
215
217
|
const input = block.input ?? {};
|
|
216
218
|
const name = input.subagent_type || input.description || 'sub-agent';
|
|
217
|
-
|
|
219
|
+
const model = input.model ?? '';
|
|
220
|
+
_agentTag = model
|
|
221
|
+
? `${colorizeAgent(name)}${TC.dim}/${model}${TC.reset} `
|
|
222
|
+
: `${colorizeAgent(name)} `;
|
|
218
223
|
}
|
|
219
224
|
}
|
|
220
225
|
}
|
|
@@ -317,19 +317,37 @@ describe('formatStreamEvent with Agent tool', () => {
|
|
|
317
317
|
beforeEach(() => {
|
|
318
318
|
resetStreamState();
|
|
319
319
|
});
|
|
320
|
-
it('formats Agent tool_use with robot icon
|
|
320
|
+
it('formats Agent tool_use with robot icon, sub-agent type, and model', () => {
|
|
321
321
|
const result = formatStreamEvent({
|
|
322
322
|
type: 'assistant',
|
|
323
323
|
message: {
|
|
324
324
|
content: [{
|
|
325
325
|
type: 'tool_use',
|
|
326
326
|
name: 'Agent',
|
|
327
|
-
input: { subagent_type: 'ui-engineer', description: 'plan frontend' },
|
|
327
|
+
input: { subagent_type: 'ui-engineer', description: 'plan frontend', model: 'opus' },
|
|
328
328
|
}],
|
|
329
329
|
},
|
|
330
330
|
});
|
|
331
331
|
expect(result).toContain('🤖');
|
|
332
332
|
expect(result).toContain('ui-engineer');
|
|
333
|
+
expect(result).toContain('opus');
|
|
334
|
+
});
|
|
335
|
+
it('formats Agent tool_use without model when not specified', () => {
|
|
336
|
+
const result = formatStreamEvent({
|
|
337
|
+
type: 'assistant',
|
|
338
|
+
message: {
|
|
339
|
+
content: [{
|
|
340
|
+
type: 'tool_use',
|
|
341
|
+
name: 'Agent',
|
|
342
|
+
input: { subagent_type: 'Explore', description: 'find files' },
|
|
343
|
+
}],
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
expect(result).toContain('🤖');
|
|
347
|
+
expect(result).toContain('Explore');
|
|
348
|
+
// No /model suffix when model not specified
|
|
349
|
+
const stripped = result.replace(/\x1b\[\d+m/g, '');
|
|
350
|
+
expect(stripped).not.toMatch(/Explore\//);
|
|
333
351
|
});
|
|
334
352
|
it('falls back to description when no subagent_type', () => {
|
|
335
353
|
const result = formatStreamEvent({
|
|
@@ -370,26 +388,27 @@ describe('formatStreamEventTracked', () => {
|
|
|
370
388
|
setAgentTag('orchestrator', 'opus-4.6');
|
|
371
389
|
});
|
|
372
390
|
it('switches tag to sub-agent after Agent tool_use, restores on tool_result', () => {
|
|
373
|
-
// 1. Agent tool_use — should show orchestrator (parent delegates)
|
|
391
|
+
// 1. Agent tool_use — should show orchestrator (parent delegates) + sub-agent with model
|
|
374
392
|
const delegation = formatStreamEventTracked({
|
|
375
393
|
type: 'assistant',
|
|
376
|
-
message: { content: [{ type: 'tool_use', name: 'Agent', input: { subagent_type: 'ui-engineer', description: 'plan' } }] },
|
|
394
|
+
message: { content: [{ type: 'tool_use', name: 'Agent', input: { subagent_type: 'ui-engineer', description: 'plan', model: 'opus' } }] },
|
|
377
395
|
});
|
|
378
396
|
expect(stripAnsi(delegation)).toContain('orchestrator/opus-4.6');
|
|
379
397
|
expect(stripAnsi(delegation)).toContain('🤖');
|
|
380
|
-
|
|
398
|
+
expect(stripAnsi(delegation)).toContain('ui-engineer/opus');
|
|
399
|
+
// 2. Sub-agent's internal tool call — should show ui-engineer/opus
|
|
381
400
|
const subTool = formatStreamEventTracked({
|
|
382
401
|
type: 'assistant',
|
|
383
402
|
message: { content: [{ type: 'tool_use', name: 'Read', input: { file_path: 'src/main.ts' } }] },
|
|
384
403
|
});
|
|
385
|
-
expect(stripAnsi(subTool)).toContain('ui-engineer');
|
|
404
|
+
expect(stripAnsi(subTool)).toContain('ui-engineer/opus');
|
|
386
405
|
expect(stripAnsi(subTool)).not.toContain('orchestrator');
|
|
387
406
|
// 3. Sub-agent's tool result — still sub-agent context
|
|
388
407
|
const subResult = formatStreamEventTracked({
|
|
389
408
|
type: 'user',
|
|
390
409
|
message: { content: [{ type: 'tool_result', content: 'file contents' }] },
|
|
391
410
|
});
|
|
392
|
-
expect(stripAnsi(subResult)).toContain('ui-engineer');
|
|
411
|
+
expect(stripAnsi(subResult)).toContain('ui-engineer/opus');
|
|
393
412
|
// 4. Agent tool result — restores orchestrator
|
|
394
413
|
const agentResult = formatStreamEventTracked({
|
|
395
414
|
type: 'user',
|
|
@@ -404,33 +423,33 @@ describe('formatStreamEventTracked', () => {
|
|
|
404
423
|
expect(stripAnsi(nextTool)).toContain('orchestrator/opus-4.6');
|
|
405
424
|
});
|
|
406
425
|
it('handles nested Agent calls (agent within agent)', () => {
|
|
407
|
-
// orchestrator → backend-ts-architect → (inner tool)
|
|
426
|
+
// orchestrator → backend-ts-architect/opus → (inner tool)
|
|
408
427
|
formatStreamEventTracked({
|
|
409
428
|
type: 'assistant',
|
|
410
|
-
message: { content: [{ type: 'tool_use', name: 'Agent', input: { subagent_type: 'backend-ts-architect' } }] },
|
|
429
|
+
message: { content: [{ type: 'tool_use', name: 'Agent', input: { subagent_type: 'backend-ts-architect', model: 'opus' } }] },
|
|
411
430
|
});
|
|
412
431
|
// backend-ts-architect calls Agent (hypothetical nested delegation)
|
|
413
432
|
formatStreamEventTracked({
|
|
414
433
|
type: 'assistant',
|
|
415
|
-
message: { content: [{ type: 'tool_use', name: 'Agent', input: { subagent_type: 'Explore' } }] },
|
|
434
|
+
message: { content: [{ type: 'tool_use', name: 'Agent', input: { subagent_type: 'Explore', model: 'haiku' } }] },
|
|
416
435
|
});
|
|
417
|
-
// Inner agent's tool
|
|
436
|
+
// Inner agent's tool — should show Explore/haiku
|
|
418
437
|
const innerTool = formatStreamEventTracked({
|
|
419
438
|
type: 'assistant',
|
|
420
439
|
message: { content: [{ type: 'tool_use', name: 'Grep', input: { pattern: 'TODO' } }] },
|
|
421
440
|
});
|
|
422
|
-
expect(stripAnsi(innerTool)).toContain('Explore');
|
|
441
|
+
expect(stripAnsi(innerTool)).toContain('Explore/haiku');
|
|
423
442
|
// Inner agent's tool result
|
|
424
443
|
formatStreamEventTracked({
|
|
425
444
|
type: 'user',
|
|
426
445
|
message: { content: [{ type: 'tool_result', content: 'found' }] },
|
|
427
446
|
});
|
|
428
|
-
// Inner Agent result → back to backend-ts-architect
|
|
447
|
+
// Inner Agent result → back to backend-ts-architect/opus
|
|
429
448
|
const innerAgentResult = formatStreamEventTracked({
|
|
430
449
|
type: 'user',
|
|
431
450
|
message: { content: [{ type: 'tool_result', content: 'explore done' }] },
|
|
432
451
|
});
|
|
433
|
-
expect(stripAnsi(innerAgentResult)).toContain('backend-ts-architect');
|
|
452
|
+
expect(stripAnsi(innerAgentResult)).toContain('backend-ts-architect/opus');
|
|
434
453
|
// Outer Agent result → back to orchestrator
|
|
435
454
|
const outerAgentResult = formatStreamEventTracked({
|
|
436
455
|
type: 'user',
|
|
@@ -106,7 +106,8 @@ At the beginning of EVERY session (including every Ralph Loop iteration):
|
|
|
106
106
|
### 5. Reconcile working state
|
|
107
107
|
Before doing anything, check for unexpected state from user intervention or previous session:
|
|
108
108
|
- **Worktree recovery**: Read MEMORY.md `Current Worktree`. If set, check if the worktree directory exists:
|
|
109
|
-
- Worktree exists + `Current Step` is set → resume
|
|
109
|
+
- Worktree exists + `Current Step` is set → resume using the rules below:
|
|
110
|
+
- **Crash recovery for STEPs 1-10** (planning/implementation/review phase): The architect's plan from the previous session is LOST (it was only in context). You MUST re-delegate to the architect agent (STEP 2) before continuing. The architect will see the existing implementation in the worktree and produce a plan covering only the remaining work. Then continue from STEP 3. **Do NOT attempt to assess, read, or continue implementation yourself without a fresh architect plan.**
|
|
110
111
|
- **Crash recovery for STEP 11**: If `Current Step` contains `11 —` (exactly `11 — COMMIT`, not `11b`):
|
|
111
112
|
- Check `git -C {worktree} status` for uncommitted changes. If present, the STEP 11 commit was incomplete — re-stage all task files + TODO.md + MEMORY.md in the worktree and commit.
|
|
112
113
|
- If worktree is clean, the commit succeeded. Proceed to STEP 11b (CI watch) if git integration active, otherwise STEP 12.
|
|
@@ -119,8 +120,7 @@ Before doing anything, check for unexpected state from user intervention or prev
|
|
|
119
120
|
- **Crash recovery for STEP 12**: If `Current Step` contains `12`:
|
|
120
121
|
- Check if the feature branch has commits ahead of main. If yes, proceed to STEP 12 (merge from main).
|
|
121
122
|
- If already merged: clean up worktree and branch, clear `Current Worktree` from MEMORY.md.
|
|
122
|
-
- **Crash recovery for hotfix**: If `Current Step` starts with `hotfix`, resume in hotfix workflow mode (shortened cycle).
|
|
123
|
-
- Worktree has uncommitted changes matching current task → continue from recorded step
|
|
123
|
+
- **Crash recovery for hotfix**: If `Current Step` starts with `hotfix`, resume in hotfix workflow mode (shortened cycle). Re-delegate to architect (same as STEPs 1-10 recovery above), then continue from the hotfix step.
|
|
124
124
|
- `Current Worktree` is set but directory doesn't exist → orphaned reference. Check if the branch was merged (in `git log main`). If merged: clear `Current Worktree` and `Current Task` from MEMORY.md. If not merged but branch exists: recreate worktree `git worktree add {path} {branch}` and resume. If branch is gone: clear tracking, move to next task.
|
|
125
125
|
- **Orphaned worktrees**: `git worktree list` — if there are worktrees not referenced by MEMORY.md `Current Worktree`:
|
|
126
126
|
- `git worktree remove --force {path}` + `git branch -D {branch}` if the branch is not merged
|