create-claude-workspace 2.1.0 → 2.1.2

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/README.md CHANGED
@@ -11,7 +11,7 @@ One command to scaffold, then let Claude build your TypeScript project.
11
11
  npx create-claude-workspace
12
12
  ```
13
13
 
14
- This scaffolds the `.claude/` folder (agents, scripts, templates, Docker config) into your project.
14
+ This scaffolds the `.claude/` folder (agents, templates, profiles) into your project.
15
15
 
16
16
  Then initialize your project:
17
17
 
@@ -27,13 +27,13 @@ Answer the discovery questions (non-technical). Claude generates the full pipeli
27
27
 
28
28
  ### Start Autonomous Development
29
29
 
30
- Pick one:
30
+ ```bash
31
+ npx create-claude-workspace scheduler # start (1 agent)
32
+ npx create-claude-workspace scheduler --concurrency 3 # 3 parallel agents
33
+ npx create-claude-workspace scheduler --resume # resume from saved state
34
+ ```
31
35
 
32
- - **Multi-agent scheduler (v2)**: `npx create-claude-workspace scheduler` parallel agents, short contexts, TS-driven workflow
33
- - **Multi-agent parallel**: `npx create-claude-workspace scheduler --concurrency 3` — up to N agents working simultaneously
34
- - **Classic loop (v1)**: `npx create-claude-workspace run` — single orchestrator, sequential tasks
35
- - **Docker** (fully isolated): `npx create-claude-workspace docker` — container sandbox, safe `--skip-permissions`
36
- - **Interactive**: `claude --agent orchestrator`, then `/ralph-loop:ralph-loop Continue autonomous development according to CLAUDE.md`
36
+ Or interactively: `claude --agent orchestrator`, then `/ralph-loop:ralph-loop Continue autonomous development according to CLAUDE.md`
37
37
 
38
38
  ### Scheduler v2 (Multi-Agent)
39
39
 
@@ -70,26 +70,9 @@ The scheduler reads the inbox every iteration and processes messages immediately
70
70
  ```bash
71
71
  npx create-claude-workspace [directory] # scaffold into a specific directory
72
72
  npx create-claude-workspace --update # overwrite agents with latest version
73
- npx create-claude-workspace --run # scaffold + start autonomous loop
74
- npx create-claude-workspace --docker # scaffold + run in Docker
75
73
  npx create-claude-workspace validate # check prerequisites (Claude CLI, auth, git)
76
74
  ```
77
75
 
78
- ### Docker Options
79
-
80
- ```bash
81
- npx create-claude-workspace docker --max-iterations 20 # limit iterations
82
- npx create-claude-workspace docker --max-turns 80 # more turns per invocation
83
- npx create-claude-workspace docker --shell # interactive shell
84
- npx create-claude-workspace docker --rebuild # force rebuild image
85
- npx create-claude-workspace docker --login # run 'claude auth login' on host first
86
- npx create-claude-workspace docker --resume-session <id> # resume a previous session
87
- ANTHROPIC_API_KEY=sk-... npx create-claude-workspace docker # API key
88
- ```
89
-
90
- **Authentication:** The Docker container uses your host machine's Claude auth automatically.
91
- Authenticate on your host first (`claude auth login`), or pass an API key.
92
-
93
76
  ## Agents
94
77
 
95
78
  | Agent | Purpose |
@@ -132,52 +115,9 @@ ralph-loop -> orchestrator agent (autonomous development)
132
115
  |-> Next task
133
116
  ```
134
117
 
