swellai 1.0.0

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 (130) hide show
  1. package/README.md +711 -0
  2. package/dist/agents/linear-agent.d.ts +32 -0
  3. package/dist/agents/linear-agent.d.ts.map +1 -0
  4. package/dist/agents/linear-agent.js +263 -0
  5. package/dist/agents/linear-agent.js.map +1 -0
  6. package/dist/agents/planning-agent.d.ts +36 -0
  7. package/dist/agents/planning-agent.d.ts.map +1 -0
  8. package/dist/agents/planning-agent.js +248 -0
  9. package/dist/agents/planning-agent.js.map +1 -0
  10. package/dist/cli/index.d.ts +3 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +102 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/cli/install.d.ts +11 -0
  15. package/dist/cli/install.d.ts.map +1 -0
  16. package/dist/cli/install.js +257 -0
  17. package/dist/cli/install.js.map +1 -0
  18. package/dist/cli/manifest.d.ts +27 -0
  19. package/dist/cli/manifest.d.ts.map +1 -0
  20. package/dist/cli/manifest.js +65 -0
  21. package/dist/cli/manifest.js.map +1 -0
  22. package/dist/index.d.ts +17 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +17 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/lib/claude-agent-sdk.d.ts +73 -0
  27. package/dist/lib/claude-agent-sdk.d.ts.map +1 -0
  28. package/dist/lib/claude-agent-sdk.js +114 -0
  29. package/dist/lib/claude-agent-sdk.js.map +1 -0
  30. package/dist/lib/conversation-logger.d.ts +66 -0
  31. package/dist/lib/conversation-logger.d.ts.map +1 -0
  32. package/dist/lib/conversation-logger.js +159 -0
  33. package/dist/lib/conversation-logger.js.map +1 -0
  34. package/dist/lib/opencode.d.ts +68 -0
  35. package/dist/lib/opencode.d.ts.map +1 -0
  36. package/dist/lib/opencode.js +151 -0
  37. package/dist/lib/opencode.js.map +1 -0
  38. package/dist/lib/turso-schema.d.ts +13 -0
  39. package/dist/lib/turso-schema.d.ts.map +1 -0
  40. package/dist/lib/turso-schema.js +69 -0
  41. package/dist/lib/turso-schema.js.map +1 -0
  42. package/dist/lib/turso.d.ts +56 -0
  43. package/dist/lib/turso.d.ts.map +1 -0
  44. package/dist/lib/turso.js +144 -0
  45. package/dist/lib/turso.js.map +1 -0
  46. package/dist/lib/types.d.ts +31 -0
  47. package/dist/lib/types.d.ts.map +1 -0
  48. package/dist/lib/types.js +20 -0
  49. package/dist/lib/types.js.map +1 -0
  50. package/dist/lib/utils.d.ts +34 -0
  51. package/dist/lib/utils.d.ts.map +1 -0
  52. package/dist/lib/utils.js +72 -0
  53. package/dist/lib/utils.js.map +1 -0
  54. package/dist/scripts/build-templates.d.ts +17 -0
  55. package/dist/scripts/build-templates.d.ts.map +1 -0
  56. package/dist/scripts/build-templates.js +132 -0
  57. package/dist/scripts/build-templates.js.map +1 -0
  58. package/dist/scripts/claude-agent-runner.d.ts +28 -0
  59. package/dist/scripts/claude-agent-runner.d.ts.map +1 -0
  60. package/dist/scripts/claude-agent-runner.js +278 -0
  61. package/dist/scripts/claude-agent-runner.js.map +1 -0
  62. package/dist/src/agents/linear-agent.d.ts +32 -0
  63. package/dist/src/agents/linear-agent.d.ts.map +1 -0
  64. package/dist/src/agents/linear-agent.js +285 -0
  65. package/dist/src/agents/linear-agent.js.map +1 -0
  66. package/dist/src/agents/planning-agent.d.ts +36 -0
  67. package/dist/src/agents/planning-agent.d.ts.map +1 -0
  68. package/dist/src/agents/planning-agent.js +248 -0
  69. package/dist/src/agents/planning-agent.js.map +1 -0
  70. package/dist/src/cli/index.d.ts +3 -0
  71. package/dist/src/cli/index.d.ts.map +1 -0
  72. package/dist/src/cli/index.js +102 -0
  73. package/dist/src/cli/index.js.map +1 -0
  74. package/dist/src/cli/install.d.ts +11 -0
  75. package/dist/src/cli/install.d.ts.map +1 -0
  76. package/dist/src/cli/install.js +257 -0
  77. package/dist/src/cli/install.js.map +1 -0
  78. package/dist/src/cli/manifest.d.ts +27 -0
  79. package/dist/src/cli/manifest.d.ts.map +1 -0
  80. package/dist/src/cli/manifest.js +65 -0
  81. package/dist/src/cli/manifest.js.map +1 -0
  82. package/dist/src/index.d.ts +17 -0
  83. package/dist/src/index.d.ts.map +1 -0
  84. package/dist/src/index.js +17 -0
  85. package/dist/src/index.js.map +1 -0
  86. package/dist/src/lib/claude-agent-sdk.d.ts +73 -0
  87. package/dist/src/lib/claude-agent-sdk.d.ts.map +1 -0
  88. package/dist/src/lib/claude-agent-sdk.js +114 -0
  89. package/dist/src/lib/claude-agent-sdk.js.map +1 -0
  90. package/dist/src/lib/conversation-logger.d.ts +66 -0
  91. package/dist/src/lib/conversation-logger.d.ts.map +1 -0
  92. package/dist/src/lib/conversation-logger.js +159 -0
  93. package/dist/src/lib/conversation-logger.js.map +1 -0
  94. package/dist/src/lib/opencode.d.ts +153 -0
  95. package/dist/src/lib/opencode.d.ts.map +1 -0
  96. package/dist/src/lib/opencode.js +153 -0
  97. package/dist/src/lib/opencode.js.map +1 -0
  98. package/dist/src/lib/turso-schema.d.ts +13 -0
  99. package/dist/src/lib/turso-schema.d.ts.map +1 -0
  100. package/dist/src/lib/turso-schema.js +69 -0
  101. package/dist/src/lib/turso-schema.js.map +1 -0
  102. package/dist/src/lib/turso.d.ts +56 -0
  103. package/dist/src/lib/turso.d.ts.map +1 -0
  104. package/dist/src/lib/turso.js +144 -0
  105. package/dist/src/lib/turso.js.map +1 -0
  106. package/dist/src/lib/types.d.ts +31 -0
  107. package/dist/src/lib/types.d.ts.map +1 -0
  108. package/dist/src/lib/types.js +20 -0
  109. package/dist/src/lib/types.js.map +1 -0
  110. package/dist/src/lib/utils.d.ts +34 -0
  111. package/dist/src/lib/utils.d.ts.map +1 -0
  112. package/dist/src/lib/utils.js +72 -0
  113. package/dist/src/lib/utils.js.map +1 -0
  114. package/package.json +63 -0
  115. package/templates/.env.example +51 -0
  116. package/templates/agents/codebase-analyzer.md +121 -0
  117. package/templates/agents/codebase-locator.md +105 -0
  118. package/templates/agents/coding-agent.md +187 -0
  119. package/templates/agents/debug-agent.md +300 -0
  120. package/templates/prompts/consolidate-and-create-linear.md +282 -0
  121. package/templates/prompts/implementation.md +94 -0
  122. package/templates/prompts/plan-generation.md +171 -0
  123. package/templates/prompts/review.md +39 -0
  124. package/templates/prompts/verify.md +80 -0
  125. package/templates/scripts/claude-agent-runner.js +12887 -0
  126. package/templates/scripts/detect-runtime.sh +95 -0
  127. package/templates/scripts/linear-agent.js +1753 -0
  128. package/templates/scripts/planning-agent.js +1738 -0
  129. package/templates/workflows/claude-implement.yml +931 -0
  130. package/templates/workflows/claude-plan.yml +301 -0
