autonomous-coding-toolkit 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 (324) hide show
  1. package/.claude-plugin/marketplace.json +22 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/LICENSE +21 -0
  4. package/Makefile +21 -0
  5. package/README.md +140 -0
  6. package/SECURITY.md +28 -0
  7. package/agents/bash-expert.md +113 -0
  8. package/agents/dependency-auditor.md +138 -0
  9. package/agents/integration-tester.md +120 -0
  10. package/agents/lesson-scanner.md +149 -0
  11. package/agents/python-expert.md +179 -0
  12. package/agents/service-monitor.md +141 -0
  13. package/agents/shell-expert.md +147 -0
  14. package/benchmarks/runner.sh +147 -0
  15. package/benchmarks/tasks/01-rest-endpoint/rubric.sh +29 -0
  16. package/benchmarks/tasks/01-rest-endpoint/task.md +17 -0
  17. package/benchmarks/tasks/02-refactor-module/task.md +8 -0
  18. package/benchmarks/tasks/03-fix-integration-bug/task.md +8 -0
  19. package/benchmarks/tasks/04-add-test-coverage/task.md +8 -0
  20. package/benchmarks/tasks/05-multi-file-feature/task.md +8 -0
  21. package/bin/act.js +238 -0
  22. package/commands/autocode.md +6 -0
  23. package/commands/cancel-ralph.md +18 -0
  24. package/commands/code-factory.md +53 -0
  25. package/commands/create-prd.md +55 -0
  26. package/commands/ralph-loop.md +18 -0
  27. package/commands/run-plan.md +117 -0
  28. package/commands/submit-lesson.md +122 -0
  29. package/docs/ARCHITECTURE.md +630 -0
  30. package/docs/CONTRIBUTING.md +125 -0
  31. package/docs/lessons/0001-bare-exception-swallowing.md +34 -0
  32. package/docs/lessons/0002-async-def-without-await.md +28 -0
  33. package/docs/lessons/0003-create-task-without-callback.md +28 -0
  34. package/docs/lessons/0004-hardcoded-test-counts.md +28 -0
  35. package/docs/lessons/0005-sqlite-without-closing.md +33 -0
  36. package/docs/lessons/0006-venv-pip-path.md +27 -0
  37. package/docs/lessons/0007-runner-state-self-rejection.md +35 -0
  38. package/docs/lessons/0008-quality-gate-blind-spot.md +33 -0
  39. package/docs/lessons/0009-parser-overcount-empty-batches.md +36 -0
  40. package/docs/lessons/0010-local-outside-function-bash.md +33 -0
  41. package/docs/lessons/0011-batch-tests-for-unimplemented-code.md +36 -0
  42. package/docs/lessons/0012-api-markdown-unescaped-chars.md +33 -0
  43. package/docs/lessons/0013-export-prefix-env-parsing.md +33 -0
  44. package/docs/lessons/0014-decorator-registry-import-side-effect.md +43 -0
  45. package/docs/lessons/0015-frontend-backend-schema-drift.md +43 -0
  46. package/docs/lessons/0016-event-driven-cold-start-seeding.md +44 -0
  47. package/docs/lessons/0017-copy-paste-logic-diverges.md +43 -0
  48. package/docs/lessons/0018-layer-passes-pipeline-broken.md +45 -0
  49. package/docs/lessons/0019-systemd-envfile-ignores-export.md +41 -0
  50. package/docs/lessons/0020-persist-state-incrementally.md +44 -0
  51. package/docs/lessons/0021-dual-axis-testing.md +48 -0
  52. package/docs/lessons/0022-jsx-factory-shadowing.md +43 -0
  53. package/docs/lessons/0023-static-analysis-spiral.md +51 -0
  54. package/docs/lessons/0024-shared-pipeline-implementation.md +55 -0
  55. package/docs/lessons/0025-defense-in-depth-all-entry-points.md +65 -0
  56. package/docs/lessons/0026-linter-no-rules-false-enforcement.md +54 -0
  57. package/docs/lessons/0027-jsx-silent-prop-drop.md +64 -0
  58. package/docs/lessons/0028-no-infrastructure-in-client-code.md +49 -0
  59. package/docs/lessons/0029-never-write-secrets-to-files.md +61 -0
  60. package/docs/lessons/0030-cache-merge-not-replace.md +62 -0
  61. package/docs/lessons/0031-verify-units-at-boundaries.md +66 -0
  62. package/docs/lessons/0032-module-lifecycle-subscribe-unsubscribe.md +89 -0
  63. package/docs/lessons/0033-async-iteration-mutable-snapshot.md +72 -0
  64. package/docs/lessons/0034-caller-missing-await-silent-discard.md +65 -0
  65. package/docs/lessons/0035-duplicate-registration-silent-overwrite.md +85 -0
  66. package/docs/lessons/0036-websocket-dirty-disconnect.md +33 -0
  67. package/docs/lessons/0037-parallel-agents-worktree-corruption.md +31 -0
  68. package/docs/lessons/0038-subscribe-no-stored-ref.md +36 -0
  69. package/docs/lessons/0039-fallback-or-default-hides-bugs.md +34 -0
  70. package/docs/lessons/0040-event-firehose-filter-first.md +36 -0
  71. package/docs/lessons/0041-ambiguous-base-dir-path-nesting.md +32 -0
  72. package/docs/lessons/0042-spec-compliance-insufficient.md +36 -0
  73. package/docs/lessons/0043-exact-count-extensible-collections.md +32 -0
  74. package/docs/lessons/0044-relative-file-deps-worktree.md +39 -0
  75. package/docs/lessons/0045-iterative-design-improvement.md +33 -0
  76. package/docs/lessons/0046-plan-assertion-math-bugs.md +38 -0
  77. package/docs/lessons/0047-pytest-single-threaded-default.md +37 -0
  78. package/docs/lessons/0048-integration-wiring-batch.md +40 -0
  79. package/docs/lessons/0049-ab-verification.md +41 -0
  80. package/docs/lessons/0050-editing-sourced-files-during-execution.md +33 -0
  81. package/docs/lessons/0051-infrastructure-fixes-cant-self-heal.md +30 -0
  82. package/docs/lessons/0052-uncommitted-changes-poison-quality-gates.md +31 -0
  83. package/docs/lessons/0053-jq-compact-flag-inconsistency.md +31 -0
  84. package/docs/lessons/0054-parser-matches-inside-code-blocks.md +30 -0
  85. package/docs/lessons/0055-agents-compensate-for-garbled-prompts.md +31 -0
  86. package/docs/lessons/0056-grep-count-exit-code-on-zero.md +42 -0
  87. package/docs/lessons/0057-new-artifacts-break-git-clean-gates.md +42 -0
  88. package/docs/lessons/0058-dead-config-keys-never-consumed.md +49 -0
  89. package/docs/lessons/0059-contract-test-shared-structures.md +53 -0
  90. package/docs/lessons/0060-set-e-silent-death-in-runners.md +53 -0
  91. package/docs/lessons/0061-context-injection-dirty-state.md +50 -0
  92. package/docs/lessons/0062-sibling-bug-neighborhood-scan.md +29 -0
  93. package/docs/lessons/0063-one-flag-two-lifetimes.md +31 -0
  94. package/docs/lessons/0064-test-passes-wrong-reason.md +31 -0
  95. package/docs/lessons/0065-pipefail-grep-count-double-output.md +39 -0
  96. package/docs/lessons/0066-local-keyword-outside-function.md +37 -0
  97. package/docs/lessons/0067-stdin-hang-non-interactive-shell.md +36 -0
  98. package/docs/lessons/0068-agent-builds-wrong-thing-correctly.md +31 -0
  99. package/docs/lessons/0069-plan-quality-dominates-execution.md +30 -0
  100. package/docs/lessons/0070-spec-echo-back-prevents-drift.md +31 -0
  101. package/docs/lessons/0071-positive-instructions-outperform-negative.md +30 -0
  102. package/docs/lessons/0072-lost-in-the-middle-context-placement.md +30 -0
  103. package/docs/lessons/0073-unscoped-lessons-cause-false-positives.md +30 -0
  104. package/docs/lessons/0074-stale-context-injection-wrong-batch.md +32 -0
  105. package/docs/lessons/0075-research-artifacts-must-persist.md +32 -0
  106. package/docs/lessons/0076-wrong-decomposition-contaminates-downstream.md +30 -0
  107. package/docs/lessons/0077-cherry-pick-merges-need-manual-resolution.md +30 -0
  108. package/docs/lessons/0078-static-review-without-live-test.md +30 -0
  109. package/docs/lessons/0079-integration-wiring-batch-required.md +32 -0
  110. package/docs/lessons/FRAMEWORK.md +161 -0
  111. package/docs/lessons/SUMMARY.md +201 -0
  112. package/docs/lessons/TEMPLATE.md +85 -0
  113. package/docs/plans/2026-02-21-code-factory-v2-design.md +204 -0
  114. package/docs/plans/2026-02-21-code-factory-v2-implementation-plan.md +2189 -0
  115. package/docs/plans/2026-02-21-code-factory-v2-phase4-design.md +537 -0
  116. package/docs/plans/2026-02-21-code-factory-v2-phase4-implementation-plan.md +2012 -0
  117. package/docs/plans/2026-02-21-hardening-pass-design.md +108 -0
  118. package/docs/plans/2026-02-21-hardening-pass-plan.md +1378 -0
  119. package/docs/plans/2026-02-21-mab-research-report.md +406 -0
  120. package/docs/plans/2026-02-21-marketplace-restructure-design.md +240 -0
  121. package/docs/plans/2026-02-21-marketplace-restructure-plan.md +832 -0
  122. package/docs/plans/2026-02-21-phase4-completion-plan.md +697 -0
  123. package/docs/plans/2026-02-21-validator-suite-design.md +148 -0
  124. package/docs/plans/2026-02-21-validator-suite-plan.md +540 -0
  125. package/docs/plans/2026-02-22-mab-research-round2.md +556 -0
  126. package/docs/plans/2026-02-22-mab-run-design.md +462 -0
  127. package/docs/plans/2026-02-22-mab-run-plan.md +2046 -0
  128. package/docs/plans/2026-02-22-operations-design-methodology-research.md +681 -0
  129. package/docs/plans/2026-02-22-research-agent-failure-taxonomy.md +532 -0
  130. package/docs/plans/2026-02-22-research-code-guideline-policies.md +886 -0
  131. package/docs/plans/2026-02-22-research-codebase-audit-refactoring.md +908 -0
  132. package/docs/plans/2026-02-22-research-coding-standards-documentation.md +541 -0
  133. package/docs/plans/2026-02-22-research-competitive-landscape.md +687 -0
  134. package/docs/plans/2026-02-22-research-comprehensive-testing.md +1076 -0
  135. package/docs/plans/2026-02-22-research-context-utilization.md +459 -0
  136. package/docs/plans/2026-02-22-research-cost-quality-tradeoff.md +548 -0
  137. package/docs/plans/2026-02-22-research-lesson-transferability.md +508 -0
  138. package/docs/plans/2026-02-22-research-multi-agent-coordination.md +312 -0
  139. package/docs/plans/2026-02-22-research-phase-integration.md +602 -0
  140. package/docs/plans/2026-02-22-research-plan-quality.md +428 -0
  141. package/docs/plans/2026-02-22-research-prompt-engineering.md +558 -0
  142. package/docs/plans/2026-02-22-research-unconventional-perspectives.md +528 -0
  143. package/docs/plans/2026-02-22-research-user-adoption.md +638 -0
  144. package/docs/plans/2026-02-22-research-verification-effectiveness.md +433 -0
  145. package/docs/plans/2026-02-23-agent-suite-design.md +299 -0
  146. package/docs/plans/2026-02-23-agent-suite-plan.md +578 -0
  147. package/docs/plans/2026-02-23-phase3-cost-infrastructure-design.md +148 -0
  148. package/docs/plans/2026-02-23-phase3-cost-infrastructure-plan.md +1062 -0
  149. package/docs/plans/2026-02-23-research-bash-expert-agent.md +543 -0
  150. package/docs/plans/2026-02-23-research-dependency-auditor-agent.md +564 -0
  151. package/docs/plans/2026-02-23-research-improving-existing-agents.md +503 -0
  152. package/docs/plans/2026-02-23-research-integration-tester-agent.md +454 -0
  153. package/docs/plans/2026-02-23-research-python-expert-agent.md +429 -0
  154. package/docs/plans/2026-02-23-research-service-monitor-agent.md +425 -0
  155. package/docs/plans/2026-02-23-research-shell-expert-agent.md +533 -0
  156. package/docs/plans/2026-02-23-roadmap-to-completion.md +530 -0
  157. package/docs/plans/2026-02-24-headless-module-split-design.md +98 -0
  158. package/docs/plans/2026-02-24-headless-module-split.md +443 -0
  159. package/docs/plans/2026-02-24-lesson-scope-metadata-design.md +228 -0
  160. package/docs/plans/2026-02-24-lesson-scope-metadata-plan.md +968 -0
  161. package/docs/plans/2026-02-24-npm-packaging-design.md +841 -0
  162. package/docs/plans/2026-02-24-npm-packaging-plan.md +1965 -0
  163. package/docs/plans/audit-findings.md +186 -0
  164. package/docs/telegram-notification-format.md +98 -0
  165. package/examples/example-plan.md +51 -0
  166. package/examples/example-prd.json +72 -0
  167. package/examples/example-roadmap.md +33 -0
  168. package/examples/quickstart-plan.md +63 -0
  169. package/hooks/hooks.json +26 -0
  170. package/hooks/setup-symlinks.sh +48 -0
  171. package/hooks/stop-hook.sh +135 -0
  172. package/package.json +47 -0
  173. package/policies/bash.md +71 -0
  174. package/policies/python.md +71 -0
  175. package/policies/testing.md +61 -0
  176. package/policies/universal.md +60 -0
  177. package/scripts/analyze-report.sh +97 -0
  178. package/scripts/architecture-map.sh +145 -0
  179. package/scripts/auto-compound.sh +273 -0
  180. package/scripts/batch-audit.sh +42 -0
  181. package/scripts/batch-test.sh +101 -0
  182. package/scripts/entropy-audit.sh +221 -0
  183. package/scripts/failure-digest.sh +51 -0
  184. package/scripts/generate-ast-rules.sh +96 -0
  185. package/scripts/init.sh +112 -0
  186. package/scripts/lesson-check.sh +428 -0
  187. package/scripts/lib/common.sh +61 -0
  188. package/scripts/lib/cost-tracking.sh +153 -0
  189. package/scripts/lib/ollama.sh +60 -0
  190. package/scripts/lib/progress-writer.sh +128 -0
  191. package/scripts/lib/run-plan-context.sh +215 -0
  192. package/scripts/lib/run-plan-echo-back.sh +231 -0
  193. package/scripts/lib/run-plan-headless.sh +396 -0
  194. package/scripts/lib/run-plan-notify.sh +57 -0
  195. package/scripts/lib/run-plan-parser.sh +81 -0
  196. package/scripts/lib/run-plan-prompt.sh +215 -0
  197. package/scripts/lib/run-plan-quality-gate.sh +132 -0
  198. package/scripts/lib/run-plan-routing.sh +315 -0
  199. package/scripts/lib/run-plan-sampling.sh +170 -0
  200. package/scripts/lib/run-plan-scoring.sh +146 -0
  201. package/scripts/lib/run-plan-state.sh +142 -0
  202. package/scripts/lib/run-plan-team.sh +199 -0
  203. package/scripts/lib/telegram.sh +54 -0
  204. package/scripts/lib/thompson-sampling.sh +176 -0
  205. package/scripts/license-check.sh +74 -0
  206. package/scripts/mab-run.sh +575 -0
  207. package/scripts/module-size-check.sh +146 -0
  208. package/scripts/patterns/async-no-await.yml +5 -0
  209. package/scripts/patterns/bare-except.yml +6 -0
  210. package/scripts/patterns/empty-catch.yml +6 -0
  211. package/scripts/patterns/hardcoded-localhost.yml +9 -0
  212. package/scripts/patterns/retry-loop-no-backoff.yml +12 -0
  213. package/scripts/pipeline-status.sh +197 -0
  214. package/scripts/policy-check.sh +226 -0
  215. package/scripts/prior-art-search.sh +133 -0
  216. package/scripts/promote-mab-lessons.sh +126 -0
  217. package/scripts/prompts/agent-a-superpowers.md +29 -0
  218. package/scripts/prompts/agent-b-ralph.md +29 -0
  219. package/scripts/prompts/judge-agent.md +61 -0
  220. package/scripts/prompts/planner-agent.md +44 -0
  221. package/scripts/pull-community-lessons.sh +90 -0
  222. package/scripts/quality-gate.sh +266 -0
  223. package/scripts/research-gate.sh +90 -0
  224. package/scripts/run-plan.sh +329 -0
  225. package/scripts/scope-infer.sh +159 -0
  226. package/scripts/setup-ralph-loop.sh +155 -0
  227. package/scripts/telemetry.sh +230 -0
  228. package/scripts/tests/run-all-tests.sh +52 -0
  229. package/scripts/tests/test-act-cli.sh +46 -0
  230. package/scripts/tests/test-agents-md.sh +87 -0
  231. package/scripts/tests/test-analyze-report.sh +114 -0
  232. package/scripts/tests/test-architecture-map.sh +89 -0
  233. package/scripts/tests/test-auto-compound.sh +169 -0
  234. package/scripts/tests/test-batch-test.sh +65 -0
  235. package/scripts/tests/test-benchmark-runner.sh +25 -0
  236. package/scripts/tests/test-common.sh +168 -0
  237. package/scripts/tests/test-cost-tracking.sh +158 -0
  238. package/scripts/tests/test-echo-back.sh +180 -0
  239. package/scripts/tests/test-entropy-audit.sh +146 -0
  240. package/scripts/tests/test-failure-digest.sh +66 -0
  241. package/scripts/tests/test-generate-ast-rules.sh +145 -0
  242. package/scripts/tests/test-helpers.sh +82 -0
  243. package/scripts/tests/test-init.sh +47 -0
  244. package/scripts/tests/test-lesson-check.sh +278 -0
  245. package/scripts/tests/test-lesson-local.sh +55 -0
  246. package/scripts/tests/test-license-check.sh +109 -0
  247. package/scripts/tests/test-mab-run.sh +182 -0
  248. package/scripts/tests/test-ollama-lib.sh +49 -0
  249. package/scripts/tests/test-ollama.sh +60 -0
  250. package/scripts/tests/test-pipeline-status.sh +198 -0
  251. package/scripts/tests/test-policy-check.sh +124 -0
  252. package/scripts/tests/test-prior-art-search.sh +96 -0
  253. package/scripts/tests/test-progress-writer.sh +140 -0
  254. package/scripts/tests/test-promote-mab-lessons.sh +110 -0
  255. package/scripts/tests/test-pull-community-lessons.sh +149 -0
  256. package/scripts/tests/test-quality-gate.sh +241 -0
  257. package/scripts/tests/test-research-gate.sh +132 -0
  258. package/scripts/tests/test-run-plan-cli.sh +86 -0
  259. package/scripts/tests/test-run-plan-context.sh +305 -0
  260. package/scripts/tests/test-run-plan-e2e.sh +153 -0
  261. package/scripts/tests/test-run-plan-headless.sh +424 -0
  262. package/scripts/tests/test-run-plan-notify.sh +124 -0
  263. package/scripts/tests/test-run-plan-parser.sh +217 -0
  264. package/scripts/tests/test-run-plan-prompt.sh +254 -0
  265. package/scripts/tests/test-run-plan-quality-gate.sh +222 -0
  266. package/scripts/tests/test-run-plan-routing.sh +178 -0
  267. package/scripts/tests/test-run-plan-scoring.sh +148 -0
  268. package/scripts/tests/test-run-plan-state.sh +261 -0
  269. package/scripts/tests/test-run-plan-team.sh +157 -0
  270. package/scripts/tests/test-scope-infer.sh +150 -0
  271. package/scripts/tests/test-setup-ralph-loop.sh +63 -0
  272. package/scripts/tests/test-telegram-env.sh +38 -0
  273. package/scripts/tests/test-telegram.sh +121 -0
  274. package/scripts/tests/test-telemetry.sh +46 -0
  275. package/scripts/tests/test-thompson-sampling.sh +139 -0
  276. package/scripts/tests/test-validate-all.sh +60 -0
  277. package/scripts/tests/test-validate-commands.sh +89 -0
  278. package/scripts/tests/test-validate-hooks.sh +98 -0
  279. package/scripts/tests/test-validate-lessons.sh +150 -0
  280. package/scripts/tests/test-validate-plan-quality.sh +235 -0
  281. package/scripts/tests/test-validate-plans.sh +187 -0
  282. package/scripts/tests/test-validate-plugin.sh +106 -0
  283. package/scripts/tests/test-validate-prd.sh +184 -0
  284. package/scripts/tests/test-validate-skills.sh +134 -0
  285. package/scripts/validate-all.sh +57 -0
  286. package/scripts/validate-commands.sh +67 -0
  287. package/scripts/validate-hooks.sh +89 -0
  288. package/scripts/validate-lessons.sh +98 -0
  289. package/scripts/validate-plan-quality.sh +369 -0
  290. package/scripts/validate-plans.sh +120 -0
  291. package/scripts/validate-plugin.sh +86 -0
  292. package/scripts/validate-policies.sh +42 -0
  293. package/scripts/validate-prd.sh +118 -0
  294. package/scripts/validate-skills.sh +96 -0
  295. package/skills/autocode/SKILL.md +285 -0
  296. package/skills/autocode/ab-verification.md +51 -0
  297. package/skills/autocode/code-quality-standards.md +37 -0
  298. package/skills/autocode/competitive-mode.md +364 -0
  299. package/skills/brainstorming/SKILL.md +97 -0
  300. package/skills/capture-lesson/SKILL.md +187 -0
  301. package/skills/check-lessons/SKILL.md +116 -0
  302. package/skills/dispatching-parallel-agents/SKILL.md +110 -0
  303. package/skills/executing-plans/SKILL.md +85 -0
  304. package/skills/finishing-a-development-branch/SKILL.md +201 -0
  305. package/skills/receiving-code-review/SKILL.md +72 -0
  306. package/skills/requesting-code-review/SKILL.md +59 -0
  307. package/skills/requesting-code-review/code-reviewer.md +82 -0
  308. package/skills/research/SKILL.md +145 -0
  309. package/skills/roadmap/SKILL.md +115 -0
  310. package/skills/subagent-driven-development/SKILL.md +98 -0
  311. package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +18 -0
  312. package/skills/subagent-driven-development/implementer-prompt.md +73 -0
  313. package/skills/subagent-driven-development/spec-reviewer-prompt.md +57 -0
  314. package/skills/systematic-debugging/SKILL.md +134 -0
  315. package/skills/systematic-debugging/condition-based-waiting.md +64 -0
  316. package/skills/systematic-debugging/defense-in-depth.md +32 -0
  317. package/skills/systematic-debugging/root-cause-tracing.md +55 -0
  318. package/skills/test-driven-development/SKILL.md +167 -0
  319. package/skills/using-git-worktrees/SKILL.md +219 -0
  320. package/skills/using-superpowers/SKILL.md +54 -0
  321. package/skills/verification-before-completion/SKILL.md +140 -0
  322. package/skills/verify/SKILL.md +82 -0
  323. package/skills/writing-plans/SKILL.md +128 -0
  324. package/skills/writing-skills/SKILL.md +93 -0
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env bash
2
+ # run-plan-state.sh — Track batch progress in a JSON state file
3
+ #
4
+ # State file: <worktree>/.run-plan-state.json
5
+ # Requires: jq
6
+ #
7
+ # Functions:
8
+ # init_state <worktree> <plan_file> <mode> -> create state file
9
+ # read_state_field <worktree> <field> -> read top-level field
10
+ # complete_batch <worktree> <batch_num> <test_count> [duration] -> mark batch done
11
+ # get_previous_test_count <worktree> -> last completed batch's test count (0 if none, -1 if missing)
12
+ # set_quality_gate <worktree> <batch_num> <passed> <test_count> -> record quality gate result
13
+
14
+ _state_file() {
15
+ echo "$1/.run-plan-state.json"
16
+ }
17
+
18
+ init_state() {
19
+ local worktree="$1" plan_file="$2" mode="$3"
20
+ local sf
21
+ sf=$(_state_file "$worktree")
22
+ local now
23
+ now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
24
+
25
+ jq -n \
26
+ --arg plan_file "$plan_file" \
27
+ --arg mode "$mode" \
28
+ --arg started_at "$now" \
29
+ '{
30
+ plan_file: $plan_file,
31
+ mode: $mode,
32
+ current_batch: 1,
33
+ completed_batches: [],
34
+ test_counts: {},
35
+ durations: {},
36
+ costs: {},
37
+ total_cost_usd: 0,
38
+ started_at: $started_at,
39
+ last_quality_gate: null
40
+ }' > "$sf"
41
+ }
42
+
43
+ read_state_field() {
44
+ local worktree="$1" field="$2"
45
+ local sf
46
+ sf=$(_state_file "$worktree")
47
+ jq -c -r --arg f "$field" '.[$f]' "$sf"
48
+ }
49
+
50
+ complete_batch() {
51
+ local worktree="$1" batch_num="$2" test_count="$3" duration="${4:-0}"
52
+ local sf tmp
53
+ sf=$(_state_file "$worktree")
54
+ tmp=$(mktemp)
55
+
56
+ # batch_num may be non-numeric (e.g. 'final'), so use --arg and convert in jq.
57
+ # current_batch is only meaningful for numeric batch sequences — it tracks
58
+ # the next expected numeric batch. Non-numeric batches (like 'final') are
59
+ # recorded in completed_batches and test_counts but do not advance current_batch.
60
+ if [[ "$batch_num" =~ ^[0-9]+$ ]]; then
61
+ jq \
62
+ --argjson batch "$batch_num" \
63
+ --argjson tc "$test_count" \
64
+ --argjson dur "$duration" \
65
+ '
66
+ .completed_batches += [$batch] |
67
+ .current_batch = ($batch + 1) |
68
+ .test_counts[($batch | tostring)] = $tc |
69
+ .durations[($batch | tostring)] = $dur
70
+ ' "$sf" > "$tmp" && mv "$tmp" "$sf"
71
+ else
72
+ jq \
73
+ --arg batch "$batch_num" \
74
+ --argjson tc "$test_count" \
75
+ --argjson dur "$duration" \
76
+ '
77
+ .completed_batches += [$batch] |
78
+ .test_counts[$batch] = $tc |
79
+ .durations[$batch] = $dur
80
+ ' "$sf" > "$tmp" && mv "$tmp" "$sf"
81
+ fi
82
+ }
83
+
84
+ get_previous_test_count() {
85
+ local worktree="$1"
86
+ local sf
87
+ sf=$(_state_file "$worktree")
88
+
89
+ jq -r '
90
+ if (.completed_batches | length) == 0 then "0"
91
+ else (.test_counts[(.completed_batches | last | tostring)] // -1) | tostring
92
+ end
93
+ ' "$sf"
94
+ }
95
+
96
+ set_quality_gate() {
97
+ local worktree="$1" batch_num="$2" passed="$3" test_count="$4"
98
+ local sf tmp now
99
+ sf=$(_state_file "$worktree")
100
+ tmp=$(mktemp)
101
+ now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
102
+
103
+ # Normalize passed to a JSON boolean so --argjson never receives an
104
+ # unexpected value (#8). Callers pass "true"/"false" conventionally, but
105
+ # "1"/"0" or "yes"/"no" would crash jq with --argjson.
106
+ if [[ "$passed" == "true" || "$passed" == "1" || "$passed" == "yes" ]]; then
107
+ passed="true"
108
+ else
109
+ passed="false"
110
+ fi
111
+
112
+ # batch_num may be non-numeric (e.g. 'final'), so use --arg and convert in jq
113
+ if [[ "$batch_num" =~ ^[0-9]+$ ]]; then
114
+ jq \
115
+ --argjson batch "$batch_num" \
116
+ --argjson passed "$passed" \
117
+ --argjson tc "$test_count" \
118
+ --arg ts "$now" \
119
+ '
120
+ .last_quality_gate = {
121
+ batch: $batch,
122
+ passed: $passed,
123
+ test_count: $tc,
124
+ timestamp: $ts
125
+ }
126
+ ' "$sf" > "$tmp" && mv "$tmp" "$sf"
127
+ else
128
+ jq \
129
+ --arg batch "$batch_num" \
130
+ --argjson passed "$passed" \
131
+ --argjson tc "$test_count" \
132
+ --arg ts "$now" \
133
+ '
134
+ .last_quality_gate = {
135
+ batch: $batch,
136
+ passed: $passed,
137
+ test_count: $tc,
138
+ timestamp: $ts
139
+ }
140
+ ' "$sf" > "$tmp" && mv "$tmp" "$sf"
141
+ fi
142
+ }
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env bash
2
+ # run-plan-team.sh — Team mode execution with parallel batch groups
3
+ #
4
+ # Analyzes plan dependency graph and executes batches in parallel groups.
5
+ # Each group's batches have all dependencies satisfied by prior groups.
6
+ # Headless version uses parallel `claude -p` processes.
7
+ #
8
+ # Requires sourced: run-plan-parser.sh, run-plan-routing.sh, run-plan-prompt.sh,
9
+ # run-plan-quality-gate.sh, run-plan-state.sh, run-plan-context.sh, run-plan-scoring.sh
10
+ #
11
+ # Functions:
12
+ # compute_parallel_groups <dep_graph_json> <start_batch> <end_batch> -> JSON [[1],[2,3],[4]]
13
+ # run_mode_team (uses globals: PLAN_FILE, WORKTREE, etc.)
14
+
15
+ # --- Compute parallel execution groups from dependency graph ---
16
+ # Takes a JSON dependency graph and batch range, returns JSON array of arrays.
17
+ # Each inner array is a group of batches that can run in parallel.
18
+ compute_parallel_groups() {
19
+ local dep_graph="$1" start_batch="$2" end_batch="$3"
20
+
21
+ # Collect all batch numbers in range
22
+ local batches=()
23
+ for ((b = start_batch; b <= end_batch; b++)); do
24
+ batches+=("$b")
25
+ done
26
+
27
+ local completed=""
28
+ local groups="["
29
+ local first_group=true
30
+ local remaining=${#batches[@]}
31
+
32
+ while [[ "$remaining" -gt 0 ]]; do
33
+ local group="["
34
+ local first_in_group=true
35
+ local new_completed=""
36
+ local group_size=0
37
+
38
+ for b in "${batches[@]}"; do
39
+ # Skip already completed
40
+ [[ "$completed" == *"|$b|"* ]] && continue
41
+
42
+ # Check if all deps are satisfied (completed or outside range)
43
+ local deps
44
+ deps=$(echo "$dep_graph" | jq -r ".\"$b\"[]" 2>/dev/null || true)
45
+ local all_met=true
46
+ while IFS= read -r dep; do
47
+ [[ -z "$dep" ]] && continue
48
+ local dep_num=$((dep + 0))
49
+ # Deps outside our batch range are treated as satisfied
50
+ if [[ "$dep_num" -ge "$start_batch" && "$dep_num" -le "$end_batch" ]]; then
51
+ if [[ "$completed" != *"|$dep|"* ]]; then
52
+ all_met=false
53
+ break
54
+ fi
55
+ fi
56
+ done <<< "$deps"
57
+
58
+ if [[ "$all_met" == true ]]; then
59
+ [[ "$first_in_group" != true ]] && group+=","
60
+ group+="$b"
61
+ first_in_group=false
62
+ new_completed+="|$b|"
63
+ group_size=$((group_size + 1))
64
+ remaining=$((remaining - 1))
65
+ fi
66
+ done
67
+
68
+ group+="]"
69
+ completed+="$new_completed"
70
+
71
+ if [[ "$group_size" -gt 0 ]]; then
72
+ [[ "$first_group" != true ]] && groups+=","
73
+ groups+="$group"
74
+ first_group=false
75
+ else
76
+ break # No progress — circular dependency or error
77
+ fi
78
+ done
79
+
80
+ groups+="]"
81
+ echo "$groups"
82
+ }
83
+
84
+ # --- Team mode execution (headless parallel) ---
85
+ run_mode_team() {
86
+ # WARNING: Team mode uses a shared worktree. Concurrent git operations
87
+ # (add, commit, stash) from parallel claude processes may conflict and
88
+ # corrupt the staging area. Each batch in a group runs against the same
89
+ # filesystem; the batches within a group are independent by design
90
+ # (no shared files per the dependency graph), but git state is global.
91
+ # For full isolation, run batches sequentially (headless mode) or ensure
92
+ # batches in each parallel group touch strictly non-overlapping files.
93
+ echo "WARNING: Team mode uses a shared worktree. Concurrent git operations may conflict." >&2
94
+
95
+ local dep_graph
96
+ dep_graph=$(build_dependency_graph "$PLAN_FILE")
97
+
98
+ local groups
99
+ groups=$(compute_parallel_groups "$dep_graph" "$START_BATCH" "$END_BATCH")
100
+ local group_count
101
+ group_count=$(echo "$groups" | jq 'length')
102
+
103
+ mkdir -p "$WORKTREE/logs"
104
+
105
+ # Initialize state if not resuming
106
+ if [[ "$RESUME" != true ]]; then
107
+ init_state "$WORKTREE" "$PLAN_FILE" "$MODE"
108
+ if [[ "$START_BATCH" -gt 1 ]]; then
109
+ for ((b = 1; b < START_BATCH; b++)); do
110
+ complete_batch "$WORKTREE" "$b" 0
111
+ done
112
+ fi
113
+ fi
114
+
115
+ log_routing_decision "$WORKTREE" "MODE" "team mode selected ($group_count groups)"
116
+
117
+ for ((g = 0; g < group_count; g++)); do
118
+ local group_batches
119
+ group_batches=$(echo "$groups" | jq -r ".[$g][]")
120
+ local batch_count
121
+ batch_count=$(echo "$group_batches" | wc -l)
122
+
123
+ echo ""
124
+ echo "================================================================"
125
+ echo " Group $((g+1))/$group_count: batches $(echo "$group_batches" | tr '\n' ',' | sed 's/,$//')"
126
+ echo " ($batch_count batches in parallel)"
127
+ echo "================================================================"
128
+
129
+ local pids=()
130
+ local batch_logs=()
131
+ local batch_list=()
132
+
133
+ for batch in $group_batches; do
134
+ local title
135
+ title=$(get_batch_title "$PLAN_FILE" "$batch")
136
+ local model
137
+ model=$(classify_batch_model "$PLAN_FILE" "$batch")
138
+ local log_file="$WORKTREE/logs/batch-${batch}-team.log"
139
+ batch_logs+=("$log_file")
140
+ batch_list+=("$batch")
141
+
142
+ local prev_test_count
143
+ prev_test_count=$(get_previous_test_count "$WORKTREE")
144
+
145
+ local prompt
146
+ prompt=$(build_batch_prompt "$PLAN_FILE" "$batch" "$WORKTREE" "$PYTHON" "$QUALITY_GATE_CMD" "$prev_test_count")
147
+
148
+ log_routing_decision "$WORKTREE" "PARALLEL" "batch $batch ($title) [$model] in group $((g+1))"
149
+
150
+ echo " Starting batch $batch: $title ($model)..."
151
+ CLAUDECODE='' claude -p "$prompt" \
152
+ --model "$model" \
153
+ --allowedTools "Bash,Read,Write,Edit,Grep,Glob" \
154
+ --permission-mode bypassPermissions \
155
+ > "$log_file" 2>&1 &
156
+ pids+=($!)
157
+ done
158
+
159
+ # Wait for all batches in group
160
+ local all_passed=true
161
+ for i in "${!pids[@]}"; do
162
+ local pid=${pids[$i]}
163
+ local batch=${batch_list[$i]}
164
+ wait "$pid" || true
165
+
166
+ # Run quality gate
167
+ local gate_exit=0
168
+ run_quality_gate "$WORKTREE" "$QUALITY_GATE_CMD" "$batch" "0" || gate_exit=$?
169
+ if [[ $gate_exit -eq 0 ]]; then
170
+ echo " Batch $batch PASSED"
171
+ log_routing_decision "$WORKTREE" "GATE_PASS" "batch $batch passed quality gate"
172
+ else
173
+ echo " Batch $batch FAILED quality gate"
174
+ log_routing_decision "$WORKTREE" "GATE_FAIL" "batch $batch failed quality gate"
175
+ all_passed=false
176
+ fi
177
+ done
178
+
179
+ if [[ "$all_passed" != true ]]; then
180
+ echo ""
181
+ echo "Group $((g+1)) had failures. Stopping."
182
+ exit 1
183
+ fi
184
+ done
185
+
186
+ echo ""
187
+ echo "================================================================"
188
+ echo " All groups complete ($group_count groups, batches $START_BATCH → $END_BATCH)"
189
+ echo "================================================================"
190
+
191
+ if [[ "$VERIFY" == true ]]; then
192
+ echo ""
193
+ echo "Running final verification..."
194
+ run_quality_gate "$WORKTREE" "$QUALITY_GATE_CMD" "final" || {
195
+ echo "FINAL VERIFICATION FAILED"
196
+ exit 1
197
+ }
198
+ fi
199
+ }
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bash
2
+ # telegram.sh — Shared Telegram notification helpers
3
+ #
4
+ # Functions:
5
+ # _load_telegram_env [env_file] -> load TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID
6
+ # _send_telegram <message> -> send via Telegram Bot API
7
+
8
+ _load_telegram_env() {
9
+ local env_file="${1:-${ACT_ENV_FILE:-$HOME/.env}}"
10
+
11
+ if [[ ! -f "$env_file" ]]; then
12
+ echo "WARNING: env file not found: $env_file" >&2
13
+ return 1
14
+ fi
15
+
16
+ # Extract values and strip surrounding single or double quotes (#7).
17
+ # .env files may store values as TELEGRAM_BOT_TOKEN="abc123" or
18
+ # TELEGRAM_BOT_TOKEN='abc123' — the cut captures the raw quoted string.
19
+ TELEGRAM_BOT_TOKEN=$(grep -E '^(export )?TELEGRAM_BOT_TOKEN=' "$env_file" | head -1 | sed 's/^export //' | cut -d= -f2- | sed "s/^['\"]//; s/['\"]$//")
20
+ TELEGRAM_CHAT_ID=$(grep -E '^(export )?TELEGRAM_CHAT_ID=' "$env_file" | head -1 | sed 's/^export //' | cut -d= -f2- | sed "s/^['\"]//; s/['\"]$//")
21
+
22
+ if [[ -z "${TELEGRAM_BOT_TOKEN:-}" || -z "${TELEGRAM_CHAT_ID:-}" ]]; then
23
+ echo "WARNING: TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID not found in $env_file" >&2
24
+ return 1
25
+ fi
26
+
27
+ export TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID
28
+ }
29
+
30
+ _send_telegram() {
31
+ local message="$1"
32
+
33
+ if [[ -z "${TELEGRAM_BOT_TOKEN:-}" || -z "${TELEGRAM_CHAT_ID:-}" ]]; then
34
+ echo "WARNING: Telegram credentials not set — skipping notification" >&2
35
+ return 0
36
+ fi
37
+
38
+ local url="https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage"
39
+
40
+ local http_code
41
+ http_code=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$url" \
42
+ -d chat_id="$TELEGRAM_CHAT_ID" \
43
+ -d text="$message" \
44
+ -d parse_mode="Markdown" \
45
+ --max-time 10 2>/dev/null) || {
46
+ echo "WARNING: Failed to send Telegram notification (curl error)" >&2
47
+ return 1
48
+ }
49
+
50
+ if [[ "$http_code" != "200" ]]; then
51
+ echo "WARNING: Telegram API returned HTTP $http_code" >&2
52
+ return 1
53
+ fi
54
+ }
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env bash
2
+ # thompson-sampling.sh — Thompson Sampling for MAB strategy routing
3
+ #
4
+ # Functions:
5
+ # thompson_sample <wins> <losses> — Beta approximation, returns float [0,1]
6
+ # thompson_route <batch_type> <perf_file> — Returns "superpowers"|"ralph"|"mab"
7
+ # init_strategy_perf <file> — Creates JSON with 4 batch types + calibration
8
+ # update_strategy_perf <file> <batch_type> <winner_strategy> — Increments counters
9
+ #
10
+ # Routing logic:
11
+ # - Missing file or < 5 points per strategy → "mab" (compete)
12
+ # - integration batch type → always "mab" (most variable)
13
+ # - Win rate spread < 15% → "mab" (too close to call)
14
+ # - Clear winner (≥70%, 10+ points) → return strategy name
15
+ # - Otherwise → Thompson sample both, return higher sample
16
+
17
+ # Thompson sample from Beta(alpha, beta) using Box-Muller approximation.
18
+ # For Beta(a,b) with a,b > 1, a good approximation is:
19
+ # mean = a/(a+b), variance = ab/((a+b)^2*(a+b+1))
20
+ # sample ≈ mean + sqrt(variance) * normal_noise
21
+ # Clamped to [0.01, 0.99] to avoid degenerate values.
22
+ #
23
+ # Args: <wins> <losses>
24
+ # Output: float in [0,1]
25
+ thompson_sample() {
26
+ local wins="${1:-0}" losses="${2:-0}"
27
+ local alpha=$((wins + 1))
28
+ local beta=$((losses + 1))
29
+
30
+ # Use central limit theorem for a pseudo-normal: sum 12 uniform [0,1), subtract 6 → approx N(0,1).
31
+ # Seed with bash $RANDOM + PID — PROCINFO["pid"] is gawk-only, mawk silently ignores it,
32
+ # producing identical samples within the same second (kills Thompson Sampling).
33
+ local noise
34
+ noise=$(awk -v seed="$((RANDOM + $$))" 'BEGIN {
35
+ srand(systime() * 1000 + seed);
36
+ s = 0;
37
+ for (i = 0; i < 12; i++) s += rand();
38
+ printf "%.6f", s - 6;
39
+ }' 2>/dev/null || echo "0")
40
+
41
+ # Compute Beta sample approximation
42
+ bc -l <<EOF
43
+ scale=6
44
+ a = $alpha
45
+ b = $beta
46
+ mean = a / (a + b)
47
+ var = (a * b) / ((a + b) * (a + b) * (a + b + 1))
48
+ sd = sqrt(var)
49
+ sample = mean + sd * $noise
50
+ if (sample < 0.01) sample = 0.01
51
+ if (sample > 0.99) sample = 0.99
52
+ sample
53
+ EOF
54
+ }
55
+
56
+ # Route a batch type to a strategy or "mab" (compete).
57
+ #
58
+ # Args: <batch_type> <perf_file>
59
+ # Output: "superpowers" | "ralph" | "mab"
60
+ thompson_route() {
61
+ local batch_type="$1"
62
+ local perf_file="$2"
63
+
64
+ # Missing file → compete
65
+ if [[ ! -f "$perf_file" ]]; then
66
+ echo "mab"
67
+ return
68
+ fi
69
+
70
+ # Integration → always compete (most variable)
71
+ if [[ "$batch_type" == "integration" ]]; then
72
+ echo "mab"
73
+ return
74
+ fi
75
+
76
+ # Read strategy data
77
+ local sp_wins sp_losses ralph_wins ralph_losses
78
+ sp_wins=$(jq -r --arg bt "$batch_type" '.[$bt].superpowers.wins // 0' "$perf_file" 2>/dev/null || echo "0")
79
+ sp_losses=$(jq -r --arg bt "$batch_type" '.[$bt].superpowers.losses // 0' "$perf_file" 2>/dev/null || echo "0")
80
+ ralph_wins=$(jq -r --arg bt "$batch_type" '.[$bt].ralph.wins // 0' "$perf_file" 2>/dev/null || echo "0")
81
+ ralph_losses=$(jq -r --arg bt "$batch_type" '.[$bt].ralph.losses // 0' "$perf_file" 2>/dev/null || echo "0")
82
+
83
+ local sp_total=$((sp_wins + sp_losses))
84
+ local ralph_total=$((ralph_wins + ralph_losses))
85
+
86
+ # < 5 data points per strategy → compete
87
+ if [[ "$sp_total" -lt 5 || "$ralph_total" -lt 5 ]]; then
88
+ echo "mab"
89
+ return
90
+ fi
91
+
92
+ # Compute win rates
93
+ local sp_rate ralph_rate spread
94
+ sp_rate=$(echo "scale=2; $sp_wins * 100 / $sp_total" | bc -l)
95
+ ralph_rate=$(echo "scale=2; $ralph_wins * 100 / $ralph_total" | bc -l)
96
+
97
+ # Spread check (< 15% → too close)
98
+ spread=$(echo "scale=2; x=$sp_rate - $ralph_rate; if (x < 0) -x else x" | bc -l)
99
+ local spread_too_close
100
+ spread_too_close=$(echo "$spread < 15" | bc -l)
101
+ if [[ "$spread_too_close" == "1" ]]; then
102
+ echo "mab"
103
+ return
104
+ fi
105
+
106
+ # Clear winner check (≥70%, 10+ points)
107
+ local sp_clear ralph_clear
108
+ sp_clear=$(echo "$sp_rate >= 70" | bc -l)
109
+ ralph_clear=$(echo "$ralph_rate >= 70" | bc -l)
110
+
111
+ if [[ "$sp_clear" == "1" && "$sp_total" -ge 10 ]]; then
112
+ echo "superpowers"
113
+ return
114
+ fi
115
+ if [[ "$ralph_clear" == "1" && "$ralph_total" -ge 10 ]]; then
116
+ echo "ralph"
117
+ return
118
+ fi
119
+
120
+ # Thompson sample both — higher sample wins
121
+ local sp_sample ralph_sample
122
+ sp_sample=$(thompson_sample "$sp_wins" "$sp_losses")
123
+ ralph_sample=$(thompson_sample "$ralph_wins" "$ralph_losses")
124
+
125
+ local sp_higher
126
+ sp_higher=$(echo "$sp_sample > $ralph_sample" | bc -l)
127
+ if [[ "$sp_higher" == "1" ]]; then
128
+ echo "superpowers"
129
+ else
130
+ echo "ralph"
131
+ fi
132
+ }
133
+
134
+ # Initialize strategy performance file with zero counters.
135
+ #
136
+ # Args: <file>
137
+ init_strategy_perf() {
138
+ local file="$1"
139
+ mkdir -p "$(dirname "$file")"
140
+ cat > "$file" <<'JSON'
141
+ {
142
+ "new-file": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
143
+ "refactoring": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
144
+ "integration": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
145
+ "test-only": {"superpowers": {"wins": 0, "losses": 0}, "ralph": {"wins": 0, "losses": 0}},
146
+ "calibration_count": 0,
147
+ "calibration_complete": false
148
+ }
149
+ JSON
150
+ }
151
+
152
+ # Update strategy performance after a MAB run.
153
+ # Winner gets +1 win, loser gets +1 loss.
154
+ #
155
+ # Args: <file> <batch_type> <winner_strategy>
156
+ update_strategy_perf() {
157
+ local file="$1" batch_type="$2" winner="$3"
158
+
159
+ if [[ ! -f "$file" ]]; then
160
+ init_strategy_perf "$file"
161
+ fi
162
+
163
+ local loser
164
+ if [[ "$winner" == "superpowers" ]]; then
165
+ loser="ralph"
166
+ else
167
+ loser="superpowers"
168
+ fi
169
+
170
+ local tmp
171
+ tmp=$(mktemp)
172
+ jq --arg bt "$batch_type" --arg w "$winner" --arg l "$loser" '
173
+ .[$bt][$w].wins += 1 |
174
+ .[$bt][$l].losses += 1
175
+ ' "$file" > "$tmp" && mv "$tmp" "$file"
176
+ }
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env bash
2
+ # license-check.sh — Check dependencies for license compatibility
3
+ #
4
+ # Usage: license-check.sh [--project-root <dir>]
5
+ # Flags GPL/AGPL in MIT-licensed projects.
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ source "$SCRIPT_DIR/lib/common.sh"
10
+
11
+ PROJECT_ROOT="."
12
+
13
+ while [[ $# -gt 0 ]]; do
14
+ case "$1" in
15
+ --project-root) PROJECT_ROOT="$2"; shift 2 ;;
16
+ -h|--help)
17
+ echo "license-check.sh — Check dependency licenses"
18
+ echo "Usage: license-check.sh [--project-root <dir>]"
19
+ echo "Flags GPL/AGPL in MIT-licensed projects."
20
+ exit 0 ;;
21
+ *) echo "Unknown: $1" >&2; exit 1 ;;
22
+ esac
23
+ done
24
+
25
+ cd "$PROJECT_ROOT"
26
+ project_type=$(detect_project_type ".")
27
+
28
+ echo "=== License Check ==="
29
+ violations=0
30
+
31
+ case "$project_type" in
32
+ python)
33
+ if [[ -d ".venv" ]] && command -v pip-licenses >/dev/null 2>&1; then
34
+ echo "Checking Python dependencies..."
35
+ gpl_deps=$(.venv/bin/python -m pip-licenses --format=csv 2>/dev/null | grep -iE 'GPL|AGPL' | grep -v 'LGPL' || true)
36
+ if [[ -n "$gpl_deps" ]]; then
37
+ echo "WARNING: GPL/AGPL dependencies found:"
38
+ echo "$gpl_deps" | sed 's/^/ /'
39
+ violations=$((violations + 1))
40
+ else
41
+ echo " No GPL/AGPL dependencies"
42
+ fi
43
+ else
44
+ echo " pip-licenses not available — skipping"
45
+ fi
46
+ ;;
47
+ node)
48
+ if command -v npx >/dev/null 2>&1; then
49
+ echo "Checking Node dependencies..."
50
+ gpl_deps=$(npx license-checker --csv 2>/dev/null | grep -iE 'GPL|AGPL' | grep -v 'LGPL' || true)
51
+ if [[ -n "$gpl_deps" ]]; then
52
+ echo "WARNING: GPL/AGPL dependencies found:"
53
+ echo "$gpl_deps" | head -10 | sed 's/^/ /'
54
+ violations=$((violations + 1))
55
+ else
56
+ echo " No GPL/AGPL dependencies"
57
+ fi
58
+ else
59
+ echo " license-checker not available — skipping"
60
+ fi
61
+ ;;
62
+ *)
63
+ echo " No license check for project type: $project_type"
64
+ ;;
65
+ esac
66
+
67
+ if [[ $violations -gt 0 ]]; then
68
+ echo ""
69
+ echo "license-check: $violations issue(s) found"
70
+ exit 1
71
+ fi
72
+
73
+ echo "license-check: CLEAN"
74
+ exit 0