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 the agent name "test-dummy"
139
- expect(agentLinePlain, 'expected agent name "test-dummy" in delegation line')
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
- // Verify sub-agent's internal tool calls show sub-agent name (not orchestrator)
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, not orchestrator')
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
- return `${ts()}🤖 ${colored}${desc && agentType ? ` ${TC.dim}${desc}${TC.reset}` : ''}`;
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
- _agentTag = `${colorizeAgent(name)} `;
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 and sub-agent type', () => {
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
- // 2. Sub-agent's internal tool call — should show ui-engineer
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 from that step (continue working in the worktree)
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). Use the STEP number after `hotfix —` to determine where to continue.
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-workspace",
3
- "version": "1.1.82",
3
+ "version": "1.1.84",
4
4
  "description": "Scaffold a project with Claude Code agents for autonomous AI-driven development",
5
5
  "type": "module",
6
6
  "bin": {