@@ -0,0 +1,931 @@
1
+ name: Claude Implement - Parallel Implementation Workflow
2
+
3
+ on:
4
+ issues:
5
+ types: [labeled]
6
+ workflow_dispatch:
7
+ inputs:
8
+ linear_issue:
9
+ description: 'Linear issue ID (e.g., ENG-123) or URL'
10
+ required: true
11
+ type: string
12
+ num_implementations:
13
+ description: 'Number of parallel implementations'
14
+ required: false
15
+ type: number
16
+ default: 3
17
+ claude_model:
18
+ description: 'Claude model to use'
19
+ required: false
20
+ type: string
21
+ default: 'claude-opus-4-5-20251101'
22
+ dry_run:
23
+ description: 'Skip Claude, use mock responses'
24
+ required: false
25
+ type: boolean
26
+ default: false
27
+
28
+ jobs:
29
+ generate-matrix:
30
+ runs-on: ubuntu-latest
31
+ outputs:
32
+ matrix: ${{ steps.generate.outputs.matrix }}
33
+ linear_issue: ${{ steps.extract.outputs.linear_issue }}
34
+ steps:
35
+ - name: Validate authentication
36
+ if: ${{ inputs.dry_run != true }}
37
+ env:
38
+ CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
40
+ run: |
41
+ if [[ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]] && [[ -z "$ANTHROPIC_API_KEY" ]]; then
42
+ echo "Error: At least one authentication method must be provided"
43
+ echo "Please provide either CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY"
44
+ exit 1
45
+ fi
46
+
47
+ - name: Extract Linear issue from label or input
48
+ id: extract
49
+ env:
50
+ EVENT_NAME: ${{ github.event_name }}
51
+ INPUT_LINEAR_ISSUE: ${{ inputs.linear_issue }}
52
+ ISSUE_LABELS: ${{ toJson(github.event.issue.labels.*.name) }}
53
+ run: |
54
+ if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
55
+ ISSUE="$INPUT_LINEAR_ISSUE"
56
+ else
57
+ # Extract from label like "linear:ENG-123"
58
+ ISSUE=$(echo "$ISSUE_LABELS" | jq -r '.[] | select(startswith("linear:")) | sub("linear:"; "")')
59
+ if [ -z "$ISSUE" ]; then
60
+ echo "Error: No linear: label found on issue"
61
+ exit 1
62
+ fi
63
+ fi
64
+ echo "linear_issue=$ISSUE" >> $GITHUB_OUTPUT
65
+ echo "Using Linear issue: $ISSUE"
66
+
67
+ - name: Generate matrix
68
+ id: generate
69
+ run: |
70
+ # Generate array [1, 2, ..., num_implementations]
71
+ MATRIX=$(seq 1 ${{ inputs.num_implementations || 3 }} | jq -s -c '.')
72
+ echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
73
+ echo "Generated matrix: $MATRIX"
74
+
75
+ implement:
76
+ needs: generate-matrix
77
+ runs-on: ubuntu-latest
78
+ strategy:
79
+ matrix:
80
+ impl: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }}
81
+ fail-fast: false
82
+
83
+ steps:
84
+ - name: Checkout
85
+ uses: actions/checkout@v4
86
+ with:
87
+ fetch-depth: 0
88
+ token: ${{ secrets.GH_PAT }}
89
+
90
+ - name: Create implementation branch
91
+ run: |
92
+ BRANCH="impl-${{ github.run_id }}-${{ matrix.impl }}"
93
+ git checkout -b "$BRANCH"
94
+ echo "BRANCH=$BRANCH" >> $GITHUB_ENV
95
+
96
+ - name: Setup Claude CLI
97
+ if: ${{ inputs.dry_run != true }}
98
+ env:
99
+ CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
100
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
101
+ run: |
102
+ # Validate authentication
103
+ if [[ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]] && [[ -z "$ANTHROPIC_API_KEY" ]]; then
104
+ echo "Error: At least one authentication method must be provided"
105
+ exit 1
106
+ fi
107
+
108
+ # Install Claude CLI
109
+ curl -fsSL https://claude.ai/install.sh | bash
110
+ echo "$HOME/.local/bin" >> $GITHUB_PATH
111
+
112
+ # Set authentication method
113
+ if [[ -n "$CLAUDE_CODE_OAUTH_TOKEN" ]]; then
114
+ echo "CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN" >> $GITHUB_ENV
115
+ echo "Using OAuth token for authentication"
116
+ elif [[ -n "$ANTHROPIC_API_KEY" ]]; then
117
+ echo "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" >> $GITHUB_ENV
118
+ echo "Using API key for authentication"
119
+ fi
120
+
121
+ - name: Setup Bun for Agent Runner
122
+ if: ${{ inputs.dry_run != true }}
123
+ uses: oven-sh/setup-bun@v2
124
+ with:
125
+ bun-version: latest
126
+
127
+ - name: Install agent runner dependencies
128
+ if: ${{ inputs.dry_run != true }}
129
+ run: |
130
+ cd ${{ github.workspace }}
131
+ bun install
132
+
133
+ - name: Copy custom agents to home directory
134
+ if: ${{ inputs.dry_run != true }}
135
+ run: |
136
+ mkdir -p "$HOME/.claude/agents"
137
+ for agent in codebase-analyzer codebase-locator coding-agent debug-agent; do
138
+ cp .claude/agents/${agent}.md "$HOME/.claude/agents/${agent}.md"
139
+ done
140
+ echo "Installed agents:"
141
+ ls -la "$HOME/.claude/agents/"
142
+
143
+ - name: Detect runtime
144
+ id: runtime
145
+ run: |
146
+ # Run the bundled detect-runtime script from installed location
147
+ bash .github/claude-parallel/scripts/detect-runtime.sh
148
+
149
+ - name: Setup Bun
150
+ if: steps.runtime.outputs.runtime == 'js' && steps.runtime.outputs.package_manager == 'bun'
151
+ uses: oven-sh/setup-bun@v2
152
+
153
+ - name: Setup Node.js
154
+ if: steps.runtime.outputs.runtime == 'js' && steps.runtime.outputs.package_manager != 'bun'
155
+ uses: actions/setup-node@v4
156
+ with:
157
+ node-version: '20'
158
+
159
+ - name: Setup Python
160
+ if: steps.runtime.outputs.runtime == 'python'
161
+ uses: actions/setup-python@v5
162
+ with:
163
+ python-version: '3.11'
164
+
165
+ - name: Setup Go
166
+ if: steps.runtime.outputs.runtime == 'go'
167
+ uses: actions/setup-go@v5
168
+ with:
169
+ go-version: '1.21'
170
+
171
+ - name: Setup Rust
172
+ if: steps.runtime.outputs.runtime == 'rust'
173
+ uses: actions-rust-lang/setup-rust-toolchain@v1
174
+
175
+ - name: Install dependencies
176
+ run: |
177
+ case "${{ steps.runtime.outputs.runtime }}" in
178
+ js)
179
+ if [ -f "package.json" ]; then
180
+ case "${{ steps.runtime.outputs.package_manager }}" in
181
+ bun) bun install ;;
182
+ pnpm) npm install -g pnpm && pnpm install ;;
183
+ yarn) yarn install ;;
184
+ npm) npm install ;;
185
+ esac
186
+ fi
187
+ ;;
188
+ python)
189
+ if [ -f "requirements.txt" ]; then
190
+ pip install -r requirements.txt
191
+ elif [ -f "pyproject.toml" ]; then
192
+ if [ "${{ steps.runtime.outputs.package_manager }}" = "poetry" ]; then
193
+ pip install poetry && poetry install
194
+ else
195
+ pip install .
196
+ fi
197
+ fi
198
+ ;;
199
+ go)
200
+ if [ -f "go.mod" ]; then
201
+ go mod download
202
+ fi
203
+ ;;
204
+ rust)
205
+ if [ -f "Cargo.toml" ]; then
206
+ cargo fetch
207
+ fi
208
+ ;;
209
+ esac
210
+
211
+ - name: Run implementation
212
+ if: ${{ inputs.dry_run != true }}
213
+ env:
214
+ LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
215
+ run: |
216
+ export PATH="$HOME/.local/bin:$PATH"
217
+
218
+ # Read implementation prompt template from installed location
219
+ IMPL_TEMPLATE=$(cat .github/claude-parallel/prompts/implementation.md)
220
+
221
+ # Substitute LINEAR_ISSUE placeholder with the issue ID/URL
222
+ IMPL_PROMPT=$(echo "$IMPL_TEMPLATE" | sed "s|{{LINEAR_ISSUE}}|${{ needs.generate-matrix.outputs.linear_issue }}|g")
223
+
224
+ echo "Starting implementation ${{ matrix.impl }}..."
225
+ echo "Linear issue: ${{ needs.generate-matrix.outputs.linear_issue }}"
226
+ echo "Prompt size: $(echo "$IMPL_PROMPT" | wc -c) bytes"
227
+
228
+ # Run agent runner and capture exit code
229
+ set +e
230
+ echo "$IMPL_PROMPT" | node .github/claude-parallel/scripts/claude-agent-runner.js \
231
+ --cwd "$(pwd)" \
232
+ --model ${{ inputs.claude_model || 'claude-opus-4-5-20251101' }} \
233
+ --mode implementation \
234
+ > result.json 2> error.log
235
+ RUNNER_EXIT=$?
236
+ set -e
237
+
238
+ echo "Agent runner exit code: $RUNNER_EXIT"
239
+
240
+ # Check if we got valid output
241
+ if [ -s result.json ] && jq -e . result.json > /dev/null 2>&1; then
242
+ echo "Implementation completed successfully"
243
+ else
244
+ echo "::error::Implementation ${{ matrix.impl }} failed"
245
+ echo "=== Agent runner exit code: $RUNNER_EXIT ==="
246
+ echo "=== result.json contents (first 500 chars): ==="
247
+ head -c 500 result.json 2>/dev/null || echo "(empty or missing)"
248
+ echo ""
249
+ echo "=== error.log contents: ==="
250
+ cat error.log 2>/dev/null || echo "(empty or missing)"
251
+ exit 1
252
+ fi
253
+
254
+ - name: Mock implementation (dry run)
255
+ if: ${{ inputs.dry_run == true }}
256
+ run: |
257
+ # Create mock result
258
+ echo '{"result": "Mock implementation ${{ matrix.impl }} completed"}' > result.json
259
+
260
+ # Create a mock change to test the workflow
261
+ echo "# Mock Implementation ${{ matrix.impl }}" >> MOCK_CHANGES.md
262
+ echo "" >> MOCK_CHANGES.md
263
+ echo "Issue: ${{ needs.generate-matrix.outputs.linear_issue }}" >> MOCK_CHANGES.md
264
+ echo "Run ID: ${{ github.run_id }}" >> MOCK_CHANGES.md
265
+ echo "Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> MOCK_CHANGES.md
266
+
267
+ - name: Commit changes
268
+ env:
269
+ GIT_AUTHOR_NAME: Claude Parallel Bot
270
+ GIT_AUTHOR_EMAIL: bot@claude-parallel.dev
271
+ GIT_COMMITTER_NAME: Claude Parallel Bot
272
+ GIT_COMMITTER_EMAIL: bot@claude-parallel.dev
273
+ run: |
274
+ git config user.name "Claude Parallel Bot"
275
+ git config user.email "bot@claude-parallel.dev"
276
+
277
+ # Add all changes
278
+ git add -A
279
+
280
+ if git diff --staged --quiet; then
281
+ echo "No changes to commit"
282
+ echo "HAS_CHANGES=false" >> $GITHUB_ENV
283
+ else
284
+ git commit -m "Implementation ${{ matrix.impl }}: ${{ needs.generate-matrix.outputs.linear_issue }}"
285
+ echo "HAS_CHANGES=true" >> $GITHUB_ENV
286
+ fi
287
+
288
+ - name: Push branch
289
+ if: env.HAS_CHANGES == 'true'
290
+ run: |
291
+ git push origin "$BRANCH"
292
+
293
+ - name: Upload result
294
+ uses: actions/upload-artifact@v4
295
+ with:
296
+ name: impl-${{ matrix.impl }}
297
+ path: |
298
+ result.json
299
+ error.log
300
+ retention-days: 1
301
+
302
+ review:
303
+ needs: [generate-matrix, implement]
304
+ if: always() && !cancelled()
305
+ runs-on: ubuntu-latest
306
+ outputs:
307
+ winning_branch: ${{ steps.create_pr.outputs.winning_branch }}
308
+ pr_number: ${{ steps.create_pr.outputs.pr_number }}
309
+
310
+ steps:
311
+ - name: Checkout
312
+ uses: actions/checkout@v4
313
+ with:
314
+ fetch-depth: 0
315
+ token: ${{ secrets.GH_PAT }}
316
+
317
+ - name: Download all implementation results
318
+ uses: actions/download-artifact@v4
319
+ with:
320
+ path: artifacts
321
+
322
+ - name: Fetch implementation branches
323
+ run: |
324
+ mkdir -p worktrees
325
+ for i in $(echo '${{ needs.generate-matrix.outputs.matrix }}' | jq -r '.[]'); do
326
+ BRANCH="impl-${{ github.run_id }}-$i"
327
+ if git fetch origin "$BRANCH" 2>/dev/null; then
328
+ git worktree add "worktrees/impl-$i" "origin/$BRANCH"
329
+ echo "Fetched impl-$i"
330
+ else
331
+ echo "Branch $BRANCH not found (implementation may have failed)"
332
+ fi
333
+ done
334
+
335
+ - name: Setup Claude CLI
336
+ if: ${{ inputs.dry_run != true }}
337
+ env:
338
+ CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
339
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
340
+ run: |
341
+ # Validate authentication
342
+ if [[ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]] && [[ -z "$ANTHROPIC_API_KEY" ]]; then
343
+ echo "Error: At least one authentication method must be provided"
344
+ exit 1
345
+ fi
346
+
347
+ # Install Claude CLI
348
+ curl -fsSL https://claude.ai/install.sh | bash
349
+ echo "$HOME/.local/bin" >> $GITHUB_PATH
350
+
351
+ # Set authentication method
352
+ if [[ -n "$CLAUDE_CODE_OAUTH_TOKEN" ]]; then
353
+ echo "CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN" >> $GITHUB_ENV
354
+ echo "Using OAuth token for authentication"
355
+ elif [[ -n "$ANTHROPIC_API_KEY" ]]; then
356
+ echo "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" >> $GITHUB_ENV
357
+ echo "Using API key for authentication"
358
+ fi
359
+
360
+ - name: Setup Bun for Agent Runner
361
+ if: ${{ inputs.dry_run != true }}
362
+ uses: oven-sh/setup-bun@v2
363
+ with:
364
+ bun-version: latest
365
+
366
+ - name: Install agent runner dependencies
367
+ if: ${{ inputs.dry_run != true }}
368
+ run: |
369
+ cd ${{ github.workspace }}
370
+ bun install
371
+
372
+ - name: Copy custom agents to home directory
373
+ if: ${{ inputs.dry_run != true }}
374
+ run: |
375
+ mkdir -p "$HOME/.claude/agents"
376
+ for agent in codebase-analyzer codebase-locator coding-agent debug-agent; do
377
+ cp .claude/agents/${agent}.md "$HOME/.claude/agents/${agent}.md"
378
+ done
379
+ echo "Installed agents:"
380
+ ls -la "$HOME/.claude/agents/"
381
+
382
+ - name: Review implementations
383
+ if: ${{ inputs.dry_run != true }}
384
+ env:
385
+ LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
386
+ run: |
387
+ export PATH="$HOME/.local/bin:$PATH"
388
+
389
+ # Read review prompt template from installed location
390
+ REVIEW_TEMPLATE=$(cat .github/claude-parallel/prompts/review.md)
391
+
392
+ # Substitute template variables
393
+ REVIEW_PROMPT=$(echo "$REVIEW_TEMPLATE" | sed \
394
+ -e "s|{{LINEAR_ISSUE}}|${{ needs.generate-matrix.outputs.linear_issue }}|g" \
395
+ -e "s|{{WORKTREES_DIR}}|worktrees|g" \
396
+ -e "s|{{NUM_IMPLEMENTATIONS}}|${{ inputs.num_implementations || 3 }}|g")
397
+
398
+ echo "Starting review..."
399
+ echo "Linear issue: ${{ needs.generate-matrix.outputs.linear_issue }}"
400
+
401
+ # Run agent runner in review mode (schema automatically applied)
402
+ echo "$REVIEW_PROMPT" | node .github/claude-parallel/scripts/claude-agent-runner.js \
403
+ --cwd "$(pwd)" \
404
+ --model ${{ inputs.claude_model || 'claude-opus-4-5-20251101' }} \
405
+ --mode review \
406
+ > review-result.json 2> review-error.log || true
407
+
408
+ cat review-result.json
409
+
410
+ - name: Mock review (dry run)
411
+ if: ${{ inputs.dry_run == true }}
412
+ run: |
413
+ # Pick a random winner (1-num_implementations) for testing
414
+ MAX=${{ inputs.num_implementations || 3 }}
415
+ WINNER=$(( (RANDOM % MAX) + 1 ))
416
+ echo "Mock review selecting implementation $WINNER"
417
+
418
+ # Agent runner with --mode review always produces structured_output
419
+ echo "{\"type\":\"result\",\"result\":\"\",\"structured_output\":{\"best\":$WINNER,\"reasoning\":\"Dry run mock - selected implementation $WINNER\"}}" > review-result.json
420
+
421
+ echo "=== Mock review-result.json: ==="
422
+ cat review-result.json
423
+
424
+ - name: Parse review and create PR
425
+ id: create_pr
426
+ env:
427
+ GH_TOKEN: ${{ secrets.GH_PAT }}
428
+ run: |
429
+ # Extract decision from structured_output (guaranteed by agent runner)
430
+ echo "=== Parsing review result ==="
431
+ cat review-result.json
432
+ echo ""
433
+
434
+ DECISION_JSON=$(jq -c '.structured_output' review-result.json 2>/dev/null)
435
+
436
+ # Validate the decision exists and has required fields
437
+ if [ -z "$DECISION_JSON" ] || [ "$DECISION_JSON" = "null" ]; then
438
+ echo "::error::No structured_output field in review result"
439
+ echo "Agent runner with --mode review should always produce structured_output"
440
+ exit 1
441
+ fi
442
+
443
+ if ! echo "$DECISION_JSON" | jq -e '.best' >/dev/null 2>&1; then
444
+ echo "::error::structured_output missing 'best' field"
445
+ exit 1
446
+ fi
447
+
448
+ echo "✓ Successfully extracted decision from structured_output"
449
+
450
+ # Parse the extracted JSON
451
+ BEST=$(echo "$DECISION_JSON" | jq -r '.best')
452
+ REASONING=$(echo "$DECISION_JSON" | jq -r '.reasoning // "No reasoning provided"')
453
+
454
+ echo "Selected implementation: $BEST"
455
+
456
+ WINNING_BRANCH="impl-${{ github.run_id }}-$BEST"
457
+ echo "winning_branch=$WINNING_BRANCH" >> $GITHUB_OUTPUT
458
+
459
+ # Create PR
460
+ PR_BODY="## AI-Generated Implementation (Best of ${{ inputs.num_implementations || 3 }})
461
+
462
+ **Linear Issue:** ${{ needs.generate-matrix.outputs.linear_issue }}
463
+ **Selected:** Implementation $BEST
464
+
465
+ ### Reasoning
466
+ $REASONING
467
+
468
+ ---
469
+ *Generated by Claude Parallel workflow*"
470
+
471
+ PR_URL=$(gh pr create \
472
+ --head "$WINNING_BRANCH" \
473
+ --title "Implementation: ${{ needs.generate-matrix.outputs.linear_issue }}" \
474
+ --body "$PR_BODY" \
475
+ --draft)
476
+
477
+ # Extract PR number from URL
478
+ PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
479
+ echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
480
+ echo "Created PR #$PR_NUMBER"
481
+
482
+ - name: Cleanup losing branches
483
+ if: always() && steps.create_pr.outputs.winning_branch != ''
484
+ run: |
485
+ WINNING="${{ steps.create_pr.outputs.winning_branch }}"
486
+ # Extract impl number (last character after final dash)
487
+ BEST="${WINNING##*-}"
488
+
489
+ for i in $(echo '${{ needs.generate-matrix.outputs.matrix }}' | jq -r '.[]'); do
490
+ if [ "$i" != "$BEST" ]; then
491
+ BRANCH="impl-${{ github.run_id }}-$i"
492
+ git push origin --delete "$BRANCH" 2>/dev/null || true
493
+ fi
494
+ done
495
+
496
+ verify:
497
+ needs: [generate-matrix, review]
498
+ if: needs.review.outputs.pr_number != ''
499
+ runs-on: ubuntu-latest
500
+
501
+ steps:
502
+ - name: Checkout winning branch
503
+ uses: actions/checkout@v4
504
+ with:
505
+ ref: ${{ needs.review.outputs.winning_branch }}
506
+ fetch-depth: 0
507
+ token: ${{ secrets.GH_PAT }}
508
+
509
+ - name: Setup Claude CLI
510
+ if: ${{ inputs.dry_run != true }}
511
+ env:
512
+ CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
513
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
514
+ run: |
515
+ # Validate authentication
516
+ if [[ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]] && [[ -z "$ANTHROPIC_API_KEY" ]]; then
517
+ echo "Error: At least one authentication method must be provided"
518
+ exit 1
519
+ fi
520
+
521
+ # Install Claude CLI
522
+ curl -fsSL https://claude.ai/install.sh | bash
523
+ echo "$HOME/.local/bin" >> $GITHUB_PATH
524
+
525
+ # Set authentication method
526
+ if [[ -n "$CLAUDE_CODE_OAUTH_TOKEN" ]]; then
527
+ echo "CLAUDE_CODE_OAUTH_TOKEN=$CLAUDE_CODE_OAUTH_TOKEN" >> $GITHUB_ENV
528
+ echo "Using OAuth token for authentication"
529
+ elif [[ -n "$ANTHROPIC_API_KEY" ]]; then
530
+ echo "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" >> $GITHUB_ENV
531
+ echo "Using API key for authentication"
532
+ fi
533
+
534
+ - name: Setup Bun for Agent Runner
535
+ if: ${{ inputs.dry_run != true }}
536
+ uses: oven-sh/setup-bun@v2
537
+ with:
538
+ bun-version: latest
539
+
540
+ - name: Install agent runner dependencies
541
+ if: ${{ inputs.dry_run != true }}
542
+ run: |
543
+ cd ${{ github.workspace }}
544
+ bun install
545
+
546
+ - name: Copy custom agents to home directory
547
+ if: ${{ inputs.dry_run != true }}
548
+ run: |
549
+ mkdir -p "$HOME/.claude/agents"
550
+ for agent in codebase-analyzer codebase-locator coding-agent debug-agent; do
551
+ cp .claude/agents/${agent}.md "$HOME/.claude/agents/${agent}.md"
552
+ done
553
+ echo "Installed agents:"
554
+ ls -la "$HOME/.claude/agents/"
555
+
556
+ - name: Detect runtime
557
+ id: runtime
558
+ run: |
559
+ # Run the bundled detect-runtime script from installed location
560
+ bash .github/claude-parallel/scripts/detect-runtime.sh
561
+
562
+ - name: Setup Bun
563
+ if: steps.runtime.outputs.runtime == 'js' && steps.runtime.outputs.package_manager == 'bun'
564
+ uses: oven-sh/setup-bun@v2
565
+
566
+ - name: Setup Node.js
567
+ if: steps.runtime.outputs.runtime == 'js' && steps.runtime.outputs.package_manager != 'bun'
568
+ uses: actions/setup-node@v4
569
+ with:
570
+ node-version: '20'
571
+
572
+ - name: Setup Python
573
+ if: steps.runtime.outputs.runtime == 'python'
574
+ uses: actions/setup-python@v5
575
+ with:
576
+ python-version: '3.11'
577
+
578
+ - name: Setup Go
579
+ if: steps.runtime.outputs.runtime == 'go'
580
+ uses: actions/setup-go@v5
581
+ with:
582
+ go-version: '1.21'
583
+
584
+ - name: Setup Rust
585
+ if: steps.runtime.outputs.runtime == 'rust'
586
+ uses: actions-rust-lang/setup-rust-toolchain@v1
587
+
588
+ - name: Install dependencies
589
+ run: |
590
+ case "${{ steps.runtime.outputs.runtime }}" in
591
+ js)
592
+ if [ -f "package.json" ]; then
593
+ case "${{ steps.runtime.outputs.package_manager }}" in
594
+ bun) bun install ;;
595
+ pnpm) npm install -g pnpm && pnpm install ;;
596
+ yarn) yarn install ;;
597
+ npm) npm install ;;
598
+ esac
599
+ fi
600
+ ;;
601
+ python)
602
+ if [ -f "requirements.txt" ]; then
603
+ pip install -r requirements.txt
604
+ elif [ -f "pyproject.toml" ]; then
605
+ if [ "${{ steps.runtime.outputs.package_manager }}" = "poetry" ]; then
606
+ pip install poetry && poetry install
607
+ else
608
+ pip install .
609
+ fi
610
+ fi
611
+ ;;
612
+ go)
613
+ if [ -f "go.mod" ]; then
614
+ go mod download
615
+ fi
616
+ ;;
617
+ rust)
618
+ if [ -f "Cargo.toml" ]; then
619
+ cargo fetch
620
+ fi
621
+ ;;
622
+ esac
623
+
624
+ - name: Run build checks
625
+ id: build_checks
626
+ run: |
627
+ mkdir -p /tmp/build-results
628
+
629
+ # Initialize statuses
630
+ BUILD_STATUS="skip"
631
+ TESTS_STATUS="skip"
632
+ LINT_STATUS="skip"
633
+ TYPECHECK_STATUS="skip"
634
+
635
+ # Run build
636
+ echo "=== Running build ===" > /tmp/build-results/build.txt
637
+ case "${{ steps.runtime.outputs.runtime }}" in
638
+ js)
639
+ if [ -f "package.json" ] && grep -q '"build"' package.json; then
640
+ if ${{ steps.runtime.outputs.package_manager }} run build >> /tmp/build-results/build.txt 2>&1; then
641
+ BUILD_STATUS="pass"
642
+ else
643
+ BUILD_STATUS="fail"
644
+ fi
645
+ else
646
+ echo "No build script found" >> /tmp/build-results/build.txt
647
+ fi
648
+ ;;
649
+ python)
650
+ if [ -f "setup.py" ] || ([ -f "pyproject.toml" ] && grep -q '\[build-system\]' pyproject.toml); then
651
+ if python -m build >> /tmp/build-results/build.txt 2>&1; then
652
+ BUILD_STATUS="pass"
653
+ else
654
+ BUILD_STATUS="fail"
655
+ fi
656
+ else
657
+ echo "No build system found" >> /tmp/build-results/build.txt
658
+ fi
659
+ ;;
660
+ go)
661
+ if go build ./... >> /tmp/build-results/build.txt 2>&1; then
662
+ BUILD_STATUS="pass"
663
+ else
664
+ BUILD_STATUS="fail"
665
+ fi
666
+ ;;
667
+ rust)
668
+ if cargo build >> /tmp/build-results/build.txt 2>&1; then
669
+ BUILD_STATUS="pass"
670
+ else
671
+ BUILD_STATUS="fail"
672
+ fi
673
+ ;;
674
+ esac
675
+ echo "BUILD_STATUS=$BUILD_STATUS" >> $GITHUB_OUTPUT
676
+
677
+ # Run tests
678
+ echo "=== Running tests ===" > /tmp/build-results/tests.txt
679
+ case "${{ steps.runtime.outputs.runtime }}" in
680
+ js)
681
+ if [ -f "package.json" ] && grep -q '"test"' package.json; then
682
+ if ${{ steps.runtime.outputs.package_manager }} test >> /tmp/build-results/tests.txt 2>&1; then
683
+ TESTS_STATUS="pass"
684
+ else
685
+ TESTS_STATUS="fail"
686
+ fi
687
+ else
688
+ echo "No test script found" >> /tmp/build-results/tests.txt
689
+ fi
690
+ ;;
691
+ python)
692
+ if command -v pytest >/dev/null 2>&1; then
693
+ if pytest >> /tmp/build-results/tests.txt 2>&1; then
694
+ TESTS_STATUS="pass"
695
+ else
696
+ TESTS_STATUS="fail"
697
+ fi
698
+ else
699
+ echo "pytest not found" >> /tmp/build-results/tests.txt
700
+ fi
701
+ ;;
702
+ go)
703
+ if go test ./... >> /tmp/build-results/tests.txt 2>&1; then
704
+ TESTS_STATUS="pass"
705
+ else
706
+ TESTS_STATUS="fail"
707
+ fi
708
+ ;;
709
+ rust)
710
+ if cargo test >> /tmp/build-results/tests.txt 2>&1; then
711
+ TESTS_STATUS="pass"
712
+ else
713
+ TESTS_STATUS="fail"
714
+ fi
715
+ ;;
716
+ esac
717
+ echo "TESTS_STATUS=$TESTS_STATUS" >> $GITHUB_OUTPUT
718
+
719
+ # Run lint
720
+ echo "=== Running lint ===" > /tmp/build-results/lint.txt
721
+ case "${{ steps.runtime.outputs.runtime }}" in
722
+ js)
723
+ if [ -f "package.json" ] && grep -q '"lint"' package.json; then
724
+ if ${{ steps.runtime.outputs.package_manager }} run lint >> /tmp/build-results/lint.txt 2>&1; then
725
+ LINT_STATUS="pass"
726
+ else
727
+ LINT_STATUS="fail"
728
+ fi
729
+ else
730
+ echo "No lint script found" >> /tmp/build-results/lint.txt
731
+ fi
732
+ ;;
733
+ python)
734
+ if command -v ruff >/dev/null 2>&1; then
735
+ if ruff check . >> /tmp/build-results/lint.txt 2>&1; then
736
+ LINT_STATUS="pass"
737
+ else
738
+ LINT_STATUS="fail"
739
+ fi
740
+ else
741
+ echo "ruff not found" >> /tmp/build-results/lint.txt
742
+ fi
743
+ ;;
744
+ go)
745
+ if command -v golint >/dev/null 2>&1; then
746
+ if golint ./... >> /tmp/build-results/lint.txt 2>&1; then
747
+ LINT_STATUS="pass"
748
+ else
749
+ LINT_STATUS="fail"
750
+ fi
751
+ else
752
+ echo "golint not found" >> /tmp/build-results/lint.txt
753
+ fi
754
+ ;;
755
+ rust)
756
+ if cargo clippy >> /tmp/build-results/lint.txt 2>&1; then
757
+ LINT_STATUS="pass"
758
+ else
759
+ LINT_STATUS="fail"
760
+ fi
761
+ ;;
762
+ esac
763
+ echo "LINT_STATUS=$LINT_STATUS" >> $GITHUB_OUTPUT
764
+
765
+ # Run typecheck
766
+ echo "=== Running typecheck ===" > /tmp/build-results/typecheck.txt
767
+ case "${{ steps.runtime.outputs.runtime }}" in
768
+ js)
769
+ if [ -f "tsconfig.json" ]; then
770
+ if npx tsc --noEmit >> /tmp/build-results/typecheck.txt 2>&1; then
771
+ TYPECHECK_STATUS="pass"
772
+ else
773
+ TYPECHECK_STATUS="fail"
774
+ fi
775
+ else
776
+ echo "No tsconfig.json found" >> /tmp/build-results/typecheck.txt
777
+ fi
778
+ ;;
779
+ python)
780
+ if command -v mypy >/dev/null 2>&1; then
781
+ if mypy . >> /tmp/build-results/typecheck.txt 2>&1; then
782
+ TYPECHECK_STATUS="pass"
783
+ else
784
+ TYPECHECK_STATUS="fail"
785
+ fi
786
+ else
787
+ echo "mypy not found" >> /tmp/build-results/typecheck.txt
788
+ fi
789
+ ;;
790
+ *)
791
+ echo "Typecheck not applicable for ${{ steps.runtime.outputs.runtime }}" >> /tmp/build-results/typecheck.txt
792
+ ;;
793
+ esac
794
+ echo "TYPECHECK_STATUS=$TYPECHECK_STATUS" >> $GITHUB_OUTPUT
795
+
796
+ # Combine all results
797
+ cat /tmp/build-results/*.txt > /tmp/build-results/all.txt
798
+ echo "Build: $BUILD_STATUS"
799
+ echo "Tests: $TESTS_STATUS"
800
+ echo "Lint: $LINT_STATUS"
801
+ echo "TypeCheck: $TYPECHECK_STATUS"
802
+
803
+ - name: Run verification
804
+ if: ${{ inputs.dry_run != true }}
805
+ env:
806
+ LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
807
+ run: |
808
+ export PATH="$HOME/.local/bin:$PATH"
809
+
810
+ # Read verify prompt template from installed location
811
+ VERIFY_TEMPLATE=$(cat .github/claude-parallel/prompts/verify.md)
812
+
813
+ # Read build output for substitution
814
+ BUILD_OUTPUT=$(cat /tmp/build-results/all.txt)
815
+
816
+ # Substitute template variables
817
+ VERIFY_PROMPT=$(echo "$VERIFY_TEMPLATE" | sed \
818
+ -e "s|{{LINEAR_ISSUE}}|${{ needs.generate-matrix.outputs.linear_issue }}|g" \
819
+ -e "s|{{WINNING_BRANCH}}|${{ needs.review.outputs.winning_branch }}|g" \
820
+ -e "s|{{PR_NUMBER}}|${{ needs.review.outputs.pr_number }}|g" \
821
+ -e "s|{{BUILD_STATUS}}|${{ steps.build_checks.outputs.BUILD_STATUS }}|g" \
822
+ -e "s|{{TESTS_STATUS}}|${{ steps.build_checks.outputs.TESTS_STATUS }}|g" \
823
+ -e "s|{{LINT_STATUS}}|${{ steps.build_checks.outputs.LINT_STATUS }}|g" \
824
+ -e "s|{{TYPECHECK_STATUS}}|${{ steps.build_checks.outputs.TYPECHECK_STATUS }}|g" \
825
+ | awk -v build="$BUILD_OUTPUT" '{gsub(/\{\{BUILD_OUTPUT\}\}/, build); print}')
826
+
827
+ echo "Starting verification..."
828
+ echo "Linear issue: ${{ needs.generate-matrix.outputs.linear_issue }}"
829
+
830
+ # Run agent runner in implementation mode
831
+ echo "$VERIFY_PROMPT" | node .github/claude-parallel/scripts/claude-agent-runner.js \
832
+ --cwd "$(pwd)" \
833
+ --model ${{ inputs.claude_model || 'claude-opus-4-5-20251101' }} \
834
+ --mode implementation \
835
+ > verify-result.json 2> verify-error.log || true
836
+
837
+ cat verify-result.json
838
+
839
+ - name: Mock verification (dry run)
840
+ if: ${{ inputs.dry_run == true }}
841
+ run: |
842
+ # Create mock verification result based on build checks
843
+ BUILD="${{ steps.build_checks.outputs.BUILD_STATUS }}"
844
+ TESTS="${{ steps.build_checks.outputs.TESTS_STATUS }}"
845
+
846
+ if [ "$BUILD" = "pass" ] || [ "$BUILD" = "skip" ]; then
847
+ VERIFIED="true"
848
+ SUMMARY="Dry run verification passed"
849
+ else
850
+ VERIFIED="false"
851
+ SUMMARY="Dry run verification failed due to build errors"
852
+ fi
853
+
854
+ # Create mock result (no leading whitespace - matches Claude output format)
855
+ echo "{\"result\": \"Mock verification complete. {\\\"verified\\\": $VERIFIED, \\\"summary\\\": \\\"$SUMMARY\\\", \\\"issues\\\": []}\"}" > verify-result.json
856
+ cat verify-result.json
857
+
858
+ - name: Post verification results to PR
859
+ env:
860
+ GH_TOKEN: ${{ secrets.GH_PAT }}
861
+ run: |
862
+ # Use pre-computed build results
863
+ BUILD="${{ steps.build_checks.outputs.BUILD_STATUS }}"
864
+ TESTS="${{ steps.build_checks.outputs.TESTS_STATUS }}"
865
+ LINT="${{ steps.build_checks.outputs.LINT_STATUS }}"
866
+ TYPECHECK="${{ steps.build_checks.outputs.TYPECHECK_STATUS }}"
867
+
868
+ # Extract the verification result for summary and issues
869
+ RESULT=$(jq -r '.result // .content[0].text // .text // .' verify-result.json)
870
+ VERIFY_JSON=$(echo "$RESULT" | grep -oE '\{[^{}]*"verified"[^{}]*\}' | tail -1)
871
+
872
+ if [ -z "$VERIFY_JSON" ]; then
873
+ VERIFIED="false"
874
+ SUMMARY="Could not parse Claude verification output"
875
+ ISSUES=""
876
+ else
877
+ VERIFIED=$(echo "$VERIFY_JSON" | jq -r '.verified // false')
878
+ SUMMARY=$(echo "$VERIFY_JSON" | jq -r '.summary // "No summary"')
879
+ ISSUES=$(echo "$VERIFY_JSON" | jq -r '.issues // [] | .[]' | sed 's/^/- /')
880
+ fi
881
+
882
+ # Determine overall status
883
+ if [ "$BUILD" = "fail" ] || [ "$TESTS" = "fail" ]; then
884
+ STATUS=":x: **Verification Failed**"
885
+ elif [ "$VERIFIED" = "true" ]; then
886
+ STATUS=":white_check_mark: **Verified**"
887
+ else
888
+ STATUS=":warning: **Needs Review**"
889
+ fi
890
+
891
+ # Format status icons
892
+ format_status() {
893
+ case "$1" in
894
+ pass) echo ":white_check_mark: pass" ;;
895
+ fail) echo ":x: fail" ;;
896
+ skip) echo ":heavy_minus_sign: skip" ;;
897
+ *) echo ":question: $1" ;;
898
+ esac
899
+ }
900
+
901
+ COMMENT="## Verification Results
902
+
903
+ $STATUS
904
+
905
+ | Check | Status |
906
+ |-------|--------|
907
+ | Build | $(format_status "$BUILD") |
908
+ | Tests | $(format_status "$TESTS") |
909
+ | Lint | $(format_status "$LINT") |
910
+ | TypeCheck | $(format_status "$TYPECHECK") |
911
+
912
+ ### Summary
913
+ $SUMMARY"
914
+
915
+ if [ -n "$ISSUES" ]; then
916
+ COMMENT="$COMMENT
917
+
918
+ ### Issues Found
919
+ $ISSUES"
920
+ fi
921
+
922
+ gh pr comment "${{ needs.review.outputs.pr_number }}" --body "$COMMENT"
923
+
924
+ - name: Upload verification result
925
+ uses: actions/upload-artifact@v4
926
+ with:
927
+ name: verify-result
928
+ path: |
929
+ verify-result.json
930
+ verify-error.log
931
+ retention-days: 7