@jackchen_me/open-multi-agent 0.2.0 → 1.0.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.
Files changed (117) hide show
  1. package/README.md +87 -20
  2. package/dist/agent/agent.d.ts +15 -1
  3. package/dist/agent/agent.d.ts.map +1 -1
  4. package/dist/agent/agent.js +144 -10
  5. package/dist/agent/agent.js.map +1 -1
  6. package/dist/agent/loop-detector.d.ts +39 -0
  7. package/dist/agent/loop-detector.d.ts.map +1 -0
  8. package/dist/agent/loop-detector.js +122 -0
  9. package/dist/agent/loop-detector.js.map +1 -0
  10. package/dist/agent/pool.d.ts +2 -1
  11. package/dist/agent/pool.d.ts.map +1 -1
  12. package/dist/agent/pool.js +4 -2
  13. package/dist/agent/pool.js.map +1 -1
  14. package/dist/agent/runner.d.ts +23 -1
  15. package/dist/agent/runner.d.ts.map +1 -1
  16. package/dist/agent/runner.js +113 -12
  17. package/dist/agent/runner.js.map +1 -1
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/llm/adapter.d.ts +4 -1
  23. package/dist/llm/adapter.d.ts.map +1 -1
  24. package/dist/llm/adapter.js +11 -0
  25. package/dist/llm/adapter.js.map +1 -1
  26. package/dist/llm/copilot.d.ts.map +1 -1
  27. package/dist/llm/copilot.js +2 -1
  28. package/dist/llm/copilot.js.map +1 -1
  29. package/dist/llm/gemini.d.ts +65 -0
  30. package/dist/llm/gemini.d.ts.map +1 -0
  31. package/dist/llm/gemini.js +317 -0
  32. package/dist/llm/gemini.js.map +1 -0
  33. package/dist/llm/grok.d.ts +21 -0
  34. package/dist/llm/grok.d.ts.map +1 -0
  35. package/dist/llm/grok.js +24 -0
  36. package/dist/llm/grok.js.map +1 -0
  37. package/dist/llm/openai-common.d.ts +8 -1
  38. package/dist/llm/openai-common.d.ts.map +1 -1
  39. package/dist/llm/openai-common.js +35 -2
  40. package/dist/llm/openai-common.js.map +1 -1
  41. package/dist/llm/openai.d.ts +1 -1
  42. package/dist/llm/openai.d.ts.map +1 -1
  43. package/dist/llm/openai.js +20 -2
  44. package/dist/llm/openai.js.map +1 -1
  45. package/dist/orchestrator/orchestrator.d.ts.map +1 -1
  46. package/dist/orchestrator/orchestrator.js +89 -9
  47. package/dist/orchestrator/orchestrator.js.map +1 -1
  48. package/dist/task/queue.d.ts +31 -2
  49. package/dist/task/queue.d.ts.map +1 -1
  50. package/dist/task/queue.js +69 -2
  51. package/dist/task/queue.js.map +1 -1
  52. package/dist/tool/text-tool-extractor.d.ts +32 -0
  53. package/dist/tool/text-tool-extractor.d.ts.map +1 -0
  54. package/dist/tool/text-tool-extractor.js +187 -0
  55. package/dist/tool/text-tool-extractor.js.map +1 -0
  56. package/dist/types.d.ts +139 -7
  57. package/dist/types.d.ts.map +1 -1
  58. package/dist/utils/trace.d.ts +12 -0
  59. package/dist/utils/trace.d.ts.map +1 -0
  60. package/dist/utils/trace.js +30 -0
  61. package/dist/utils/trace.js.map +1 -0
  62. package/package.json +18 -2
  63. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  64. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
  65. package/.github/pull_request_template.md +0 -14
  66. package/.github/workflows/ci.yml +0 -23
  67. package/CLAUDE.md +0 -72
  68. package/CODE_OF_CONDUCT.md +0 -48
  69. package/CONTRIBUTING.md +0 -72
  70. package/DECISIONS.md +0 -43
  71. package/README_zh.md +0 -217
  72. package/SECURITY.md +0 -17
  73. package/examples/01-single-agent.ts +0 -131
  74. package/examples/02-team-collaboration.ts +0 -167
  75. package/examples/03-task-pipeline.ts +0 -201
  76. package/examples/04-multi-model-team.ts +0 -261
  77. package/examples/05-copilot-test.ts +0 -49
  78. package/examples/06-local-model.ts +0 -199
  79. package/examples/07-fan-out-aggregate.ts +0 -209
  80. package/examples/08-gemma4-local.ts +0 -203
  81. package/examples/09-gemma4-auto-orchestration.ts +0 -162
  82. package/src/agent/agent.ts +0 -473
  83. package/src/agent/pool.ts +0 -278
  84. package/src/agent/runner.ts +0 -413
  85. package/src/agent/structured-output.ts +0 -126
  86. package/src/index.ts +0 -167
  87. package/src/llm/adapter.ts +0 -87
  88. package/src/llm/anthropic.ts +0 -389
  89. package/src/llm/copilot.ts +0 -551
  90. package/src/llm/openai-common.ts +0 -255
  91. package/src/llm/openai.ts +0 -272
  92. package/src/memory/shared.ts +0 -181
  93. package/src/memory/store.ts +0 -124
  94. package/src/orchestrator/orchestrator.ts +0 -977
  95. package/src/orchestrator/scheduler.ts +0 -352
  96. package/src/task/queue.ts +0 -394
  97. package/src/task/task.ts +0 -239
  98. package/src/team/messaging.ts +0 -232
  99. package/src/team/team.ts +0 -334
  100. package/src/tool/built-in/bash.ts +0 -187
  101. package/src/tool/built-in/file-edit.ts +0 -154
  102. package/src/tool/built-in/file-read.ts +0 -105
  103. package/src/tool/built-in/file-write.ts +0 -81
  104. package/src/tool/built-in/grep.ts +0 -362
  105. package/src/tool/built-in/index.ts +0 -50
  106. package/src/tool/executor.ts +0 -178
  107. package/src/tool/framework.ts +0 -557
  108. package/src/types.ts +0 -391
  109. package/src/utils/semaphore.ts +0 -89
  110. package/tests/semaphore.test.ts +0 -57
  111. package/tests/shared-memory.test.ts +0 -122
  112. package/tests/structured-output.test.ts +0 -331
  113. package/tests/task-queue.test.ts +0 -244
  114. package/tests/task-retry.test.ts +0 -368
  115. package/tests/task-utils.test.ts +0 -155
  116. package/tests/tool-executor.test.ts +0 -193
  117. package/tsconfig.json +0 -25