135
- ## Autonomous Mode (Unattended)
136
-
137
- The autonomous loop runs Claude Code in separate invocations with clean context per task. MEMORY.md maintains continuity between invocations.
138
-
139
- ```bash
140
- # Unattended (--skip-permissions is added automatically)
141
- npx create-claude-workspace run
142
-
143
- # Resume after Ctrl+C (continues from checkpoint + resumes last Claude session)
144
- npx create-claude-workspace run --resume
145
-
146
- # Custom options
147
- npx create-claude-workspace run --max-iterations 20 --delay 10000
148
-
149
- # All options:
150
- # --max-iterations <n> Safety limit (default: 50)
151
- # --max-turns <n> Max turns per Claude invocation (default: 50)
152
- # --delay <ms> Pause between tasks (default: 5000)
153
- # --cooldown <ms> Wait after error (default: 60000)
154
- # --process-timeout <ms> Max time per invocation (default: 1800000 = 30min)
155
- # --activity-timeout <ms> Max silence before kill (default: 300000 = 5min)
156
- # --resume Resume from checkpoint state
157
- # --resume-session <id> Resume specific Claude session
158
- # --notify-command <cmd> Shell command on critical events
159
- # --log-file <path> Log file (default: .claude/autonomous.log)
160
- # --project-dir <path> Target project (default: cwd)
161
- # --no-lock Disable lock file
162
- # --no-pull Skip auto git pull
163
- # --dry-run Validate prerequisites only
164
- # --help Show help
165
- ```
166
-
167
- **How it works:**
168
- - Each iteration = fresh `claude -p` call = clean context (no context overflow)
169
- - Checkpoint state (`.claude/autonomous-state.json`) tracks progress between invocations
170
- - Rate limits handled with exponential backoff (up to 30 min between retries)
171
- - `--resume` continues from last checkpoint and auto-resumes the interrupted Claude session
172
- - Graceful shutdown: Ctrl+C stops after current iteration completes (Ctrl+C again to force)
173
- - Stops when all TODO.md tasks complete or max iterations reached
174
- - Pre-flight checks: Claude CLI, auth, git identity, filesystem, git state
175
-
176
- **vs ralph-loop:** Ralph-loop runs within one conversation (context fills up). This script runs separate conversations (unlimited iterations).
177
-
178
118
  ## Fully Unattended Setup (PLAN.md)
179
119
 
180
- For server/CI environments where no human interaction is possible, create a `PLAN.md` file in the project root before starting the autonomous loop. The project-initializer reads it and skips the discovery conversation.
120
+ For server/CI environments where no human interaction is possible, create a `PLAN.md` file in the project root before starting the scheduler. The project-initializer reads it and skips the discovery conversation.
181
121
 
182
122
  ```bash
183
123
  # 1. Copy the template
@@ -186,13 +126,9 @@ cp .claude/templates/PLAN.md PLAN.md
186
126
  # 2. Fill in your project details, credentials, and preferences
187
127
 
188
128
  # 3. Start — no interaction needed
189
- npx create-claude-workspace run
190
- # or in Docker:
191
- npx create-claude-workspace docker
129
+ npx create-claude-workspace scheduler
192
130
  ```
193
131
 
194
- The template covers: project info, product vision, tech stack, credentials (API keys, git tokens, npm auth), workflow mode, and runtime constraints.
195
-
196
132
  ## Required Plugins
197
133
 
198
134
  Install before starting:
@@ -215,15 +151,15 @@ claude plugin add commit-commands # git workflow shortcuts
215
151
  # Figma MCP — configure in Claude Code MCP settings with API token
