@multiclawcli/core 0.3.0 → 0.3.1

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/index.js CHANGED
@@ -1,9 +1,1069 @@
1
- export * from "./types";
2
- export { EventBus } from "./event/EventBus";
3
- export { Orchestrator } from "./orchestrator/Orchestrator";
4
- export { buildStages } from "./orchestrator/StageBuilder";
5
- export { runtimeRegistry, ClaudeRuntime } from "./runtime";
6
- export { RuntimeBase } from "./runtime/RuntimeBase";
7
- export { defineConfig } from "./config/defineConfig";
8
- export { agents } from "./agents";
9
- //# sourceMappingURL=index.js.map
1
+ // src/event/EventBus.ts
2
+ var EventBus = class {
3
+ constructor() {
4
+ this.listeners = /* @__PURE__ */ new Set();
5
+ }
6
+ subscribe(listener) {
7
+ this.listeners.add(listener);
8
+ return () => this.listeners.delete(listener);
9
+ }
10
+ emit(event) {
11
+ for (const listener of this.listeners) {
12
+ try {
13
+ listener(event);
14
+ } catch (err) {
15
+ console.error("EventBus listener error:", err);
16
+ }
17
+ }
18
+ }
19
+ clear() {
20
+ this.listeners.clear();
21
+ }
22
+ };
23
+
24
+ // src/orchestrator/Orchestrator.ts
25
+ import * as fs3 from "fs";
26
+ import * as path3 from "path";
27
+
28
+ // src/orchestrator/ContextManager.ts
29
+ var ContextManager = class {
30
+ constructor(initial) {
31
+ this.store = /* @__PURE__ */ new Map();
32
+ if (initial) for (const [k, v] of Object.entries(initial)) this.store.set(k, v);
33
+ }
34
+ set(key, value) {
35
+ this.store.set(key, value);
36
+ }
37
+ get(key) {
38
+ return this.store.get(key);
39
+ }
40
+ snapshot() {
41
+ return Object.fromEntries(this.store.entries());
42
+ }
43
+ };
44
+
45
+ // src/orchestrator/AgentRunner.ts
46
+ import * as fs2 from "fs";
47
+ import * as path2 from "path";
48
+
49
+ // src/runtime/ClaudeRuntime.ts
50
+ import { spawn, execSync } from "child_process";
51
+
52
+ // src/runtime/RuntimeBase.ts
53
+ var RuntimeBase = class {
54
+ };
55
+
56
+ // src/runtime/ClaudeRuntime.ts
57
+ var ClaudeRuntime = class extends RuntimeBase {
58
+ constructor() {
59
+ super(...arguments);
60
+ this.name = "claude";
61
+ }
62
+ async checkAvailable() {
63
+ try {
64
+ execSync("claude --version", { stdio: "ignore" });
65
+ return true;
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+ async execute(task) {
71
+ const args = [
72
+ "-p",
73
+ task.prompt,
74
+ "--allowedTools",
75
+ task.tools.join(",")
76
+ ];
77
+ if (task.systemPrompt) args.push("--system-prompt", task.systemPrompt);
78
+ if (task.model) args.push("--model", task.model);
79
+ return new Promise((resolve4, reject) => {
80
+ const child = spawn("claude", args, { cwd: task.workDir, env: process.env });
81
+ task.signal?.addEventListener("abort", () => child.kill());
82
+ let output = "";
83
+ let errorOutput = "";
84
+ child.stdout.on("data", (d) => {
85
+ const chunk = d.toString();
86
+ output += chunk;
87
+ task.onOutput?.(chunk);
88
+ });
89
+ child.stderr.on("data", (d) => {
90
+ errorOutput += d.toString();
91
+ });
92
+ child.on("close", (code) => {
93
+ if (task.signal?.aborted) return;
94
+ code === 0 ? resolve4({ output, exitCode: code }) : reject(new Error(`claude exited with code ${code}: ${errorOutput}`));
95
+ });
96
+ child.on("error", (err) => reject(new Error(`Failed to start claude: ${err.message}`)));
97
+ });
98
+ }
99
+ };
100
+
101
+ // src/runtime/index.ts
102
+ var RuntimeRegistry = class {
103
+ constructor() {
104
+ this.runtimes = /* @__PURE__ */ new Map();
105
+ this.register(new ClaudeRuntime());
106
+ }
107
+ register(r) {
108
+ this.runtimes.set(r.name, r);
109
+ }
110
+ get(name) {
111
+ const r = this.runtimes.get(name);
112
+ if (!r) throw new Error(`Runtime not found: "${name}". Available: ${[...this.runtimes.keys()].join(", ")}`);
113
+ return r;
114
+ }
115
+ has(name) {
116
+ return this.runtimes.has(name);
117
+ }
118
+ list() {
119
+ return [...this.runtimes.keys()];
120
+ }
121
+ };
122
+ var runtimeRegistry = new RuntimeRegistry();
123
+
124
+ // src/utils/retry.ts
125
+ function sleep(ms) {
126
+ return new Promise((r) => setTimeout(r, ms));
127
+ }
128
+ async function withRetry(fn, retries, onRetry, delayMs = 2e3) {
129
+ let lastError = new Error("Unknown error");
130
+ for (let attempt = 1; attempt <= retries + 1; attempt++) {
131
+ try {
132
+ return await fn(attempt);
133
+ } catch (err) {
134
+ lastError = err;
135
+ if (attempt <= retries) {
136
+ onRetry?.(attempt, err);
137
+ await sleep(delayMs * attempt);
138
+ }
139
+ }
140
+ }
141
+ throw lastError;
142
+ }
143
+
144
+ // src/utils/timeout.ts
145
+ function withTimeout(factory, ms, label) {
146
+ const controller = new AbortController();
147
+ let timer;
148
+ const timeout = new Promise((_, reject) => {
149
+ timer = setTimeout(() => {
150
+ controller.abort();
151
+ reject(new Error(`[${label}] timed out (${ms / 1e3}s)`));
152
+ }, ms);
153
+ });
154
+ return Promise.race([factory(controller.signal), timeout]).finally(() => clearTimeout(timer));
155
+ }
156
+
157
+ // src/utils/prompt.ts
158
+ import * as fs from "fs/promises";
159
+ import * as path from "path";
160
+ async function renderPrompt(template, context, workDir) {
161
+ let result = template;
162
+ const fileMatches = [...template.matchAll(/\{\{file:([^}]+)\}\}/g)];
163
+ for (const match of fileMatches) {
164
+ const filePath = path.resolve(workDir, match[1].trim());
165
+ try {
166
+ const content = await fs.readFile(filePath, "utf-8");
167
+ result = result.replace(match[0], content);
168
+ } catch {
169
+ result = result.replace(match[0], `[file not found: ${match[1]}]`);
170
+ }
171
+ }
172
+ for (const [key, value] of Object.entries(context)) {
173
+ result = result.replaceAll(`{{${key}}}`, value);
174
+ }
175
+ return result;
176
+ }
177
+
178
+ // src/orchestrator/AgentRunner.ts
179
+ var AgentRunner = class {
180
+ constructor(eventBus, contextManager, globalWorkDir) {
181
+ this.eventBus = eventBus;
182
+ this.contextManager = contextManager;
183
+ this.globalWorkDir = globalWorkDir;
184
+ }
185
+ async run(agent) {
186
+ const startTime = /* @__PURE__ */ new Date();
187
+ const workDir = agent.workDir ? path2.resolve(this.globalWorkDir, agent.workDir) : this.globalWorkDir;
188
+ fs2.mkdirSync(workDir, { recursive: true });
189
+ const runtime = runtimeRegistry.get(agent.runtime ?? "claude");
190
+ const retries = agent.retries ?? 0;
191
+ const timeout = agent.timeout ?? 6e5;
192
+ const tools = agent.tools ?? ["Read", "Write", "Bash"];
193
+ const model = agent.model ?? agent.models?.[agent.runtime ?? "claude"];
194
+ this.eventBus.emit({
195
+ type: "agent:start",
196
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
197
+ payload: { agentId: agent.id, agentName: agent.name, icon: agent.icon, model, taskTitle: agent.taskTitle }
198
+ });
199
+ let attempts = 0;
200
+ let output = "";
201
+ try {
202
+ output = await withTimeout(
203
+ (signal) => withRetry(async (attempt) => {
204
+ attempts = attempt;
205
+ const prompt = await renderPrompt(
206
+ agent.taskPrompt,
207
+ this.contextManager.snapshot(),
208
+ this.globalWorkDir
209
+ );
210
+ return (await runtime.execute({
211
+ agentId: agent.id,
212
+ systemPrompt: agent.systemPrompt,
213
+ prompt,
214
+ tools,
215
+ workDir,
216
+ timeout,
217
+ signal,
218
+ model,
219
+ onOutput: (chunk) => {
220
+ this.eventBus.emit({
221
+ type: "agent:output",
222
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
223
+ payload: { agentId: agent.id, chunk }
224
+ });
225
+ }
226
+ })).output;
227
+ }, retries, (attempt, err) => {
228
+ this.eventBus.emit({
229
+ type: "agent:retrying",
230
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
231
+ payload: {
232
+ agentId: agent.id,
233
+ agentName: agent.name,
234
+ attempt,
235
+ maxAttempts: retries + 1,
236
+ error: err.message
237
+ }
238
+ });
239
+ }),
240
+ timeout,
241
+ agent.name
242
+ );
243
+ const endTime = /* @__PURE__ */ new Date();
244
+ const result = {
245
+ agentId: agent.id,
246
+ agentName: agent.name,
247
+ status: "success",
248
+ output,
249
+ duration: endTime.getTime() - startTime.getTime(),
250
+ attempts,
251
+ startTime: startTime.toISOString(),
252
+ endTime: endTime.toISOString()
253
+ };
254
+ this.eventBus.emit({
255
+ type: "agent:complete",
256
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
257
+ payload: result
258
+ });
259
+ return result;
260
+ } catch (err) {
261
+ const endTime = /* @__PURE__ */ new Date();
262
+ const result = {
263
+ agentId: agent.id,
264
+ agentName: agent.name,
265
+ status: "failed",
266
+ output,
267
+ duration: endTime.getTime() - startTime.getTime(),
268
+ attempts,
269
+ error: err.message,
270
+ startTime: startTime.toISOString(),
271
+ endTime: endTime.toISOString()
272
+ };
273
+ this.eventBus.emit({
274
+ type: "agent:failed",
275
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
276
+ payload: result
277
+ });
278
+ return result;
279
+ }
280
+ }
281
+ };
282
+
283
+ // src/orchestrator/StageBuilder.ts
284
+ function buildStages(agents2) {
285
+ const stages = [];
286
+ const completed = /* @__PURE__ */ new Set();
287
+ let remaining = [...agents2];
288
+ const allIds = new Set(agents2.map((a) => a.id));
289
+ for (const a of agents2) {
290
+ for (const dep of a.dependsOn ?? []) {
291
+ if (!allIds.has(dep)) {
292
+ throw new Error(`Agent "${a.id}" depends on "${dep}" which does not exist`);
293
+ }
294
+ }
295
+ }
296
+ while (remaining.length > 0) {
297
+ const stage = remaining.filter(
298
+ (a) => (a.dependsOn ?? []).every((dep) => completed.has(dep))
299
+ );
300
+ if (stage.length === 0) {
301
+ const ids = remaining.map((a) => a.id).join(", ");
302
+ throw new Error(`Circular or unsatisfiable dependency detected: ${ids}`);
303
+ }
304
+ stages.push(stage);
305
+ stage.forEach((a) => completed.add(a.id));
306
+ remaining = remaining.filter((a) => !stage.includes(a));
307
+ }
308
+ return stages;
309
+ }
310
+
311
+ // src/orchestrator/Orchestrator.ts
312
+ var Orchestrator = class {
313
+ constructor(config) {
314
+ this.config = config;
315
+ this.eventBus = new EventBus();
316
+ this.workDir = path3.resolve(config.workDir);
317
+ fs3.mkdirSync(this.workDir, { recursive: true });
318
+ this.contextManager = new ContextManager(config.context);
319
+ this.runner = new AgentRunner(this.eventBus, this.contextManager, this.workDir);
320
+ }
321
+ async run() {
322
+ const startTime = /* @__PURE__ */ new Date();
323
+ const agents2 = await this.resolveAgents();
324
+ this.contextManager.set(
325
+ "pipelineAgents",
326
+ agents2.map((a) => `${a.id}: ${a.name}`).join("\n")
327
+ );
328
+ let stages = buildStages(agents2);
329
+ const getModel = (agent) => {
330
+ return agent.model ?? agent.models?.[agent.runtime ?? "claude"];
331
+ };
332
+ const stageInfos = stages.map((stage, i) => ({
333
+ stageIndex: i,
334
+ agents: stage.map((a) => ({ id: a.id, name: a.name, icon: a.icon, model: getModel(a), taskTitle: a.taskTitle }))
335
+ }));
336
+ this.eventBus.emit({
337
+ type: "orchestration:start",
338
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
339
+ payload: { name: this.config.name, requirement: this.config.context?.requirement, totalAgents: agents2.length, stages: stageInfos }
340
+ });
341
+ const results = [];
342
+ let allSuccess = true;
343
+ for (let i = 0; i < stages.length; i++) {
344
+ const stage = stages[i];
345
+ this.eventBus.emit({
346
+ type: "stage:start",
347
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
348
+ payload: { stageIndex: i, agentIds: stage.map((a) => a.id) }
349
+ });
350
+ const stageResults = await this.runStage(stage);
351
+ results.push(...stageResults);
352
+ if (i === 0 && stage.some((a) => a.id === "architect") && stageResults.some((r) => r.status === "success")) {
353
+ await this.applyTaskPlan(agents2);
354
+ stages = buildStages(agents2);
355
+ }
356
+ for (const r of stageResults) {
357
+ if (r.status !== "success") {
358
+ allSuccess = false;
359
+ if (!this.config.continueOnError) break;
360
+ }
361
+ }
362
+ this.eventBus.emit({
363
+ type: "stage:complete",
364
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
365
+ payload: { stageIndex: i }
366
+ });
367
+ if (!allSuccess && !this.config.continueOnError) {
368
+ for (const remainingStage of stages.slice(i + 1)) {
369
+ for (const agent of remainingStage) {
370
+ this.eventBus.emit({
371
+ type: "agent:skipped",
372
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
373
+ payload: { agentId: agent.id, agentName: agent.name, icon: agent.icon }
374
+ });
375
+ }
376
+ }
377
+ break;
378
+ }
379
+ }
380
+ const endTime = /* @__PURE__ */ new Date();
381
+ const result = {
382
+ name: this.config.name,
383
+ success: allSuccess,
384
+ totalDuration: endTime.getTime() - startTime.getTime(),
385
+ agentResults: results,
386
+ startTime: startTime.toISOString(),
387
+ endTime: endTime.toISOString()
388
+ };
389
+ this.eventBus.emit({
390
+ type: "orchestration:complete",
391
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
392
+ payload: result
393
+ });
394
+ return result;
395
+ }
396
+ async resolveAgents() {
397
+ if (!this.config.leader || this.config.useLeader === false) return this.config.agents;
398
+ this.contextManager.set(
399
+ "availableAgents",
400
+ this.config.agents.map((a) => `${a.id}: ${a.name}`).join("\n")
401
+ );
402
+ const result = await this.runner.run(this.config.leader);
403
+ if (result.status !== "success") return this.config.agents;
404
+ const match = result.output.match(/\{[\s\S]*?"run"\s*:\s*\[[\s\S]*?\]\s*\}/);
405
+ if (!match) return this.config.agents;
406
+ let plan;
407
+ try {
408
+ plan = JSON.parse(match[0]);
409
+ } catch {
410
+ return this.config.agents;
411
+ }
412
+ const runSet = new Set(plan.run);
413
+ const skippedIds = new Set(
414
+ this.config.agents.filter((a) => !runSet.has(a.id)).map((a) => a.id)
415
+ );
416
+ for (const agent of this.config.agents.filter((a) => skippedIds.has(a.id))) {
417
+ this.eventBus.emit({
418
+ type: "agent:skipped",
419
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
420
+ payload: { agentId: agent.id, agentName: agent.name, icon: agent.icon }
421
+ });
422
+ }
423
+ return this.config.agents.filter((a) => runSet.has(a.id)).map((a) => ({
424
+ ...a,
425
+ dependsOn: a.dependsOn?.filter((dep) => !skippedIds.has(dep))
426
+ }));
427
+ }
428
+ async runStage(agents2) {
429
+ const { maxConcurrency } = this.config;
430
+ if (!maxConcurrency || agents2.length <= maxConcurrency) {
431
+ return Promise.all(agents2.map((a) => this.runner.run(a)));
432
+ }
433
+ const results = [];
434
+ for (let i = 0; i < agents2.length; i += maxConcurrency) {
435
+ const batch = agents2.slice(i, i + maxConcurrency);
436
+ results.push(...await Promise.all(batch.map((a) => this.runner.run(a))));
437
+ }
438
+ return results;
439
+ }
440
+ async applyTaskPlan(agents2) {
441
+ try {
442
+ const taskPlanPath = path3.join(this.workDir, "task-plan.json");
443
+ if (!fs3.existsSync(taskPlanPath)) return;
444
+ const content = fs3.readFileSync(taskPlanPath, "utf-8");
445
+ const taskPlan = JSON.parse(content);
446
+ const taskMap = new Map(taskPlan.tasks.map((t) => [t.id, t]));
447
+ for (const agent of agents2) {
448
+ const task = taskMap.get(agent.id);
449
+ if (task) {
450
+ agent.taskTitle = task.title;
451
+ agent.dependsOn = task.dependsOn;
452
+ }
453
+ }
454
+ } catch {
455
+ }
456
+ }
457
+ };
458
+
459
+ // src/config/defineConfig.ts
460
+ function defineConfig(config) {
461
+ return config;
462
+ }
463
+
464
+ // src/agents/leader.ts
465
+ var SYSTEM = `You are the project leader. Analyze the requirement and the current state of the codebase, then decide the minimum set of agents needed to complete the task.
466
+
467
+ Do not apply fixed rules \u2014 use your judgment based on the actual requirement and code. A minor bug fix may need only a developer and code-reviewer; a complex new feature may need the full pipeline. Read the code to understand the impact before deciding.
468
+
469
+ Output ONLY a single JSON object on its own line \u2014 no prose, no markdown fences:
470
+ {"run": ["agent-id-1", "agent-id-2"]}`;
471
+ var TASK = `## Project
472
+ {{projectName}}
473
+
474
+ ## Requirement
475
+ {{requirement}}
476
+
477
+ ## Available Agents
478
+ {{availableAgents}}
479
+
480
+ ## Task
481
+ Read the codebase to understand the current state, then decide which agents to run.
482
+ Output a single JSON object: {"run": ["agent-id-1", ...]}
483
+ Agent IDs must come from the Available Agents list above.`;
484
+ function leader(overrides) {
485
+ return {
486
+ id: "leader",
487
+ name: "Leader",
488
+ icon: "\u2605",
489
+ models: { claude: "sonnet" },
490
+ systemPrompt: SYSTEM,
491
+ taskPrompt: TASK,
492
+ tools: ["Read", "Bash"],
493
+ ...overrides
494
+ };
495
+ }
496
+
497
+ // src/agents/architect.ts
498
+ var SYSTEM2 = `You are the technical architect for this project. Your responsibility is technical analysis and design \u2014 you do not write feature code.
499
+
500
+ Responsibilities:
501
+ - Understand requirements, identify key technical risks and affected components
502
+ - Review existing code (if any) and propose minimal-change solutions
503
+ - Define module boundaries, interface contracts, and directory structure
504
+ - Provide a clear design foundation for developers and testers
505
+ - Decompose the work into per-agent subtasks based on who is actually in the pipeline
506
+
507
+ Deliverables (save to the working directory):
508
+ - architecture.md \u2014 architecture plan (module breakdown, directory structure, technology choices, key component descriptions)
509
+ - api-spec.md \u2014 complete API specification (paths, methods, request/response formats, status codes, error handling)
510
+ - task-plan.json \u2014 structured task breakdown for all implementation agents in the pipeline (see format below)
511
+
512
+ task-plan.json format:
513
+ {
514
+ "projectName": "<project name>",
515
+ "requirement": "<one-line summary>",
516
+ "tasks": [
517
+ {
518
+ "id": "<agent id exactly as listed in pipelineAgents>",
519
+ "title": "<short task title>",
520
+ "scope": "<concrete description of what this agent should build or produce>",
521
+ "dependsOn": ["<other agent id>"]
522
+ }
523
+ ]
524
+ }
525
+
526
+ Rules for task-plan.json:
527
+ - Only create tasks for implementation agents (those that produce code, designs, or infrastructure). Skip review agents (code-reviewer, arch-design-reviewer, ui-design-reviewer, reviewer) and planning agents (product-manager, architect, leader).
528
+ - Use the exact agent id from pipelineAgents as the task id.
529
+ - If an agent is not in pipelineAgents, do not create a task for it.
530
+ - dependsOn references other task ids, not agent names.
531
+
532
+ Principles: Execute tasks directly without asking for confirmation. If information is missing, mark the file with BLOCKED and describe what is needed. Do not change the requirement scope. Do not write business feature code. Do not refactor beyond the task scope.`;
533
+ var TASK2 = `## Project
534
+ {{projectName}}
535
+
536
+ ## Requirement
537
+ {{requirement}}
538
+
539
+ ## Pipeline Agents
540
+ The following agents will run in this pipeline (id: name):
541
+ {{pipelineAgents}}
542
+
543
+ ## Task
544
+ Analyze the requirement and create three files in the current directory:
545
+ 1. architecture.md \u2014 architecture plan (module breakdown, directory structure, technology choices, key component descriptions, affected scope)
546
+ 2. api-spec.md \u2014 complete API specification (paths, methods, request parameters, response formats, status codes, error handling)
547
+ 3. task-plan.json \u2014 task breakdown for each implementation agent listed in the pipeline above
548
+
549
+ For task-plan.json: create one task per implementation agent (skip reviewers and planners). Each task's scope should be concrete enough that the agent knows exactly what to build without ambiguity. Set dependsOn to reflect actual execution order constraints (e.g. devops depends on backend and frontend).
550
+
551
+ Create the files directly. No extra output.`;
552
+ function architect(overrides) {
553
+ return {
554
+ id: "architect",
555
+ name: "Architect",
556
+ icon: "\u25C6",
557
+ models: { claude: "opus" },
558
+ systemPrompt: SYSTEM2,
559
+ taskPrompt: TASK2,
560
+ tools: ["Read", "Write"],
561
+ ...overrides
562
+ };
563
+ }
564
+
565
+ // src/agents/product-manager.ts
566
+ var SYSTEM3 = `You are the product manager. Your responsibility is to transform vague business ideas into clear, development-ready, testable product requirement documents.
567
+
568
+ Responsibilities:
569
+ - Analyze business goals and user scenarios
570
+ - Define feature scope, business rules, and acceptance criteria
571
+ - Structure "what is needed" into a PRD that the development team can execute directly
572
+
573
+ Deliverables (save to the working directory):
574
+ - requirements.md \u2014 product requirements document containing: one-line definition, goals and success metrics, user roles, functional requirements (FR-), business rules (BR-), acceptance criteria (AC-), open questions (OP-)
575
+
576
+ Principles: Output the document directly without preamble. Mark uncertain items as OP- (open questions) rather than guessing. Do not do technical architecture design. Do not write feature code.`;
577
+ var TASK3 = `## Business Context
578
+ {{requirement}}
579
+
580
+ ## Task
581
+ Based on the context above, create requirements.md in the current directory containing:
582
+ - One-line definition (what this product/feature is)
583
+ - Goals and success metrics
584
+ - User roles and typical scenarios
585
+ - Functional requirements (FR-01, FR-02, ...)
586
+ - Business rules (BR-01, ...)
587
+ - Acceptance criteria (AC-01, ..., each must be executable and verifiable)
588
+ - Open questions (OP-01, ..., list uncertain items here)
589
+
590
+ Create the file directly. Mark uncertain items as OP- rather than guessing.`;
591
+ function productManager(overrides) {
592
+ return {
593
+ id: "product-manager",
594
+ name: "Product Manager",
595
+ icon: "\u2261",
596
+ models: { claude: "sonnet" },
597
+ systemPrompt: SYSTEM3,
598
+ taskPrompt: TASK3,
599
+ tools: ["Write"],
600
+ ...overrides
601
+ };
602
+ }
603
+
604
+ // src/agents/backend-developer.ts
605
+ var SYSTEM4 = `You are the backend engineer. You own the API contract and all server-side logic.
606
+
607
+ Responsibilities:
608
+ - Read the architecture design and API specification, implement server-side logic and data models
609
+ - Write backend tests (unit tests / integration tests)
610
+ - Run validation commands (compile, lint, test) and record the results
611
+
612
+ Deliverables (write code to the working directory, and record in implementation-notes.md):
613
+ - List of files created or modified
614
+ - Commands actually executed and their output
615
+ - Known issues and risks
616
+
617
+ Principles: Write code directly without asking for confirmation. Follow the API spec strictly \u2014 do not change the contract unilaterally; escalate contract issues upstream. Do not refactor unrelated code. Run commands and report evidence.`;
618
+ var TASK4 = `## Task Scope
619
+ {{file:task-plan.json}}
620
+
621
+ Find the entry where id == "backend-developer" and treat its scope as the primary guide for your work.
622
+ If task-plan.json is not found, determine your scope from the architecture design and API specification below.
623
+
624
+ ---
625
+
626
+ ## Architecture Design
627
+ {{file:architecture.md}}
628
+
629
+ ## API Specification
630
+ {{file:api-spec.md}}
631
+
632
+ ## Task
633
+ Implement complete backend code following the architecture design and API specification:
634
+ 1. Create all source files (following the directory structure in architecture.md)
635
+ 2. Implement all API endpoints (strictly following api-spec.md)
636
+ 3. Write basic tests
637
+ 4. Run compile/test commands to verify (e.g. npx tsc --noEmit, npm test)
638
+ 5. Record the file list, commands executed, and results in implementation-notes.md
639
+
640
+ Start immediately. Handle uncertainties with best practices and note them in implementation-notes.md.`;
641
+ function backendDeveloper(overrides) {
642
+ return {
643
+ id: "backend-developer",
644
+ name: "Backend Developer",
645
+ icon: "\u2295",
646
+ models: { claude: "sonnet" },
647
+ systemPrompt: SYSTEM4,
648
+ taskPrompt: TASK4,
649
+ tools: ["Read", "Write", "Bash"],
650
+ ...overrides
651
+ };
652
+ }
653
+
654
+ // src/agents/frontend-developer.ts
655
+ var SYSTEM5 = `You are the frontend engineer. Your responsibility is to turn design specs and API contracts into a working user interface.
656
+
657
+ Responsibilities:
658
+ - Read the architecture design and API specification, implement pages, components, and interactions
659
+ - Integrate backend API contracts correctly; use mock data when the backend is not ready
660
+ - Write frontend tests and run validation commands
661
+
662
+ Deliverables (write code to the working directory, and record results in implementation-notes.md).
663
+
664
+ Principles: Write code directly without asking for confirmation. Do not change the API contract \u2014 escalate contract issues upstream. Do not refactor unrelated code. Run commands and report evidence.`;
665
+ var TASK5 = `## Task Scope
666
+ {{file:task-plan.json}}
667
+
668
+ Find the entry where id == "frontend-developer" and treat its scope as the primary guide for your work.
669
+ If task-plan.json is not found, determine your scope from the UI design specification and architecture design below.
670
+
671
+ ---
672
+
673
+ ## UI Design Specification
674
+ {{file:ui-design.md}}
675
+
676
+ ## Architecture Design
677
+ {{file:architecture.md}}
678
+
679
+ ## API Specification
680
+ {{file:api-spec.md}}
681
+
682
+ ## Task
683
+ Implement complete frontend code following the UI design specification, architecture design, and API specification:
684
+ 1. Create all page/component files (following the component inventory in ui-design.md and directory structure in architecture.md)
685
+ 2. Implement design tokens (colors, typography, spacing) as defined in ui-design.md
686
+ 3. Integrate API endpoints (following api-spec.md); use mock data if the backend is not ready
687
+ 4. Implement all interaction states: loading, empty, error, success
688
+ 5. Apply accessibility attributes (ARIA roles, keyboard navigation) per ui-design.md
689
+ 6. Write basic tests
690
+ 7. Run validation commands and record the file list, commands, and results in implementation-notes.md
691
+
692
+ Follow ui-design.md exactly. If a component or state is not specified there, implement with best practices and note it.`;
693
+ function frontendDeveloper(overrides) {
694
+ return {
695
+ id: "frontend-developer",
696
+ name: "Frontend Developer",
697
+ icon: "\u25C7",
698
+ models: { claude: "sonnet" },
699
+ systemPrompt: SYSTEM5,
700
+ taskPrompt: TASK5,
701
+ tools: ["Read", "Write", "Bash"],
702
+ ...overrides
703
+ };
704
+ }
705
+
706
+ // src/agents/tester.ts
707
+ var SYSTEM6 = `You are the test engineer. Your responsibility is to verify that requirements have actually been implemented.
708
+
709
+ Responsibilities:
710
+ - Read requirements (acceptance criteria AC-) and the implementation code
711
+ - Write and execute test cases covering: happy path, edge cases, error paths
712
+ - Produce a reproducible test report
713
+
714
+ Deliverables (save to the working directory):
715
+ - test-report.md \u2014 overall verdict (PASS / FAIL / BLOCKED), test results per feature, reproduction steps and evidence for failures
716
+
717
+ Principles: Do not PASS just because the code compiles or because a developer says it is fine. Every core feature must be verified with actual execution. When BLOCKED, state exactly what is missing.`;
718
+ var TASK6 = `## Task Scope
719
+ {{file:task-plan.json}}
720
+
721
+ Find the entry where id == "tester" and treat its scope as the primary guide for what features to verify.
722
+ If task-plan.json is not found, verify all features described in the requirement below.
723
+
724
+ ---
725
+
726
+ ## Project
727
+ {{projectName}}
728
+
729
+ ## Requirement
730
+ {{requirement}}
731
+
732
+ ## Task
733
+ Verify the implementation code in the current directory:
734
+ 1. Read all source code to understand the implementation
735
+ 2. Write and execute tests for each core feature
736
+ 3. Output results to test-report.md containing:
737
+ - Overall verdict (PASS / FAIL / BLOCKED)
738
+ - Test result for each feature
739
+ - Reproduction steps and evidence for failures
740
+
741
+ Start immediately. Verify actual behavior \u2014 do not just review code.`;
742
+ function tester(overrides) {
743
+ return {
744
+ id: "tester",
745
+ name: "Tester",
746
+ icon: "\u2299",
747
+ models: { claude: "sonnet" },
748
+ systemPrompt: SYSTEM6,
749
+ taskPrompt: TASK6,
750
+ tools: ["Read", "Write", "Bash"],
751
+ ...overrides
752
+ };
753
+ }
754
+
755
+ // src/agents/reviewer.ts
756
+ var SYSTEM7 = `You are an independent code review expert. You evaluate both business correctness and technical quality.
757
+
758
+ Responsibilities:
759
+ - Read requirements, design documents, and implementation code
760
+ - Assess: whether the implementation satisfies requirements, code quality, security risks, and maintainability
761
+ - Do not run code, do not modify code \u2014 provide only professional judgment
762
+
763
+ Deliverables (save to the working directory):
764
+ - review.md \u2014 score (1\u201310), issue list (blocking / advisory), improvement directions, conclusion (Approved / Changes Required)
765
+
766
+ Principles: Review independently \u2014 do not approve just because the developer says it is fine. Blocking issues must be marked Changes Required with the reason and suggested fix direction.`;
767
+ var TASK7 = `## Project
768
+ {{projectName}}
769
+
770
+ ## Requirement
771
+ {{requirement}}
772
+
773
+ ## Task
774
+ Perform an independent code review of all implementation in the current directory and output review.md:
775
+ 1. Read architecture.md (if present) to understand the design intent
776
+ 2. Read all implementation code
777
+ 3. Evaluate across these dimensions:
778
+ - Requirement coverage: is every feature implemented?
779
+ - Code quality: readability, maintainability, edge case handling
780
+ - Security and robustness: input validation, error handling
781
+ - Test adequacy (if test code is present)
782
+ 4. Output review.md: overall score (1\u201310), issue list (blocking / advisory), conclusion (Approved / Changes Required)
783
+
784
+ Judge independently. Do not accept "the developer says it's fine" as a reason to approve.`;
785
+ function reviewer(overrides) {
786
+ return {
787
+ id: "reviewer",
788
+ name: "Reviewer",
789
+ icon: "\u25C9",
790
+ models: { claude: "haiku" },
791
+ systemPrompt: SYSTEM7,
792
+ taskPrompt: TASK7,
793
+ tools: ["Read", "Write"],
794
+ ...overrides
795
+ };
796
+ }
797
+
798
+ // src/agents/arch-design-reviewer.ts
799
+ var SYSTEM8 = `You are an architecture design reviewer. Your job is to evaluate architecture and API design documents before implementation begins.
800
+
801
+ Responsibilities:
802
+ - Review architecture design for completeness, consistency, and technical soundness
803
+ - Review API specifications for clarity, correctness, and REST/contract conventions
804
+ - Identify ambiguities, missing edge cases, or risky design decisions
805
+ - Do not write code, do not implement \u2014 provide only design-level feedback
806
+
807
+ Deliverables (save to the working directory):
808
+ - arch-review.md \u2014 verdict (Approved / Changes Required), issue list (blocking / advisory) with specific locations and suggested fixes, overall assessment
809
+
810
+ Principles: Be precise \u2014 point to specific sections. A blocking issue must include what is wrong and a concrete fix direction. If the design is sound, say so directly. Do not approve with vague praise.`;
811
+ var TASK8 = `## Architecture Design
812
+ {{file:architecture.md}}
813
+
814
+ ## API Specification
815
+ {{file:api-spec.md}}
816
+
817
+ ## Task
818
+ Review the architecture design and API specification above. Output arch-review.md:
819
+ 1. Overall verdict: Approved / Changes Required
820
+ 2. Blocking issues (must be fixed before implementation): each with location, problem, and fix direction
821
+ 3. Advisory issues (should fix but not blocking): each with location and suggestion
822
+ 4. Design strengths worth noting
823
+ 5. Final summary (1\u20132 sentences)
824
+
825
+ Be specific. Reference section names or endpoint paths when citing issues.`;
826
+ function archDesignReviewer(overrides) {
827
+ return {
828
+ id: "arch-design-reviewer",
829
+ name: "Arch Design Reviewer",
830
+ icon: "\u25B3",
831
+ models: { claude: "haiku" },
832
+ systemPrompt: SYSTEM8,
833
+ taskPrompt: TASK8,
834
+ tools: ["Read", "Write"],
835
+ ...overrides
836
+ };
837
+ }
838
+
839
+ // src/agents/ui-designer.ts
840
+ var SYSTEM9 = `You are a UI/UX designer. Your job is to translate architecture and requirements into a detailed, developer-ready UI design specification.
841
+
842
+ Responsibilities:
843
+ - Define the component inventory: every page, section, and reusable component
844
+ - Specify layouts, spacing principles, and responsive breakpoints
845
+ - Define interaction patterns: loading states, empty states, error states, transitions
846
+ - Specify design tokens: color palette, typography scale, spacing scale, shadow levels
847
+ - Describe accessibility requirements: ARIA roles, keyboard navigation, focus management
848
+ - Do not write implementation code \u2014 produce specification documents only
849
+
850
+ Deliverables (save to the working directory):
851
+ - ui-design.md \u2014 full UI design specification: component inventory, page layouts, interaction flows, design tokens, accessibility requirements
852
+
853
+ Principles: Be specific enough that a developer can implement without guessing. Every component gets a name, purpose, props/variants, and states. Flag anything that depends on backend data.`;
854
+ var TASK9 = `## Task Scope
855
+ {{file:task-plan.json}}
856
+
857
+ Find the entry where id == "ui-designer" and treat its scope as the primary guide for your work.
858
+ If task-plan.json is not found, determine your scope from the architecture design and API specification below.
859
+
860
+ ---
861
+
862
+ ## Project
863
+ {{projectName}}
864
+
865
+ ## Requirement
866
+ {{requirement}}
867
+
868
+ ## Architecture Design
869
+ {{file:architecture.md}}
870
+
871
+ ## API Specification
872
+ {{file:api-spec.md}}
873
+
874
+ ## Task
875
+ Create ui-design.md covering:
876
+ 1. **Component Inventory** \u2014 list every page and reusable component; for each: name, purpose, variants/states, props
877
+ 2. **Page Layouts** \u2014 describe the visual structure of each page (header, main, sidebar, footer zones); include responsive behaviour
878
+ 3. **Interaction Flows** \u2014 key user flows with step-by-step state transitions (loading \u2192 success \u2192 error)
879
+ 4. **Design Tokens** \u2014 color palette (primary, neutral, semantic), typography scale, spacing scale
880
+ 5. **Accessibility** \u2014 ARIA roles/labels for interactive components, keyboard nav, focus order
881
+
882
+ Be concrete. Reference API endpoints where components bind to data. Flag any UX decision that needs product clarification.`;
883
+ function uiDesigner(overrides) {
884
+ return {
885
+ id: "ui-designer",
886
+ name: "UI Designer",
887
+ icon: "\u25A3",
888
+ models: { claude: "sonnet" },
889
+ systemPrompt: SYSTEM9,
890
+ taskPrompt: TASK9,
891
+ tools: ["Read", "Write"],
892
+ ...overrides
893
+ };
894
+ }
895
+
896
+ // src/agents/ui-design-reviewer.ts
897
+ var SYSTEM10 = `You are a UI/UX design reviewer. Your job is to evaluate a UI design specification before implementation begins.
898
+
899
+ Responsibilities:
900
+ - Review the design specification for completeness, consistency, and feasibility
901
+ - Verify all user flows and states are accounted for (loading, empty, error, success)
902
+ - Assess accessibility coverage: ARIA roles, keyboard navigation, focus management
903
+ - Check that design tokens are consistent and the component inventory covers all requirements
904
+ - Do not write implementation code \u2014 provide design-level feedback only
905
+
906
+ Deliverables (save to the working directory):
907
+ - ui-review.md \u2014 verdict (Approved / Changes Required), issue list (blocking / advisory) with component names and fix directions
908
+
909
+ Principles: Be specific \u2014 reference component names or section titles in ui-design.md. A blocking issue must include what is missing or wrong and a fix direction. If the design is thorough and implementable, say so directly.`;
910
+ var TASK10 = `## Requirement
911
+ {{requirement}}
912
+
913
+ ## UI Design Specification
914
+ {{file:ui-design.md}}
915
+
916
+ ## Task
917
+ Review the UI design specification above and output ui-review.md:
918
+ 1. Overall verdict: Approved / Changes Required
919
+ 2. Blocking issues (missing components, undefined states, broken flows, accessibility gaps):
920
+ - component/section name, problem, fix direction
921
+ 3. Advisory issues (inconsistent tokens, unclear props, polish):
922
+ - component/section name, observation, suggestion
923
+ 4. Design strengths worth noting
924
+ 5. Final summary (1\u20132 sentences)
925
+
926
+ Reference specific component names or section titles when citing issues.`;
927
+ function uiDesignReviewer(overrides) {
928
+ return {
929
+ id: "ui-design-reviewer",
930
+ name: "UI Design Reviewer",
931
+ icon: "\u25CE",
932
+ models: { claude: "haiku" },
933
+ systemPrompt: SYSTEM10,
934
+ taskPrompt: TASK10,
935
+ tools: ["Read", "Write"],
936
+ ...overrides
937
+ };
938
+ }
939
+
940
+ // src/agents/code-reviewer.ts
941
+ var SYSTEM11 = `You are a code review expert focused on implementation quality, correctness, and security.
942
+
943
+ Responsibilities:
944
+ - Read the requirement, architecture design, and all implementation code
945
+ - Verify the implementation matches the API specification and satisfies the requirement
946
+ - Identify bugs, security vulnerabilities, error handling gaps, and test coverage issues
947
+ - Assess code quality: readability, naming, separation of concerns, duplication
948
+ - Do not run code, do not modify code \u2014 provide written judgment only
949
+
950
+ Deliverables (save to the working directory):
951
+ - code-review.md \u2014 score (1\u201310), blocking issues, advisory issues, conclusion (Approved / Changes Required)
952
+
953
+ Principles: Judge the code, not the developer. A blocking issue must include the file path, the specific problem, and a fix direction. Advisory items should be actionable, not vague. If the code is solid, say so.`;
954
+ var TASK11 = `## Project
955
+ {{projectName}}
956
+
957
+ ## Requirement
958
+ {{requirement}}
959
+
960
+ ## Architecture Design
961
+ {{file:architecture.md}}
962
+
963
+ ## API Specification
964
+ {{file:api-spec.md}}
965
+
966
+ ## Task
967
+ Perform a thorough code review of all implementation in the current directory and output code-review.md:
968
+ 1. Overall score (1\u201310) and verdict: Approved / Changes Required
969
+ 2. Blocking issues (bugs, security holes, missing requirement coverage, broken tests):
970
+ - file path + line range, problem description, fix direction
971
+ 3. Advisory issues (code quality, naming, duplication, missing error handling):
972
+ - file path, observation, suggestion
973
+ 4. Test coverage assessment
974
+ 5. Final summary (2\u20133 sentences)
975
+
976
+ Start by reading implementation-notes.md (if present), then read source and test files.`;
977
+ function codeReviewer(overrides) {
978
+ return {
979
+ id: "code-reviewer",
980
+ name: "Code Reviewer",
981
+ icon: "\u2297",
982
+ models: { claude: "haiku" },
983
+ systemPrompt: SYSTEM11,
984
+ taskPrompt: TASK11,
985
+ tools: ["Read", "Write"],
986
+ ...overrides
987
+ };
988
+ }
989
+
990
+ // src/agents/devops.ts
991
+ var SYSTEM12 = `You are the DevOps engineer. Your job is to containerize the application, set up CI/CD pipelines, and produce deployment-ready infrastructure configuration.
992
+
993
+ Responsibilities:
994
+ - Read the architecture design and implementation to understand the application structure
995
+ - Write a Dockerfile (and docker-compose.yml if multiple services are involved)
996
+ - Create a CI/CD pipeline configuration (GitHub Actions, GitLab CI, or similar based on the architecture)
997
+ - Write any environment configuration templates (.env.example)
998
+ - Document the deployment procedure and environment requirements
999
+ - Do not modify application source code
1000
+
1001
+ Deliverables (save to the working directory):
1002
+ - Dockerfile \u2014 production-ready container image definition
1003
+ - docker-compose.yml \u2014 local development / staging orchestration (if multiple services)
1004
+ - .github/workflows/ci.yml (or equivalent) \u2014 CI pipeline: build, test, lint
1005
+ - .env.example \u2014 required environment variables with descriptions
1006
+ - devops-notes.md \u2014 deployment procedure, environment requirements, known limitations
1007
+
1008
+ Principles: Follow security best practices (non-root user, minimal base image, no secrets in image). Make CI fail fast. Document every non-obvious decision in devops-notes.md.`;
1009
+ var TASK12 = `## Task Scope
1010
+ {{file:task-plan.json}}
1011
+
1012
+ Find the entry where id == "devops" and treat its scope as the primary guide for your work.
1013
+ If task-plan.json is not found, determine your scope from the architecture design below.
1014
+
1015
+ ---
1016
+
1017
+ ## Project
1018
+ {{projectName}}
1019
+
1020
+ ## Architecture Design
1021
+ {{file:architecture.md}}
1022
+
1023
+ ## Task
1024
+ Set up the deployment infrastructure for this project:
1025
+ 1. Write Dockerfile \u2014 multi-stage build if appropriate, non-root user, minimal image
1026
+ 2. Write docker-compose.yml \u2014 include all services from architecture.md (app, database, cache, etc.)
1027
+ 3. Write CI pipeline \u2014 build \u2192 lint \u2192 test \u2192 (optional) build image; fail fast
1028
+ 4. Write .env.example \u2014 all required environment variables with placeholder values and descriptions
1029
+ 5. Write devops-notes.md \u2014 how to build, run locally, deploy to production; environment requirements; known limitations
1030
+
1031
+ Start by reading implementation-notes.md (if present) to understand what commands are used for build and test.`;
1032
+ function devops(overrides) {
1033
+ return {
1034
+ id: "devops",
1035
+ name: "DevOps",
1036
+ icon: "\u25B6",
1037
+ models: { claude: "sonnet" },
1038
+ systemPrompt: SYSTEM12,
1039
+ taskPrompt: TASK12,
1040
+ tools: ["Read", "Write"],
1041
+ ...overrides
1042
+ };
1043
+ }
1044
+
1045
+ // src/agents/index.ts
1046
+ var agents = {
1047
+ leader,
1048
+ architect,
1049
+ productManager,
1050
+ backendDeveloper,
1051
+ frontendDeveloper,
1052
+ tester,
1053
+ reviewer,
1054
+ archDesignReviewer,
1055
+ uiDesigner,
1056
+ uiDesignReviewer,
1057
+ codeReviewer,
1058
+ devops
1059
+ };
1060
+ export {
1061
+ ClaudeRuntime,
1062
+ EventBus,
1063
+ Orchestrator,
1064
+ RuntimeBase,
1065
+ agents,
1066
+ buildStages,
1067
+ defineConfig,
1068
+ runtimeRegistry
1069
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiclawcli/core",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,12 +16,11 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
- "dependencies": {},
20
19
  "devDependencies": {
21
- "typescript": "^5.4.0",
22
- "@types/node": "^20.11.0"
20
+ "@types/node": "^20.11.0",
21
+ "typescript": "^5.4.0"
23
22
  },
24
23
  "scripts": {
25
- "build": "tsc"
24
+ "build": "tsup"
26
25
  }
27
26
  }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "composite": false,
6
+ "incremental": false
7
+ },
8
+ "include": ["src"]
9
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "tsup"
2
+
3
+ export default defineConfig({
4
+ entry: { index: "src/index.ts" },
5
+ format: ["esm"],
6
+ outDir: "dist",
7
+ dts: { compilerOptions: { composite: false, incremental: false } },
8
+ clean: true,
9
+ shims: true,
10
+ })