@@ -1,131 +0,0 @@
1
- /**
2
- * Example 01 — Single Agent
3
- *
4
- * The simplest possible usage: one agent with bash and file tools, running
5
- * a coding task. Then shows streaming output using the Agent class directly.
6
- *
7
- * Run:
8
- * npx tsx examples/01-single-agent.ts
9
- *
10
- * Prerequisites:
11
- * ANTHROPIC_API_KEY env var must be set.
12
- */
13
-
14
- import { OpenMultiAgent, Agent, ToolRegistry, ToolExecutor, registerBuiltInTools } from '../src/index.js'
15
- import type { OrchestratorEvent } from '../src/types.js'
16
-
17
- // ---------------------------------------------------------------------------
18
- // Part 1: Single agent via OpenMultiAgent (simplest path)
19
- // ---------------------------------------------------------------------------
20
-
21
- const orchestrator = new OpenMultiAgent({
22
- defaultModel: 'claude-sonnet-4-6',
23
- onProgress: (event: OrchestratorEvent) => {
24
- if (event.type === 'agent_start') {
25
- console.log(`[start] agent=${event.agent}`)
26
- } else if (event.type === 'agent_complete') {
27
- console.log(`[complete] agent=${event.agent}`)
28
- }
29
- },
30
- })
31
-
32
- console.log('Part 1: runAgent() — single one-shot task\n')
33
-
34
- const result = await orchestrator.runAgent(
35
- {
36
- name: 'coder',
37
- model: 'claude-sonnet-4-6',
38
- systemPrompt: `You are a focused TypeScript developer.
39
- When asked to implement something, write clean, minimal code with no extra commentary.
40
- Use the bash tool to run commands and the file tools to read/write files.`,
41
- tools: ['bash', 'file_read', 'file_write'],
42
- maxTurns: 8,
43
- },
44
- `Create a small TypeScript utility function in /tmp/greet.ts that:
45
- 1. Exports a function named greet(name: string): string
46
- 2. Returns "Hello, <name>!"
47
- 3. Adds a brief usage comment at the top of the file.
48
- Then add a default call greet("World") at the bottom and run the file with: npx tsx /tmp/greet.ts`,
49
- )
50
-
51
- if (result.success) {
52
- console.log('\nAgent output:')
53
- console.log('─'.repeat(60))
54
- console.log(result.output)
55
- console.log('─'.repeat(60))
56
- } else {
57
- console.error('Agent failed:', result.output)
58
- process.exit(1)
59
- }
60
-
61
- console.log('\nToken usage:')
62
- console.log(` input: ${result.tokenUsage.input_tokens}`)
63
- console.log(` output: ${result.tokenUsage.output_tokens}`)
64
- console.log(` tool calls made: ${result.toolCalls.length}`)
65
-
66
- // ---------------------------------------------------------------------------
67
- // Part 2: Streaming via Agent directly
68
- //
69
- // OpenMultiAgent.runAgent() is a convenient wrapper. When you need streaming, use
70
- // the Agent class directly with an injected ToolRegistry + ToolExecutor.
71
- // ---------------------------------------------------------------------------
72
-
73
- console.log('\n\nPart 2: Agent.stream() — incremental text output\n')
74
-
75
- // Build a registry with all built-in tools registered
76
- const registry = new ToolRegistry()
77
- registerBuiltInTools(registry)
78
- const executor = new ToolExecutor(registry)
79
-
80
- const streamingAgent = new Agent(
81
- {
82
- name: 'explainer',
83
- model: 'claude-sonnet-4-6',
84
- systemPrompt: 'You are a concise technical writer. Keep explanations brief.',
85
- maxTurns: 3,
86
- },
87
- registry,
88
- executor,
89
- )
90
-
91
- process.stdout.write('Streaming: ')
92
-
93
- for await (const event of streamingAgent.stream(
94
- 'In two sentences, explain what a TypeScript generic constraint is.',
95
- )) {
96
- if (event.type === 'text' && typeof event.data === 'string') {
97
- process.stdout.write(event.data)
98
- } else if (event.type === 'done') {
99
- process.stdout.write('\n')
100
- } else if (event.type === 'error') {
101
- console.error('\nStream error:', event.data)
102
- }
103
- }
104
-
105
- // ---------------------------------------------------------------------------
106
- // Part 3: Multi-turn conversation via Agent.prompt()
107
- // ---------------------------------------------------------------------------
108
-
109
- console.log('\nPart 3: Agent.prompt() — multi-turn conversation\n')
110
-
111
- const conversationAgent = new Agent(
112
- {
113
- name: 'tutor',
114
- model: 'claude-sonnet-4-6',
115
- systemPrompt: 'You are a TypeScript tutor. Give short, direct answers.',
116
- maxTurns: 2,
117
- },
118
- new ToolRegistry(), // no tools needed for this conversation
119
- new ToolExecutor(new ToolRegistry()),
120
- )
121
-
122
- const turn1 = await conversationAgent.prompt('What is a type guard in TypeScript?')
123
- console.log('Turn 1:', turn1.output.slice(0, 200))
124
-
125
- const turn2 = await conversationAgent.prompt('Give me one concrete code example of what you just described.')
126
- console.log('\nTurn 2:', turn2.output.slice(0, 300))
127
-
128
- // History is retained between prompt() calls
129
- console.log(`\nConversation history length: ${conversationAgent.getHistory().length} messages`)
130
-
131
- console.log('\nDone.')
@@ -1,167 +0,0 @@
1
- /**
2
- * Example 02 — Multi-Agent Team Collaboration
3
- *
4
- * Three specialised agents (architect, developer, reviewer) collaborate on a
5
- * shared goal. The OpenMultiAgent orchestrator breaks the goal into tasks, assigns
6
- * them to the right agents, and collects the results.
7
- *
8
- * Run:
9
- * npx tsx examples/02-team-collaboration.ts
10
- *
11
- * Prerequisites:
12
- * ANTHROPIC_API_KEY env var must be set.
13
- */
14
-
15
- import { OpenMultiAgent } from '../src/index.js'
16
- import type { AgentConfig, OrchestratorEvent } from '../src/types.js'
17
-
18
- // ---------------------------------------------------------------------------
19
- // Agent definitions
20
- // ---------------------------------------------------------------------------
21
-
22
- const architect: AgentConfig = {
23
- name: 'architect',
24
- model: 'claude-sonnet-4-6',
25
- provider: 'anthropic',
26
- systemPrompt: `You are a software architect with deep experience in Node.js and REST API design.
27
- Your job is to design clear, production-quality API contracts and file/directory structures.
28
- Output concise plans in markdown — no unnecessary prose.`,
29
- tools: ['bash', 'file_write'],
30
- maxTurns: 5,
31
- temperature: 0.2,
32
- }
33
-
34
- const developer: AgentConfig = {
35
- name: 'developer',
36
- model: 'claude-sonnet-4-6',
37
- provider: 'anthropic',
38
- systemPrompt: `You are a TypeScript/Node.js developer. You implement what the architect specifies.
39
- Write clean, runnable code with proper error handling. Use the tools to write files and run tests.`,
40
- tools: ['bash', 'file_read', 'file_write', 'file_edit'],
41
- maxTurns: 12,
42
- temperature: 0.1,
43
- }
44
-
45
- const reviewer: AgentConfig = {
46
- name: 'reviewer',
47
- model: 'claude-sonnet-4-6',
48
- provider: 'anthropic',
49
- systemPrompt: `You are a senior code reviewer. Review code for correctness, security, and clarity.
50
- Provide a structured review with: LGTM items, suggestions, and any blocking issues.
51
- Read files using the tools before reviewing.`,
52
- tools: ['bash', 'file_read', 'grep'],
53
- maxTurns: 5,
54
- temperature: 0.3,
55
- }
56
-
57
- // ---------------------------------------------------------------------------
58
- // Progress tracking
59
- // ---------------------------------------------------------------------------
60
-
61
- const startTimes = new Map<string, number>()
62
-
63
- function handleProgress(event: OrchestratorEvent): void {
64
- const ts = new Date().toISOString().slice(11, 23) // HH:MM:SS.mmm
65
-
66
- switch (event.type) {
67
- case 'agent_start':
68
- startTimes.set(event.agent ?? '', Date.now())
69
- console.log(`[${ts}] AGENT START → ${event.agent}`)
70
- break
71
-
72
- case 'agent_complete': {
73
- const elapsed = Date.now() - (startTimes.get(event.agent ?? '') ?? Date.now())
74
- console.log(`[${ts}] AGENT DONE ← ${event.agent} (${elapsed}ms)`)
75
- break
76
- }
77
-
78
- case 'task_start':
79
- console.log(`[${ts}] TASK START ↓ ${event.task}`)
80
- break
81
-
82
- case 'task_complete':
83
- console.log(`[${ts}] TASK DONE ↑ ${event.task}`)
84
- break
85
-
86
- case 'message':
87
- console.log(`[${ts}] MESSAGE • ${event.agent} → (team)`)
88
- break
89
-
90
- case 'error':
91
- console.error(`[${ts}] ERROR ✗ agent=${event.agent} task=${event.task}`)
92
- if (event.data instanceof Error) {
93
- console.error(` ${event.data.message}`)
94
- }
95
- break
96
- }
97
- }
98
-
99
- // ---------------------------------------------------------------------------
100
- // Orchestrate
101
- // ---------------------------------------------------------------------------
102
-
103
- const orchestrator = new OpenMultiAgent({
104
- defaultModel: 'claude-sonnet-4-6',
105
- maxConcurrency: 1, // run agents sequentially so output is readable
106
- onProgress: handleProgress,
107
- })
108
-
109
- const team = orchestrator.createTeam('api-team', {
110
- name: 'api-team',
111
- agents: [architect, developer, reviewer],
112
- sharedMemory: true,
113
- maxConcurrency: 1,
114
- })
115
-
116
- console.log(`Team "${team.name}" created with agents: ${team.getAgents().map(a => a.name).join(', ')}`)
117
- console.log('\nStarting team run...\n')
118
- console.log('='.repeat(60))
119
-
120
- const goal = `Create a minimal Express.js REST API in /tmp/express-api/ with:
121
- - GET /health → { status: "ok" }
122
- - GET /users → returns a hardcoded array of 2 user objects
123
- - POST /users → accepts { name, email } body, logs it, returns 201
124
- - Proper error handling middleware
125
- - The server should listen on port 3001
126
- - Include a package.json with the required dependencies`
127
-
128
- const result = await orchestrator.runTeam(team, goal)
129
-
130
- console.log('\n' + '='.repeat(60))
131
-
132
- // ---------------------------------------------------------------------------
133
- // Results
134
- // ---------------------------------------------------------------------------
135
-
136
- console.log('\nTeam run complete.')
137
- console.log(`Success: ${result.success}`)
138
- console.log(`Total tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`)
139
-
140
- console.log('\nPer-agent results:')
141
- for (const [agentName, agentResult] of result.agentResults) {
142
- const status = agentResult.success ? 'OK' : 'FAILED'
143
- const tools = agentResult.toolCalls.length
144
- console.log(` ${agentName.padEnd(12)} [${status}] tool_calls=${tools}`)
145
- if (!agentResult.success) {
146
- console.log(` Error: ${agentResult.output.slice(0, 120)}`)
147
- }
148
- }
149
-
150
- // Print the developer's final output (the actual code) as a sample
151
- const developerResult = result.agentResults.get('developer')
152
- if (developerResult?.success) {
153
- console.log('\nDeveloper output (last 600 chars):')
154
- console.log('─'.repeat(60))
155
- const out = developerResult.output
156
- console.log(out.length > 600 ? '...' + out.slice(-600) : out)
157
- console.log('─'.repeat(60))
158
- }
159
-
160
- // Print the reviewer's findings
161
- const reviewerResult = result.agentResults.get('reviewer')
162
- if (reviewerResult?.success) {
163
- console.log('\nReviewer output:')
164
- console.log('─'.repeat(60))
165
- console.log(reviewerResult.output)
166
- console.log('─'.repeat(60))
167
- }
@@ -1,201 +0,0 @@
1
- /**
2
- * Example 03 — Explicit Task Pipeline with Dependencies
3
- *
4
- * Demonstrates how to define tasks with explicit dependency chains
5
- * (design → implement → test → review) using runTasks(). The TaskQueue
6
- * automatically blocks downstream tasks until their dependencies complete.
7
- *
8
- * Run:
9
- * npx tsx examples/03-task-pipeline.ts
10
- *
11
- * Prerequisites:
12
- * ANTHROPIC_API_KEY env var must be set.
13
- */
14
-
15
- import { OpenMultiAgent } from '../src/index.js'
16
- import type { AgentConfig, OrchestratorEvent, Task } from '../src/types.js'
17
-
18
- // ---------------------------------------------------------------------------
19
- // Agents
20
- // ---------------------------------------------------------------------------
21
-
22
- const designer: AgentConfig = {
23
- name: 'designer',
24
- model: 'claude-sonnet-4-6',
25
- systemPrompt: `You are a software designer. Your output is always a concise technical spec
26
- in markdown. Focus on interfaces, data shapes, and file structure. Be brief.`,
27
- tools: ['file_write'],
28
- maxTurns: 4,
29
- }
30
-
31
- const implementer: AgentConfig = {
32
- name: 'implementer',
33
- model: 'claude-sonnet-4-6',
34
- systemPrompt: `You are a TypeScript developer. Read the design spec written by the designer,
35
- then implement it. Write all files to /tmp/pipeline-output/. Use the tools.`,
36
- tools: ['bash', 'file_read', 'file_write'],
37
- maxTurns: 10,
38
- }
39
-
40
- const tester: AgentConfig = {
41
- name: 'tester',
42
- model: 'claude-sonnet-4-6',
43
- systemPrompt: `You are a QA engineer. Read the implemented files and run them to verify correctness.
44
- Report: what passed, what failed, and any bugs found.`,
45
- tools: ['bash', 'file_read', 'grep'],
46
- maxTurns: 6,
47
- }
48
-
49
- const reviewer: AgentConfig = {
50
- name: 'reviewer',
51
- model: 'claude-sonnet-4-6',
52
- systemPrompt: `You are a code reviewer. Read all files and produce a brief structured review.
53
- Sections: Summary, Strengths, Issues (if any), Verdict (SHIP / NEEDS WORK).`,
54
- tools: ['file_read', 'grep'],
55
- maxTurns: 4,
56
- }
57
-
58
- // ---------------------------------------------------------------------------
59
- // Progress handler — shows dependency blocking/unblocking
60
- // ---------------------------------------------------------------------------
61
-
62
- const taskTimes = new Map<string, number>()
63
-
64
- function handleProgress(event: OrchestratorEvent): void {
65
- const ts = new Date().toISOString().slice(11, 23)
66
-
67
- switch (event.type) {
68
- case 'task_start': {
69
- taskTimes.set(event.task ?? '', Date.now())
70
- const task = event.data as Task | undefined
71
- console.log(`[${ts}] TASK READY "${task?.title ?? event.task}" (assignee: ${task?.assignee ?? 'any'})`)
72
- break
73
- }
74
- case 'task_complete': {
75
- const elapsed = Date.now() - (taskTimes.get(event.task ?? '') ?? Date.now())
76
- const task = event.data as Task | undefined
77
- console.log(`[${ts}] TASK DONE "${task?.title ?? event.task}" in ${elapsed}ms`)
78
- break
79
- }
80
- case 'agent_start':
81
- console.log(`[${ts}] AGENT START ${event.agent}`)
82
- break
83
- case 'agent_complete':
84
- console.log(`[${ts}] AGENT DONE ${event.agent}`)
85
- break
86
- case 'error': {
87
- const task = event.data as Task | undefined
88
- console.error(`[${ts}] ERROR ${event.agent ?? ''} task="${task?.title ?? event.task}"`)
89
- break
90
- }
91
- }
92
- }
93
-
94
- // ---------------------------------------------------------------------------
95
- // Build the pipeline
96
- // ---------------------------------------------------------------------------
97
-
98
- const orchestrator = new OpenMultiAgent({
99
- defaultModel: 'claude-sonnet-4-6',
100
- maxConcurrency: 2, // allow test + review to potentially run in parallel later
101
- onProgress: handleProgress,
102
- })
103
-
104
- const team = orchestrator.createTeam('pipeline-team', {
105
- name: 'pipeline-team',
106
- agents: [designer, implementer, tester, reviewer],
107
- sharedMemory: true,
108
- })
109
-
110
- // Task IDs — use stable strings so dependsOn can reference them
111
- // (IDs will be generated by the framework; we capture the returned Task objects)
112
- const SPEC_FILE = '/tmp/pipeline-output/design-spec.md'
113
-
114
- const tasks: Array<{
115
- title: string
116
- description: string
117
- assignee?: string
118
- dependsOn?: string[]
119
- }> = [
120
- {
121
- title: 'Design: URL shortener data model',
122
- description: `Design a minimal in-memory URL shortener service.
123
- Write a markdown spec to ${SPEC_FILE} covering:
124
- - TypeScript interfaces for Url and ShortenRequest
125
- - The shortening algorithm (hash approach is fine)
126
- - API contract: POST /shorten, GET /:code
127
- Keep the spec under 30 lines.`,
128
- assignee: 'designer',
129
- // no dependencies — this is the root task
130
- },
131
- {
132
- title: 'Implement: URL shortener',
133
- description: `Read the design spec at ${SPEC_FILE}.
134
- Implement the URL shortener in /tmp/pipeline-output/src/:
135
- - shortener.ts: core logic (shorten, resolve functions)
136
- - server.ts: tiny HTTP server using Node's built-in http module (no Express)
137
- - POST /shorten body: { url: string } → { code: string, short: string }
138
- - GET /:code → redirect (301) or 404
139
- - index.ts: entry point that starts the server on port 3002
140
- No external dependencies beyond Node built-ins.`,
141
- assignee: 'implementer',
142
- dependsOn: ['Design: URL shortener data model'],
143
- },
144
- {
145
- title: 'Test: URL shortener',
146
- description: `Run the URL shortener implementation:
147
- 1. Start the server: node /tmp/pipeline-output/src/index.ts (or tsx)
148
- 2. POST a URL to shorten it using curl
149
- 3. Verify the GET redirect works
150
- 4. Report what passed and what (if anything) failed.
151
- Kill the server after testing.`,
152
- assignee: 'tester',
153
- dependsOn: ['Implement: URL shortener'],
154
- },
155
- {
156
- title: 'Review: URL shortener',
157
- description: `Read all .ts files in /tmp/pipeline-output/src/ and the design spec.
158
- Produce a structured code review with sections:
159
- - Summary (2 sentences)
160
- - Strengths (bullet list)
161
- - Issues (bullet list, or "None" if clean)
162
- - Verdict: SHIP or NEEDS WORK`,
163
- assignee: 'reviewer',
164
- dependsOn: ['Implement: URL shortener'], // runs in parallel with Test after Implement completes
165
- },
166
- ]
167
-
168
- // ---------------------------------------------------------------------------
169
- // Run
170
- // ---------------------------------------------------------------------------
171
-
172
- console.log('Starting 4-stage task pipeline...\n')
173
- console.log('Pipeline: design → implement → test + review (parallel)')
174
- console.log('='.repeat(60))
175
-
176
- const result = await orchestrator.runTasks(team, tasks)
177
-
178
- // ---------------------------------------------------------------------------
179
- // Summary
180
- // ---------------------------------------------------------------------------
181
-
182
- console.log('\n' + '='.repeat(60))
183
- console.log('Pipeline complete.\n')
184
- console.log(`Overall success: ${result.success}`)
185
- console.log(`Tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`)
186
-
187
- console.log('\nPer-agent summary:')
188
- for (const [name, r] of result.agentResults) {
189
- const icon = r.success ? 'OK ' : 'FAIL'
190
- const toolCount = r.toolCalls.map(c => c.toolName).join(', ')
191
- console.log(` [${icon}] ${name.padEnd(14)} tools used: ${toolCount || '(none)'}`)
192
- }
193
-
194
- // Print the reviewer's verdict
195
- const review = result.agentResults.get('reviewer')
196
- if (review?.success) {
197
- console.log('\nCode review:')
198
- console.log('─'.repeat(60))
199
- console.log(review.output)
200
- console.log('─'.repeat(60))
201
- }