216
152
  ```
217
153
 
218
- ## Frontend Profiles
154
+ ## Frontend Agents
219
155
 
220
- Framework-specific best practices are stored in `.claude/profiles/`. During project initialization, the correct profile is detected and copied to `.claude/profiles/frontend.md` in the target project.
156
+ Framework-specific agents are shipped in `.claude/agents/`. During project initialization, the correct agent is selected based on the detected framework.
221
157
 
222
- **Shipped profiles:**
223
- - `angular.md` — Angular (signals, standalone, zoneless, SSR, TestBed, theme system)
224
- - `react.md` — React (hooks, server components, Testing Library)
225
- - `vue.md` — Vue (Composition API, Pinia, Vue Test Utils)
226
- - `svelte.md` — Svelte (runes, $state, SvelteKit)
158
+ **Shipped agents:**
159
+ - `angular-engineer.md` — Angular (signals, standalone, zoneless, SSR, TestBed, theme system)
160
+ - `react-engineer.md` — React (hooks, server components, Testing Library)
161
+ - `vue-engineer.md` — Vue (Composition API, Pinia, Vue Test Utils)
162
+ - `svelte-engineer.md` — Svelte (runes, $state, SvelteKit)
227
163
 
228
164
  Only `ui-engineer`, `senior-code-reviewer`, and `test-engineer` read the frontend profile. Backend and infrastructure agents are unaffected — they don't receive framework-specific context.
229
165
 
@@ -6,10 +6,16 @@ export class OrchestratorClient {
6
6
  pool;
7
7
  projectDir;
8
8
  logger;
9
+ onMessage;
10
+ onSpawnStart;
11
+ onSpawnEnd;
9
12
  constructor(opts) {
10
13
  this.pool = opts.pool;
11
14
  this.projectDir = opts.projectDir;
12
15
  this.logger = opts.logger;
16
+ this.onMessage = opts.onMessage;
17
+ this.onSpawnStart = opts.onSpawnStart;
18
+ this.onSpawnEnd = opts.onSpawnEnd;
13
19
  }
14
20
  /**
15
21
  * Ask orchestrator to route a task to the best agent.
@@ -83,9 +89,16 @@ export class OrchestratorClient {
83
89
  cwd: this.projectDir,
84
90
  prompt,
85
91
  model: ORCHESTRATOR_MODEL,
92
+ onMessage: this.onMessage,
86
93
  };
87
94
  this.logger.info('Consulting orchestrator AI...');
88
- return this.pool.spawn(slot.id, spawnOpts);
95
+ this.onSpawnStart?.('orchestrator-ai');
96
+ try {
97
+ return await this.pool.spawn(slot.id, spawnOpts);
98
+ }
99
+ finally {
100
+ this.onSpawnEnd?.();
101
+ }
89
102
  }
90
103
  }
91
104
  // ─── JSON parsing helpers ───
@@ -223,11 +223,18 @@ export async function runScheduler(opts) {
223
223
  skipPermissions: opts.skipPermissions,
224
224
  logger,
225
225
  });
226
+ // SDK message handler — streams tool use, text, tokens to TUI
227
+ const onMessage = (msg) => tui.handleMessage(msg);
228
+ const onSpawnStart = (name) => tui.pushAgent(name);
229
+ const onSpawnEnd = () => tui.popAgent();
226
230
  // Orchestrator client
227
231
  const orchestrator = new OrchestratorClient({
228
232
  pool,
229
233
  projectDir: opts.projectDir,
230
234
  logger,
235
+ onMessage,
236
+ onSpawnStart,
237
+ onSpawnEnd,
231
238
  });
232
239
  // Signal handling
233
240
  let stopping = false;
@@ -255,6 +262,9 @@ export async function runScheduler(opts) {
255
262
  state,
256
263
  opts,
257
264
  logger,
265
+ onMessage,
266
+ onSpawnStart,
267
+ onSpawnEnd,
258
268
  });
259
269
  if (!workDone) {
260
270
  // No work → idle polling
@@ -20,7 +20,7 @@ const MAX_CI_FIXES = 3;
20
20
  * Returns true if work was done, false if idle.
21
21
  */
22
22
  export async function runIteration(deps) {
23
- const { pool, orchestrator, state, opts, logger } = deps;
23
+ const { pool, orchestrator, state, opts, logger, onMessage, onSpawnStart, onSpawnEnd } = deps;
24
24
  const projectDir = opts.projectDir;
25
25
  // Rotate log if needed
26
26
  rotateLog(projectDir);
@@ -164,7 +164,7 @@ export async function runIteration(deps) {
164
164
  }
165
165
  // ─── Pipeline execution ───
166
166
  async function runTaskPipeline(task, workerId, agents, deps) {
167
- const { pool, orchestrator, state, opts, logger } = deps;
167
+ const { pool, orchestrator, state, opts, logger, onMessage, onSpawnStart, onSpawnEnd } = deps;
168
168
  const projectDir = opts.projectDir;
169
169
  // Create worktree
170
170
  const slug = taskToSlug(task);
@@ -204,7 +204,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
204
204
  cwd: worktreePath,
205
205
  prompt: buildPlanPrompt({ task, worktreePath, projectDir }),
206
206
  model: getAgentModel(pipeline.assignedAgent, agents),
207
- }, state, task.id, logger);
207
+ }, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
208
208
  if (!planResult.success) {
209
209
  logger.error(`[${task.id}] Planning failed: ${planResult.error}`);
210
210
  return false;
@@ -228,7 +228,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
228
228
  apiContract: pipeline.apiContract ?? undefined,
229
229
  }),
230
230
  model: getAgentModel(pipeline.assignedAgent, agents),
231
- }, state, task.id, logger);
231
+ }, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
232
232
  if (!implResult.success) {
233
233
  logger.error(`[${task.id}] Implementation failed: ${implResult.error}`);
234
234
  return false;
@@ -246,7 +246,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
246
246
  testingSection: pipeline.testingSection ?? undefined,
247
247
  }),
248
248
  model: getAgentModel(testRouting.agent, agents),
249
- }, state, task.id, logger);
249
+ }, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
250
250
  if (!testResult.success) {
251
251
  pipeline.buildFixes++;
252
252
  if (pipeline.buildFixes >= MAX_BUILD_FIXES) {
@@ -274,7 +274,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
274
274
  testingSection: pipeline.testingSection ?? undefined,
275
275
  }),
276
276
  model: getAgentModel(reviewRouting.agent, agents),
277
- }, state, task.id, logger);
277
+ }, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
278
278
  if (reviewResult.output.includes('**PASS**') || reviewResult.output.includes('PASS')) {
279
279
  reviewPassed = true;
280
280
  }
@@ -296,7 +296,7 @@ async function runTaskPipeline(task, workerId, agents, deps) {
296
296
  reviewFindings: pipeline.reviewFindings,
297
297
  }),
298
298
  model: getAgentModel(pipeline.assignedAgent, agents),
299
- }, state, task.id, logger);
299
+ }, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
300
300
  pipeline.step = 're-review';
301
301
  }
302
302
  }
@@ -360,13 +360,17 @@ async function runTaskPipeline(task, workerId, agents, deps) {
360
360
  }
361
361
  }
362
362
  // ─── Helpers ───
363
- async function spawnAgent(pool, slotId, opts, state, taskId, logger) {
363
+ async function spawnAgent(pool, slotId, opts, state, taskId, logger, onMessage, onSpawnStart, onSpawnEnd) {
364
+ const agentName = opts.agent ?? 'claude';
365
+ onSpawnStart?.(agentName);
364
366
  // Check for existing session (resume on crash)
365
367
  const existingSession = getSession(state, taskId);
366
368
  const result = await pool.spawn(slotId, {
367
369
  ...opts,
368
370
  resume: existingSession ?? undefined,
371
+ onMessage,
369
372
  });
373
+ onSpawnEnd?.();
370
374
  // Record session for crash recovery
371
375
  if (result.sessionId) {
372
376
  recordSession(state, taskId, result.sessionId);
@@ -466,6 +466,9 @@ export class TUI {
466
466
  if (msg.session_id)
467
467
  this.fileOnly(`SESSION: ${msg.session_id}`);
468
468
  }
469
+ pushAgent(name) { this.state.agents.push(name); }
470
+ popAgent() { if (this.state.agents.length > 1)
471
+ this.state.agents.pop(); }
469
472
  resetAgentStack() { this.state.agents = this.topAgent ? [this.topAgent] : []; }
470
473
  getSessionId(msg) { return msg.session_id ?? null; }
471
474
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-workspace",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "author": "",
5
5
  "repository": {
6
6
  "type": "git",