@workerstack/repo 0.0.0-stage → 0.1.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 (438) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +21 -0
  3. package/README.md +0 -3
  4. package/agents/global-settings/antigravity.json +3 -0
  5. package/agents/global-settings/claude.json +6 -0
  6. package/agents/global-settings/codex.toml +2 -0
  7. package/agents/global-settings/cursor.json +3 -0
  8. package/agents/global-settings/grok.toml +36 -0
  9. package/agents/global-settings/manifest.json +36 -0
  10. package/agents/grok/hooks/orchestrate-parent.json +60 -0
  11. package/agents/grok/hooks/rtk.json +16 -0
  12. package/agents/grok/hooks/rulesync.json +3 -0
  13. package/agents/grok/roles/scout.toml +5 -0
  14. package/agents/hooks.jsonc +67 -0
  15. package/agents/mcp-policy.jsonc +16 -0
  16. package/agents/rules/overview.md +21 -0
  17. package/agents/settings/claude.json +5 -0
  18. package/agents/skills/ask/SKILL.md +13 -0
  19. package/agents/skills/auto/SKILL.md +13 -0
  20. package/agents/skills/away/SKILL.md +19 -0
  21. package/agents/skills/blast-radius/SKILL.md +54 -0
  22. package/agents/skills/bro/SKILL.md +9 -0
  23. package/agents/skills/browser/SKILL.md +16 -0
  24. package/agents/skills/clasp/SKILL.md +11 -0
  25. package/agents/skills/commit/SKILL.md +35 -0
  26. package/agents/skills/commit-and-push/SKILL.md +9 -0
  27. package/agents/skills/commit-push/SKILL.md +59 -0
  28. package/agents/skills/commit-push-deploy/SKILL.md +74 -0
  29. package/agents/skills/commit-push-release/SKILL.md +75 -0
  30. package/agents/skills/correct/SKILL.md +39 -0
  31. package/agents/skills/do/SKILL.md +21 -0
  32. package/agents/skills/explain/SKILL.md +13 -0
  33. package/agents/skills/google-workspace/SKILL.md +12 -0
  34. package/agents/skills/handoff/SKILL.md +81 -0
  35. package/agents/skills/implement-plan/SKILL.md +29 -0
  36. package/agents/skills/implement-roadmap/SKILL.md +27 -0
  37. package/agents/skills/jev/SKILL.md +17 -0
  38. package/agents/skills/listen/SKILL.md +15 -0
  39. package/agents/skills/pickup/SKILL.md +58 -0
  40. package/agents/skills/principle-attack-the-premise/SKILL.md +24 -0
  41. package/agents/skills/principle-boundary-discipline/SKILL.md +38 -0
  42. package/agents/skills/principle-build-the-lever/SKILL.md +28 -0
  43. package/agents/skills/principle-encode-lessons-in-structure/SKILL.md +15 -0
  44. package/agents/skills/principle-exhaust-the-design-space/SKILL.md +12 -0
  45. package/agents/skills/principle-experience-first/SKILL.md +22 -0
  46. package/agents/skills/principle-explain-the-number/SKILL.md +21 -0
  47. package/agents/skills/principle-fix-root-causes/SKILL.md +24 -0
  48. package/agents/skills/principle-foundational-thinking/SKILL.md +25 -0
  49. package/agents/skills/principle-guard-the-context-window/SKILL.md +16 -0
  50. package/agents/skills/principle-laziness-protocol/SKILL.md +13 -0
  51. package/agents/skills/principle-make-operations-idempotent/SKILL.md +25 -0
  52. package/agents/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +18 -0
  53. package/agents/skills/principle-minimize-reader-load/SKILL.md +22 -0
  54. package/agents/skills/principle-model-the-domain/SKILL.md +26 -0
  55. package/agents/skills/principle-never-block-on-the-human/SKILL.md +17 -0
  56. package/agents/skills/principle-outcome-oriented-execution/SKILL.md +25 -0
  57. package/agents/skills/principle-prove-it-works/SKILL.md +25 -0
  58. package/agents/skills/principle-redesign-from-first-principles/SKILL.md +18 -0
  59. package/agents/skills/principle-separate-before-serializing-shared-state/SKILL.md +20 -0
  60. package/agents/skills/principle-sequence-verifiable-units/SKILL.md +22 -0
  61. package/agents/skills/principle-subtract-before-you-add/SKILL.md +21 -0
  62. package/agents/skills/principle-test-behavior-not-implementation/SKILL.md +24 -0
  63. package/agents/skills/principle-type-system-discipline/SKILL.md +17 -0
  64. package/agents/skills/review-plan/SKILL.md +28 -0
  65. package/agents/skills/review-roadmap/SKILL.md +28 -0
  66. package/agents/skills/show-me/SKILL.md +21 -0
  67. package/agents/skills/speak/SKILL.md +15 -0
  68. package/agents/skills/su/SKILL.md +11 -0
  69. package/agents/skills/teach/SKILL.md +25 -0
  70. package/agents/skills/tmp/SKILL.md +54 -0
  71. package/agents/skills/typescript-best-practices/SKILL.md +32 -0
  72. package/agents/skills/typescript-best-practices/references/patterns.md +328 -0
  73. package/agents/skills/unslop/SKILL.md +62 -0
  74. package/agents/skills/write-plan/SKILL.md +33 -0
  75. package/agents/skills/write-roadmap/SKILL.md +66 -0
  76. package/agents/subagents/implementer-deep.md +57 -0
  77. package/agents/subagents/implementer-fast.md +57 -0
  78. package/agents/subagents/orchestrator.md +43 -0
  79. package/agents/subagents/researcher.md +47 -0
  80. package/agents/subagents/scout.md +44 -0
  81. package/bin/repo.ts +4 -0
  82. package/cli/actionlint-default.test.ts +38 -0
  83. package/cli/actionlint-download.test.ts +162 -0
  84. package/cli/actionlint-real.test.ts +33 -0
  85. package/cli/actionlint.constants.ts +43 -0
  86. package/cli/actionlint.test.ts +108 -0
  87. package/cli/actionlint.ts +178 -0
  88. package/cli/bin.test.ts +54 -0
  89. package/cli/bin.ts +40 -0
  90. package/cli/check-changelog.test.ts +40 -0
  91. package/cli/check-changelog.ts +12 -0
  92. package/cli/check-comments.test.ts +157 -0
  93. package/cli/check-comments.ts +123 -0
  94. package/cli/check-env-modules.test.ts +85 -0
  95. package/cli/check-env-modules.ts +56 -0
  96. package/cli/check-magic.test.ts +139 -0
  97. package/cli/check-magic.ts +120 -0
  98. package/cli/check-manifests.constants.ts +4 -0
  99. package/cli/check-manifests.test.ts +210 -0
  100. package/cli/check-manifests.ts +71 -0
  101. package/cli/check-nightly.test.ts +103 -0
  102. package/cli/check-nightly.ts +37 -0
  103. package/cli/check-ratchets-missing.test.ts +46 -0
  104. package/cli/check-ratchets-mutation.test.ts +49 -0
  105. package/cli/check-ratchets.constants.ts +20 -0
  106. package/cli/check-ratchets.test.ts +190 -0
  107. package/cli/check-ratchets.ts +193 -0
  108. package/cli/check-rejections.test.ts +220 -0
  109. package/cli/check-rejections.ts +153 -0
  110. package/cli/check-reports.ts +37 -0
  111. package/cli/check-tsconfig.test.ts +507 -0
  112. package/cli/check-tsconfig.ts +205 -0
  113. package/cli/commands.test.ts +114 -0
  114. package/cli/commands.ts +218 -0
  115. package/cli/commit-changelog.ts +27 -0
  116. package/cli/commit-msg-changelog.test.ts +93 -0
  117. package/cli/commit-msg-range.test.ts +126 -0
  118. package/cli/commit-msg-red-proof.test.ts +147 -0
  119. package/cli/commit-msg-repo.test.ts +153 -0
  120. package/cli/commit-msg.test.ts +101 -0
  121. package/cli/commit-msg.ts +194 -0
  122. package/cli/commit-range.constants.ts +17 -0
  123. package/cli/commit-range.ts +57 -0
  124. package/cli/config-knip.test.ts +70 -0
  125. package/cli/config-nightly.test.ts +84 -0
  126. package/cli/config-tdd.test.ts +46 -0
  127. package/cli/config.test.ts +576 -0
  128. package/cli/config.ts +261 -0
  129. package/cli/conventional.ts +9 -0
  130. package/cli/coverage-config.ts +62 -0
  131. package/cli/coverage.constants.ts +6 -0
  132. package/cli/coverage.test.ts +199 -0
  133. package/cli/coverage.ts +127 -0
  134. package/cli/env.constants.ts +10 -0
  135. package/cli/env.test.ts +56 -0
  136. package/cli/env.ts +72 -0
  137. package/cli/fixtures/base-config.ts +13 -0
  138. package/cli/fixtures/gate-world.ts +48 -0
  139. package/cli/fixtures/knip-run.ts +38 -0
  140. package/cli/fixtures/mutate-fakes.ts +137 -0
  141. package/cli/fixtures/planted-repo.ts +50 -0
  142. package/cli/fixtures/publish-world.ts +71 -0
  143. package/cli/fixtures/red-proof-repo.ts +72 -0
  144. package/cli/fixtures/release-context.ts +15 -0
  145. package/cli/fixtures/release-world.ts +111 -0
  146. package/cli/fixtures/stub-bin.ts +40 -0
  147. package/cli/guard-diagnostics.test.ts +157 -0
  148. package/cli/guard-diagnostics.ts +195 -0
  149. package/cli/init.test.ts +487 -0
  150. package/cli/init.ts +275 -0
  151. package/cli/knip-check-ignores.test.ts +106 -0
  152. package/cli/knip-check-real.test.ts +65 -0
  153. package/cli/knip-check.constants.ts +46 -0
  154. package/cli/knip-check.test.ts +114 -0
  155. package/cli/knip-check.ts +169 -0
  156. package/cli/knip-schema.ts +67 -0
  157. package/cli/known-failures.constants.ts +22 -0
  158. package/cli/known-failures.test.ts +138 -0
  159. package/cli/known-failures.ts +131 -0
  160. package/cli/lint/as-needs-reason.ts +41 -0
  161. package/cli/lint/boundaries.ts +73 -0
  162. package/cli/lint/class-file.ts +39 -0
  163. package/cli/lint/config-file.ts +32 -0
  164. package/cli/lint/entry-file.ts +54 -0
  165. package/cli/lint/env-in-config.ts +66 -0
  166. package/cli/lint/filename-matches-export.ts +63 -0
  167. package/cli/lint/index-file.ts +22 -0
  168. package/cli/lint/index.ts +47 -0
  169. package/cli/lint/is-reason.ts +6 -0
  170. package/cli/lint/manifest.ts +46 -0
  171. package/cli/lint/names.ts +59 -0
  172. package/cli/lint/no-commented-out-code.ts +105 -0
  173. package/cli/lint/no-mock-of-subject.ts +64 -0
  174. package/cli/lint/no-module-side-effects.ts +94 -0
  175. package/cli/lint/no-module-state.ts +58 -0
  176. package/cli/lint/no-side-effect-import.ts +17 -0
  177. package/cli/lint/no-v8-ignore.ts +18 -0
  178. package/cli/lint/script-rules.ts +9 -0
  179. package/cli/lint/skip-on-platform.constants.ts +8 -0
  180. package/cli/lint/skip-on-platform.ts +118 -0
  181. package/cli/lint/todo-needs-id.ts +63 -0
  182. package/cli/lint/types-file.ts +28 -0
  183. package/cli/lint-schema.ts +32 -0
  184. package/cli/magic/fixtures/magic-repo.ts +64 -0
  185. package/cli/magic/magic-hit.ts +2 -0
  186. package/cli/magic/magic-nodes.ts +44 -0
  187. package/cli/magic/magic-numbers.test.ts +99 -0
  188. package/cli/magic/magic-numbers.ts +117 -0
  189. package/cli/magic/magic-strings.test.ts +38 -0
  190. package/cli/magic/magic-strings.ts +81 -0
  191. package/cli/main.test.ts +853 -0
  192. package/cli/main.ts +292 -0
  193. package/cli/mutate-args.ts +79 -0
  194. package/cli/mutate-check.test.ts +147 -0
  195. package/cli/mutate-deps.ts +42 -0
  196. package/cli/mutate-edges.test.ts +150 -0
  197. package/cli/mutate-files.test.ts +100 -0
  198. package/cli/mutate-files.ts +31 -0
  199. package/cli/mutate-floors.test.ts +91 -0
  200. package/cli/mutate-floors.ts +104 -0
  201. package/cli/mutate-real-deps.ts +57 -0
  202. package/cli/mutate-real.test.ts +96 -0
  203. package/cli/mutate-report.ts +96 -0
  204. package/cli/mutate-run.ts +128 -0
  205. package/cli/mutate-shards.ts +64 -0
  206. package/cli/mutate.constants.ts +67 -0
  207. package/cli/mutate.test.ts +182 -0
  208. package/cli/mutate.ts +64 -0
  209. package/cli/nested-vitest-flags.ts +8 -0
  210. package/cli/nightly-config.constants.ts +14 -0
  211. package/cli/nightly-config.ts +62 -0
  212. package/cli/prompt.test.ts +202 -0
  213. package/cli/prompt.ts +1 -0
  214. package/cli/red-proof-edges.test.ts +78 -0
  215. package/cli/red-proof-failures.test.ts +82 -0
  216. package/cli/red-proof-tree.test.ts +157 -0
  217. package/cli/red-proof-verdict.test.ts +44 -0
  218. package/cli/red-proof.constants.ts +14 -0
  219. package/cli/red-proof.ts +202 -0
  220. package/cli/release/changelog-parse.ts +83 -0
  221. package/cli/release/changelog.test.ts +141 -0
  222. package/cli/release/changelog.ts +106 -0
  223. package/cli/release/gate-refusals.test.ts +85 -0
  224. package/cli/release/gate.test.ts +63 -0
  225. package/cli/release/gate.ts +70 -0
  226. package/cli/release/git.ts +66 -0
  227. package/cli/release/github.test.ts +128 -0
  228. package/cli/release/github.ts +46 -0
  229. package/cli/release/list-dir.test.ts +11 -0
  230. package/cli/release/list-dir.ts +6 -0
  231. package/cli/release/main.test.ts +48 -0
  232. package/cli/release/main.ts +51 -0
  233. package/cli/release/manifest.test.ts +24 -0
  234. package/cli/release/manifest.ts +34 -0
  235. package/cli/release/pack.test.ts +107 -0
  236. package/cli/release/pack.ts +76 -0
  237. package/cli/release/plan.ts +127 -0
  238. package/cli/release/publish-run.test.ts +98 -0
  239. package/cli/release/publish.test.ts +127 -0
  240. package/cli/release/publish.ts +125 -0
  241. package/cli/release/refuse.ts +5 -0
  242. package/cli/release/release-messages.constants.ts +96 -0
  243. package/cli/release/release-refusals.test.ts +149 -0
  244. package/cli/release/release.constants.ts +128 -0
  245. package/cli/release/release.test.ts +199 -0
  246. package/cli/release/release.ts +145 -0
  247. package/cli/release/version.test.ts +118 -0
  248. package/cli/release/version.ts +69 -0
  249. package/cli/release/wait.test.ts +101 -0
  250. package/cli/release/wait.ts +95 -0
  251. package/cli/script-diagnostics.test.ts +76 -0
  252. package/cli/script-diagnostics.ts +62 -0
  253. package/cli/scripts/board.ts +519 -0
  254. package/cli/scripts/browser.ts +994 -0
  255. package/cli/scripts/change-set.ts +105 -0
  256. package/cli/scripts/check-globals.ts +199 -0
  257. package/cli/scripts/ci-skips.constants.ts +17 -0
  258. package/cli/scripts/ci-skips.ts +118 -0
  259. package/cli/scripts/ci-wait-run.ts +28 -0
  260. package/cli/scripts/ci-wait.constants.ts +44 -0
  261. package/cli/scripts/ci-wait.ts +175 -0
  262. package/cli/scripts/ci.constants.ts +15 -0
  263. package/cli/scripts/ci.ts +208 -0
  264. package/cli/scripts/clean.ts +363 -0
  265. package/cli/scripts/comments-step.ts +15 -0
  266. package/cli/scripts/doctor.ts +828 -0
  267. package/cli/scripts/emit-agent-shell-tools.ts +91 -0
  268. package/cli/scripts/emit-mcp-policy.ts +402 -0
  269. package/cli/scripts/folder-map.ts +353 -0
  270. package/cli/scripts/gate-fat-tools.ts +1095 -0
  271. package/cli/scripts/gitignore-guard.ts +338 -0
  272. package/cli/scripts/google-workspace.ts +779 -0
  273. package/cli/scripts/hook-command.ts +72 -0
  274. package/cli/scripts/hook-input.ts +178 -0
  275. package/cli/scripts/hook-io.constants.ts +5 -0
  276. package/cli/scripts/hook-io.ts +46 -0
  277. package/cli/scripts/hooks-union.ts +469 -0
  278. package/cli/scripts/interactive.ts +30 -0
  279. package/cli/scripts/jev.ts +420 -0
  280. package/cli/scripts/job.ts +934 -0
  281. package/cli/scripts/jsonc.ts +9 -0
  282. package/cli/scripts/lessons.ts +564 -0
  283. package/cli/scripts/lint-baseline.ts +233 -0
  284. package/cli/scripts/lint-main.ts +24 -0
  285. package/cli/scripts/lint-skips.ts +139 -0
  286. package/cli/scripts/lint.constants.ts +6 -0
  287. package/cli/scripts/lint.ts +142 -0
  288. package/cli/scripts/listen.ts +962 -0
  289. package/cli/scripts/managed-text.ts +47 -0
  290. package/cli/scripts/orchestrate-parent.ts +1155 -0
  291. package/cli/scripts/parse-need.ts +81 -0
  292. package/cli/scripts/persist-handoff.ts +228 -0
  293. package/cli/scripts/persist-implementation.ts +276 -0
  294. package/cli/scripts/persist-plan.ts +888 -0
  295. package/cli/scripts/persist-roadmap.ts +478 -0
  296. package/cli/scripts/persist-tmp.ts +177 -0
  297. package/cli/scripts/persist.ts +54 -0
  298. package/cli/scripts/phase-files.ts +121 -0
  299. package/cli/scripts/prompt.ts +38 -0
  300. package/cli/scripts/purge-global-mcps.ts +313 -0
  301. package/cli/scripts/rejections-step.ts +37 -0
  302. package/cli/scripts/rtk-grok.ts +102 -0
  303. package/cli/scripts/scrub-spawn-prompt.ts +103 -0
  304. package/cli/scripts/scrub-text-lib.ts +58 -0
  305. package/cli/scripts/session-map.ts +69 -0
  306. package/cli/scripts/setup.ts +862 -0
  307. package/cli/scripts/skill-conflicts.ts +82 -0
  308. package/cli/scripts/speak.ts +370 -0
  309. package/cli/scripts/strategy.ts +168 -0
  310. package/cli/scripts/sync-global-settings.ts +316 -0
  311. package/cli/scripts/turn-steps.ts +249 -0
  312. package/cli/scripts/turn.ts +157 -0
  313. package/cli/scripts/typecheck-step.ts +43 -0
  314. package/cli/scripts/vscode-tasks.ts +175 -0
  315. package/cli/scripts/workspace-root.ts +160 -0
  316. package/cli/scripts/worktree.ts +806 -0
  317. package/cli/spawn.test.ts +34 -0
  318. package/cli/spawn.ts +27 -0
  319. package/cli/staged-change.constants.ts +11 -0
  320. package/cli/staged-change.ts +79 -0
  321. package/cli/stryker-preload.test.ts +50 -0
  322. package/cli/stryker-preload.ts +9 -0
  323. package/cli/sync/agents/frontmatter.test.ts +197 -0
  324. package/cli/sync/agents/frontmatter.ts +140 -0
  325. package/cli/sync/agents/hooks.test.ts +517 -0
  326. package/cli/sync/agents/hooks.ts +401 -0
  327. package/cli/sync/agents/rules.test.ts +229 -0
  328. package/cli/sync/agents/rules.ts +84 -0
  329. package/cli/sync/agents/sources.test.ts +375 -0
  330. package/cli/sync/agents/sources.ts +244 -0
  331. package/cli/sync/agents/subagents.test.ts +149 -0
  332. package/cli/sync/agents/subagents.ts +158 -0
  333. package/cli/sync/agents-text.test.ts +31 -0
  334. package/cli/sync/agents-text.ts +36 -0
  335. package/cli/sync/fixtures/copied.README.md +35 -0
  336. package/cli/sync/fixtures/copied.tgz +0 -0
  337. package/cli/sync/fixtures/maps/golden/INDEX.md +22 -0
  338. package/cli/sync/fixtures/maps-ws.tgz +0 -0
  339. package/cli/sync/git-files.test.ts +340 -0
  340. package/cli/sync/git-files.ts +197 -0
  341. package/cli/sync/git-hooks.test.ts +94 -0
  342. package/cli/sync/golden.test.ts +15 -0
  343. package/cli/sync/hooks-mode.test.ts +99 -0
  344. package/cli/sync/hosts.ts +28 -0
  345. package/cli/sync/knip-config.constants.ts +4 -0
  346. package/cli/sync/knip-config.test.ts +147 -0
  347. package/cli/sync/knip-config.ts +108 -0
  348. package/cli/sync/manifest.test.ts +78 -0
  349. package/cli/sync/manifest.ts +60 -0
  350. package/cli/sync/maps.test.ts +304 -0
  351. package/cli/sync/maps.ts +297 -0
  352. package/cli/sync/migrate.test.ts +2000 -0
  353. package/cli/sync/migrate.ts +1104 -0
  354. package/cli/sync/nightly-jobs.test.ts +63 -0
  355. package/cli/sync/nightly.constants.ts +48 -0
  356. package/cli/sync/nightly.test.ts +100 -0
  357. package/cli/sync/nightly.ts +75 -0
  358. package/cli/sync/outputs.test.ts +351 -0
  359. package/cli/sync/outputs.ts +168 -0
  360. package/cli/sync/oxlint-config.test.ts +178 -0
  361. package/cli/sync/oxlint-config.ts +168 -0
  362. package/cli/sync/release-jobs.test.ts +29 -0
  363. package/cli/sync/release-reports.test.ts +31 -0
  364. package/cli/sync/release-workflow.constants.ts +61 -0
  365. package/cli/sync/release-workflow.test.ts +192 -0
  366. package/cli/sync/release-workflow.ts +86 -0
  367. package/cli/sync/release.constants.ts +93 -0
  368. package/cli/sync/release.test.ts +187 -0
  369. package/cli/sync/release.ts +112 -0
  370. package/cli/sync/repo-dir.test.ts +371 -0
  371. package/cli/sync/repo-dir.ts +205 -0
  372. package/cli/sync/standards.constants.ts +8 -0
  373. package/cli/sync/standards.test.ts +206 -0
  374. package/cli/sync/standards.ts +179 -0
  375. package/cli/sync/stock-history.json +2796 -0
  376. package/cli/sync/stock-history.test.ts +66 -0
  377. package/cli/sync/stock-history.ts +84 -0
  378. package/cli/sync/stock.test.ts +47 -0
  379. package/cli/sync/stock.ts +24 -0
  380. package/cli/sync/trees.test.ts +621 -0
  381. package/cli/sync/trees.ts +280 -0
  382. package/cli/sync/validate.test.ts +299 -0
  383. package/cli/sync/validate.ts +402 -0
  384. package/cli/sync/workspace.test.ts +180 -0
  385. package/cli/sync/workspace.ts +138 -0
  386. package/cli/sync/write.test.ts +341 -0
  387. package/cli/sync/write.ts +231 -0
  388. package/cli/sync.test.ts +1495 -0
  389. package/cli/sync.ts +360 -0
  390. package/cli/tdd-scope.test.ts +36 -0
  391. package/cli/tdd-scope.ts +41 -0
  392. package/cli/test-files.ts +16 -0
  393. package/cli/ts-program.ts +69 -0
  394. package/cli/tsconfig.constants.ts +8 -0
  395. package/cli/workerstack-config.types.ts +21 -0
  396. package/dist/cli/env.d.ts +10 -0
  397. package/dist/cli/env.js +64 -0
  398. package/dist/presets/temp-root-env.d.ts +10 -0
  399. package/dist/presets/temp-root-env.js +20 -0
  400. package/dist/presets/temp-root.constants.d.ts +6 -0
  401. package/dist/presets/temp-root.constants.js +6 -0
  402. package/dist/presets/temp-root.d.ts +2 -0
  403. package/dist/presets/temp-root.js +60 -0
  404. package/dist/presets/vitest.constants.d.ts +16 -0
  405. package/dist/presets/vitest.constants.js +16 -0
  406. package/dist/presets/vitest.d.ts +11 -0
  407. package/dist/presets/vitest.js +67 -0
  408. package/dist/presets/workers-plugin.d.ts +10 -0
  409. package/dist/presets/workers-plugin.js +54 -0
  410. package/package.json +141 -4
  411. package/references/INDEX.md +100 -0
  412. package/references/browser.md +66 -0
  413. package/references/cascade.md +15 -0
  414. package/references/clasp.md +21 -0
  415. package/references/cloudflare.md +36 -0
  416. package/references/google-workspace.md +79 -0
  417. package/references/hooks.md +103 -0
  418. package/references/implementation-format.md +119 -0
  419. package/references/jev.md +56 -0
  420. package/references/lessons.md +73 -0
  421. package/references/listen.md +80 -0
  422. package/references/map.md +107 -0
  423. package/references/output.md +47 -0
  424. package/references/plan-format.md +264 -0
  425. package/references/review-loop.md +48 -0
  426. package/references/roadmap-format.md +104 -0
  427. package/references/rtk.md +27 -0
  428. package/references/scope.md +76 -0
  429. package/references/setup.md +42 -0
  430. package/references/speak.md +59 -0
  431. package/tsconfig/base.json +28 -0
  432. package/tsconfig/bun.d.ts +1 -0
  433. package/tsconfig/bun.json +8 -0
  434. package/tsconfig/reset.d.ts +1 -0
  435. package/tsconfig/strict.json +12 -0
  436. package/tsconfig/web.json +7 -0
  437. package/tsconfig/worker.d.ts +1 -0
  438. package/tsconfig/worker.json +8 -0
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: implementer-fast
3
+ description: "Implements one reviewed phase. Writes product code and tests."
4
+ tier: implementer
5
+ exec: true
6
+ claudecode:
7
+ model: sonnet
8
+ effort: medium
9
+ autoCompactWindow: 350000
10
+ maxTurns: 100
11
+ permissionMode: bypassPermissions
12
+ omitClaudeMd: true
13
+ tools: [Read, Grep, Glob, Bash, Write, Edit]
14
+ disallowedTools: [Agent]
15
+ grokcli:
16
+ model: grok-4.7
17
+ maxTurns: 100
18
+ permission_mode: bypassPermissions
19
+ agents_md: false
20
+ mcpInheritance: none
21
+ tools: read_file, grep, list_dir, write, search_replace, run_terminal_command
22
+ codexcli:
23
+ model: gpt-6-luna
24
+ model_reasoning_effort: medium
25
+ sandbox_mode: danger-full-access
26
+ approval_policy: never
27
+ cursor:
28
+ model: inherit
29
+ antigravity-cli:
30
+ model: flash
31
+ subagent: true
32
+ mainAgent: false
33
+ inheritMcp: false
34
+ commandExecutionPolicy: eager
35
+ tools: [read_file, run_command, edit_file, write_file]
36
+ ---
37
+ You implement one phase for a reviewer. The brief you receive is the whole task: the leaves of one phase, and any findings to fix. Do exactly that.
38
+
39
+ Write product files and tests only. Never write the plan, the journal, or anything under `.tmp/repo/plans` or `.tmp/repo/implementations`. The reviewer owns those.
40
+
41
+ Write only the files the brief lists.
42
+
43
+ Never stage and never commit. Leave every change in the working tree.
44
+
45
+ Write the failing test first when a leaf asks for one. Run it and see it fail. Then make the change. Run the phase's tests before you report.
46
+
47
+ Rules:
48
+ - Work only in the worktree the brief names.
49
+ - Run the CLI as `bun repo …`.
50
+ - Route big output through `bun repo job`: `--job test --query '<cmd>'` for tests, `--job exec` or `--job search` for anything else that prints a lot. Read a stash path only with offset and limit.
51
+
52
+ If a leaf cannot be done as written, stop and say why. Do not work around it.
53
+
54
+ End with exactly these three lines:
55
+ changed: <comma-separated repo-relative paths>
56
+ tests: <last test command> → exit <N>
57
+ status: done|partial|stuck
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: orchestrator
3
+ description: "Does the work. Spawn scout when isolation is cheaper. Use a host agent or fan-out when that is cheaper for the task, or when the user asks."
4
+ tier: parent
5
+ targets:
6
+ - grokcli
7
+ - antigravity-cli
8
+ grokcli:
9
+ model: grok-4.7
10
+ permission_mode: bypassPermissions
11
+ agents_md: false
12
+ mcpInheritance: none
13
+ # Registry ids. A comma-separated line splits Agent(...) and the allowlist stays empty.
14
+ tools:
15
+ - read_file
16
+ - grep
17
+ - list_dir
18
+ - write
19
+ - search_replace
20
+ - web_search
21
+ - web_fetch
22
+ - todo_write
23
+ - run_terminal_cmd
24
+ - task
25
+ - get_task_output
26
+ - kill_task
27
+ - workflow
28
+ - search_tool
29
+ - use_tool
30
+ - monitor
31
+ - ask_user_question
32
+ - enter_plan_mode
33
+ - exit_plan_mode
34
+ - Agent(scout, explore, plan, general-purpose)
35
+ antigravity-cli:
36
+ model: inherit
37
+ subagent: false
38
+ mainAgent: true
39
+ inheritMcp: false
40
+ commandExecutionPolicy: eager
41
+ tools: [read_file, run_command, manage_task, send_message, schedule, invoke_subagent, define_subagent, manage_subagents, write_file, edit_file, search_web, ask_question]
42
+ ---
43
+ Follow AGENTS.md. Scratch in `.tmp/repo/`. Browse with `bun repo browser`; the rail swaps to Chromium when Lightpanda is blocked. App e2e uses Playwright.
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: researcher
3
+ description: "Heavy research worker. Web docs, browser, Playwright. Facts with sources."
4
+ tier: scout
5
+ exec: true
6
+ claudecode:
7
+ model: haiku
8
+ effort: medium
9
+ autoCompactWindow: 200000
10
+ maxTurns: 60
11
+ permissionMode: bypassPermissions
12
+ omitClaudeMd: true
13
+ tools: [Read, Grep, Glob, Bash, Write, WebSearch, WebFetch]
14
+ disallowedTools: [Agent]
15
+ grokcli:
16
+ model: grok-4.7
17
+ maxTurns: 60
18
+ permission_mode: bypassPermissions
19
+ agents_md: false
20
+ mcpInheritance: none
21
+ tools: read_file, grep, list_dir, write, run_terminal_command, web_search, web_fetch
22
+ codexcli:
23
+ model: gpt-6-luna
24
+ model_reasoning_effort: medium
25
+ sandbox_mode: danger-full-access
26
+ approval_policy: never
27
+ cursor:
28
+ model: inherit
29
+ antigravity-cli:
30
+ model: flash_lite
31
+ subagent: true
32
+ mainAgent: false
33
+ inheritMcp: false
34
+ commandExecutionPolicy: eager
35
+ tools: [read_file, run_command, write_file]
36
+ ---
37
+ You research one question for an agent. Heavy research is your job: read web docs, drive pages with `bun repo browser`, and run Playwright through Bash when a page needs a real browser. Where your host has no web tool, browse with `bun repo browser` and `curl` in the shell. Your return is read by an agent, not a person: facts only, no voice, each fact with its source.
38
+
39
+ Rules:
40
+ - Work only in the worktree the brief names.
41
+ - Run the CLI as `bun repo …`.
42
+ - Route big output through `bun repo job`: `--job test --query '<cmd>'` for tests, `--job exec` or `--job search` for anything else that prints a lot. Read a stash path only with offset and limit.
43
+
44
+ End with exactly these three lines:
45
+ facts: <one fact per line, each with its source>
46
+ sources: <urls or repo paths>
47
+ status: done|partial|stuck
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: scout
3
+ description: "Isolation worker. Facts only."
4
+ tier: scout
5
+ exec: true
6
+ claudecode:
7
+ model: haiku
8
+ effort: low
9
+ autoCompactWindow: 100000
10
+ maxTurns: 40
11
+ permissionMode: bypassPermissions
12
+ omitClaudeMd: true
13
+ tools: [Read, Grep, Glob, Bash, Write, Edit, WebSearch, WebFetch]
14
+ disallowedTools: [Agent]
15
+ grokcli:
16
+ model: grok-4.7
17
+ maxTurns: 40
18
+ permission_mode: bypassPermissions
19
+ agents_md: false
20
+ mcpInheritance: none
21
+ tools: read_file, grep, list_dir, write, search_replace, run_terminal_command, web_search, web_fetch
22
+ codexcli:
23
+ model: gpt-6-luna
24
+ model_reasoning_effort: low
25
+ sandbox_mode: danger-full-access
26
+ approval_policy: never
27
+ antigravity-cli:
28
+ model: flash_lite
29
+ subagent: true
30
+ mainAgent: false
31
+ inheritMcp: false
32
+ commandExecutionPolicy: eager
33
+ tools: [read_file, run_command, edit_file]
34
+ ---
35
+ Return isolation facts (≤8 lines, stash receipt when needed). A browse snap is the payload. Your return is read by an agent, not a person: facts only, no voice.
36
+
37
+ Rules:
38
+ - Work only in the worktree the brief names.
39
+ - Run the CLI as `bun repo …`.
40
+ - Route big output through `bun repo job`: `--job test --query '<cmd>'` for tests, `--job exec` or `--job search` for anything else that prints a lot. Read a stash path only with offset and limit.
41
+
42
+ End with exactly these two lines:
43
+ facts: <one fact per line>
44
+ status: done|partial|stuck
package/bin/repo.ts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bun
2
+ import { main } from "../cli/bin.ts";
3
+
4
+ await main(process.argv.slice(2));
@@ -0,0 +1,38 @@
1
+ import path from "node:path";
2
+ import { expect, it } from "vitest";
3
+ import { childEnv } from "./env.ts";
4
+ import { fetchArchive, spawnCaptured } from "./actionlint.ts";
5
+
6
+ it("fetches the bytes of an archive over http and refuses a failing status", async () => {
7
+ const server = Bun.serve({
8
+ port: 0,
9
+ fetch: (request) =>
10
+ new URL(request.url).pathname === "/ok" ? new Response("an archive") : new Response("gone", { status: 404 }),
11
+ });
12
+ try {
13
+ const base = `http://127.0.0.1:${server.port}`;
14
+ expect(new TextDecoder().decode(await fetchArchive(`${base}/ok`))).toBe("an archive");
15
+ await expect(fetchArchive(`${base}/missing`)).rejects.toThrow(`${base}/missing answered 404`);
16
+ } finally {
17
+ await server.stop(true);
18
+ }
19
+ });
20
+
21
+ it("captures the output and exit code of a command, and a spawn error as stderr", () => {
22
+ const cwd = path.join(import.meta.dirname);
23
+ const run = spawnCaptured(process.execPath, ["-e", 'console.log("out"); console.error("err"); process.exit(3)'], {
24
+ cwd,
25
+ env: childEnv(),
26
+ });
27
+ expect(run).toEqual({ code: 3, stdout: "out\n", stderr: "err\n" });
28
+ const missing = spawnCaptured(path.join(cwd, "no-such-binary"), [], { cwd, env: childEnv() });
29
+ expect(missing.code).toBe(1);
30
+ expect(missing.stdout).toBe("");
31
+ expect(missing.stderr).toContain("no-such-binary");
32
+ });
33
+
34
+ it("reports a command killed by a signal as exit 1", () => {
35
+ const cwd = import.meta.dirname;
36
+ const killed = spawnCaptured(process.execPath, ["-e", "process.kill(process.pid, 9)"], { cwd, env: childEnv() });
37
+ expect(killed.code).toBe(1);
38
+ });
@@ -0,0 +1,162 @@
1
+ import { createHash } from "node:crypto";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { afterEach, expect, it } from "vitest";
6
+ import { childEnv } from "./env.ts";
7
+ import { checkActionlintAt, type Spawn } from "./actionlint.ts";
8
+
9
+ const dirs: string[] = [];
10
+ const temp = (): string => {
11
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "workerstack-actionlint-"));
12
+ dirs.push(dir);
13
+ return dir;
14
+ };
15
+ afterEach(() => {
16
+ for (const dir of dirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
17
+ });
18
+
19
+ const ARCHIVE = new TextEncoder().encode("an archive");
20
+ const sha = createHash("sha256").update(ARCHIVE).digest("hex");
21
+ const URL_BASE = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/";
22
+ type Call = { command: string; args: string[] };
23
+
24
+ /** A spawn that plays tar (it unpacks a binary named `exe` into the -C dir) and answers actionlint cleanly. */
25
+ function fakes(exe: string, tarCode = 0) {
26
+ const calls: Call[] = [];
27
+ const archives: string[] = [];
28
+ const spawn: Spawn = (command, args) => {
29
+ calls.push({ command, args });
30
+ if (args[0] !== "-xf") return { code: 0, stdout: "[]", stderr: "" };
31
+ archives.push(fs.readFileSync(args[1] ?? "", "utf8"));
32
+ fs.writeFileSync(path.join(args[3] ?? "", exe), "binary");
33
+ return { code: tarCode, stdout: "", stderr: tarCode === 0 ? "" : "tar broke\n" };
34
+ };
35
+ return { calls, archives, spawn };
36
+ }
37
+
38
+ /** A release-pack repo with one workflow and no cache. */
39
+ function planted() {
40
+ const root = temp();
41
+ fs.writeFileSync(path.join(root, "workerstack.config.ts"), 'export default { packs: ["release"] };\n');
42
+ fs.mkdirSync(path.join(root, ".github", "workflows"), { recursive: true });
43
+ fs.writeFileSync(path.join(root, ".github", "workflows", "ci.yml"), "name: x\n");
44
+ return { root, cacheDir: path.join(temp(), "actionlint", "1.7.12") };
45
+ }
46
+
47
+ const hashes = (key: string) => ({ [key]: sha });
48
+ const ok = () => Promise.resolve(ARCHIVE);
49
+
50
+ it("fetches the archive, checks its hash, unpacks it, caches the binary and runs it", async () => {
51
+ const { root, cacheDir } = planted();
52
+ const { calls, archives, spawn } = fakes("actionlint");
53
+ const urls: string[] = [];
54
+ const fetch = (url: string) => {
55
+ urls.push(url);
56
+ return ok();
57
+ };
58
+ const deps = { fetch, spawn, cacheDir, platform: "linux", arch: "x64", hashes: hashes("linux_amd64") };
59
+ expect(await checkActionlintAt(root, deps)).toEqual({ problems: [] });
60
+ expect(urls).toEqual([`${URL_BASE}actionlint_1.7.12_linux_amd64.tar.gz`]);
61
+ expect(archives).toEqual(["an archive"]);
62
+ expect(calls[0]?.command).toBe("tar");
63
+ expect(calls.at(-1)?.command).toBe(path.join(cacheDir, "actionlint"));
64
+ expect(fs.readdirSync(cacheDir)).toEqual(["actionlint"]);
65
+ await checkActionlintAt(root, { ...deps, fetch: () => Promise.reject(new Error("cached")) });
66
+ expect(urls).toHaveLength(1);
67
+ });
68
+
69
+ it("names its archive and unpack directory for the process, and removes both", async () => {
70
+ const { root, cacheDir } = planted();
71
+ const seen: string[][] = [];
72
+ const spawn: Spawn = (_command, args) => {
73
+ if (args[0] !== "-xf") return { code: 0, stdout: "[]", stderr: "" };
74
+ seen.push([args[1] ?? "", args[3] ?? ""]);
75
+ fs.writeFileSync(path.join(args[3] ?? "", "actionlint"), "binary");
76
+ return { code: 0, stdout: "", stderr: "" };
77
+ };
78
+ const deps = { fetch: ok, spawn, cacheDir, platform: "linux", arch: "arm64", hashes: hashes("linux_arm64") };
79
+ await checkActionlintAt(root, deps);
80
+ expect(seen).toHaveLength(1);
81
+ for (const name of seen[0] ?? []) {
82
+ expect(path.dirname(name)).toBe(cacheDir);
83
+ expect(path.basename(name)).toContain(String(process.pid));
84
+ }
85
+ expect(fs.readdirSync(cacheDir)).toEqual(["actionlint"]);
86
+ });
87
+
88
+ it.each([
89
+ [{ platform: "darwin", arch: "x64", archive: "darwin_amd64.tar.gz", exe: "actionlint" }],
90
+ [{ platform: "darwin", arch: "arm64", archive: "darwin_arm64.tar.gz", exe: "actionlint" }],
91
+ [{ platform: "win32", arch: "x64", archive: "windows_amd64.zip", exe: "actionlint.exe" }],
92
+ [{ platform: "win32", arch: "arm64", archive: "windows_arm64.zip", exe: "actionlint.exe" }],
93
+ ])("$platform $arch fetches $archive and caches $exe", async ({ platform, arch, archive, exe }) => {
94
+ const { root, cacheDir } = planted();
95
+ const key = archive.replace(/\.(tar\.gz|zip)$/, "");
96
+ const urls: string[] = [];
97
+ const fetch = (url: string) => {
98
+ urls.push(url);
99
+ return ok();
100
+ };
101
+ const { calls, spawn } = fakes(exe);
102
+ await checkActionlintAt(root, { fetch, spawn, cacheDir, platform, arch, hashes: hashes(key) });
103
+ expect(urls).toEqual([`${URL_BASE}actionlint_1.7.12_${archive}`]);
104
+ const tar = platform === "win32" ? `${childEnv().SystemRoot ?? "C:\\Windows"}\\System32\\tar.exe` : "tar";
105
+ expect(calls[0]?.command).toBe(tar);
106
+ expect(fs.readdirSync(cacheDir)).toEqual([exe]);
107
+ });
108
+
109
+ it("refuses an archive whose hash differs and caches nothing", async () => {
110
+ const { root, cacheDir } = planted();
111
+ const { calls, spawn } = fakes("actionlint");
112
+ const wrong = { linux_amd64: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" };
113
+ const deps = { fetch: ok, spawn, cacheDir, platform: "linux", arch: "x64", hashes: wrong };
114
+ const out = await checkActionlintAt(root, deps);
115
+ expect(out.problems).toEqual([
116
+ `actionlint: actionlint_1.7.12_linux_amd64.tar.gz has sha256 ${sha.slice(0, 8)}\u2026, expected 8aca8db9\u2026; nothing was cached`,
117
+ ]);
118
+ expect(calls).toEqual([]);
119
+ expect(fs.readdirSync(cacheDir)).toEqual([]);
120
+ });
121
+
122
+ it.each([
123
+ ["an error", new Error("fetch failed"), "fetch failed"],
124
+ ["a bare value", "offline", "offline"],
125
+ ])("reports a fetch that rejects with %s", async (_name, reason, text) => {
126
+ const { root, cacheDir } = planted();
127
+ const { calls, spawn } = fakes("actionlint");
128
+ const fetch = () => Promise.reject(reason);
129
+ const out = await checkActionlintAt(root, { fetch, spawn, cacheDir, platform: "linux", arch: "x64" });
130
+ expect(out.problems).toEqual([`actionlint: could not fetch 1.7.12 (${text}); connect once so repo can cache it`]);
131
+ expect(calls).toEqual([]);
132
+ });
133
+
134
+ it("reports a tar that fails and removes what it made", async () => {
135
+ const { root, cacheDir } = planted();
136
+ const { spawn } = fakes("actionlint", 1);
137
+ const deps = { fetch: ok, spawn, cacheDir, platform: "linux", arch: "x64", hashes: hashes("linux_amd64") };
138
+ const out = await checkActionlintAt(root, deps);
139
+ expect(out.problems).toEqual(["actionlint: could not unpack actionlint_1.7.12_linux_amd64.tar.gz: tar broke"]);
140
+ expect(fs.readdirSync(cacheDir)).toEqual([]);
141
+ });
142
+
143
+ it.each([
144
+ ["freebsd", "x64"],
145
+ ["linux", "ia32"],
146
+ ])("says %s %s is unsupported and fetches nothing", async (platform, arch) => {
147
+ const { root, cacheDir } = planted();
148
+ const fetch = () => Promise.reject(new Error("no"));
149
+ const out = await checkActionlintAt(root, { fetch, spawn: fakes("x").spawn, cacheDir, platform, arch });
150
+ expect(out.problems).toEqual([
151
+ `actionlint: no 1.7.12 build for ${platform} ${arch}; run repo check on linux, macOS or Windows`,
152
+ ]);
153
+ });
154
+
155
+ it("treats a platform with no pinned hash as a mismatch", async () => {
156
+ const { root, cacheDir } = planted();
157
+ const deps = { fetch: ok, spawn: fakes("actionlint").spawn, cacheDir, platform: "linux", arch: "x64", hashes: {} };
158
+ const out = await checkActionlintAt(root, deps);
159
+ expect(out.problems).toEqual([
160
+ `actionlint: actionlint_1.7.12_linux_amd64.tar.gz has sha256 ${sha.slice(0, 8)}…, expected …; nothing was cached`,
161
+ ]);
162
+ });
@@ -0,0 +1,33 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, expect, it } from "vitest";
5
+ import { checkActionlintAt } from "./actionlint.ts";
6
+
7
+ const SLOW = 180_000;
8
+ const dirs: string[] = [];
9
+ afterEach(() => {
10
+ for (const dir of dirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
11
+ });
12
+
13
+ /** A release-pack repo whose one workflow has `jobs` as its job section. */
14
+ function planted(jobs: string): string {
15
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "workerstack-actionlint-real-"));
16
+ dirs.push(root);
17
+ fs.writeFileSync(path.join(root, "workerstack.config.ts"), 'export default { packs: ["release"] };\n');
18
+ fs.mkdirSync(path.join(root, ".github", "workflows"), { recursive: true });
19
+ const body = `name: x\non: push\njobs:\n build:\n${jobs}\n steps:\n - run: echo hi\n`;
20
+ fs.writeFileSync(path.join(root, ".github", "workflows", "ci.yml"), body);
21
+ return root;
22
+ }
23
+
24
+ it("lints a planted workflow with the real binary from the real cache: runs_on fails", { timeout: SLOW }, async () => {
25
+ const out = await checkActionlintAt(planted(" runs_on: ubuntu-latest"));
26
+ const line = 'actionlint: .github/workflows/ci.yml:5:5: unexpected key "runs_on" for "job" section';
27
+ expect(out.problems.some((problem) => problem.startsWith(line) && problem.endsWith("[syntax-check]"))).toBe(true);
28
+ });
29
+
30
+ it("gives no line for a clean workflow", { timeout: SLOW }, async () => {
31
+ const out = await checkActionlintAt(planted(" runs-on: ubuntu-latest"));
32
+ expect(out).toEqual({ problems: [] });
33
+ });
@@ -0,0 +1,43 @@
1
+ /** The pinned actionlint release. */
2
+ export const ACTIONLINT_VERSION: string = "1.7.12";
3
+ /** The directory under the state home that caches the binary, then the version. */
4
+ export const CACHE_DIR: string = "actionlint";
5
+ /** Where the pinned release's archives are published. */
6
+ export const RELEASE_URL: string = `https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}`;
7
+ /** How long the archive download may take. */
8
+ export const FETCH_TIMEOUT_MS = 120_000;
9
+ /** The most output a spawned tool may print before it is cut off. */
10
+ export const MAX_OUTPUT_BYTES: number = 64 * 1024 * 1024;
11
+ /** The workflow directory, relative to the repo root. */
12
+ export const WORKFLOWS_DIR = ".github/workflows";
13
+ /** The extensions of a workflow file. */
14
+ export const WORKFLOW_EXTENSIONS: readonly string[] = [".yml", ".yaml"];
15
+ /** The flags that print json and turn off the tools runners differ in. */
16
+ export const ACTIONLINT_FLAGS: readonly string[] = ["-format", "{{json .}}", "-shellcheck=", "-pyflakes="];
17
+ /** The windows system directory when `SystemRoot` is unset. */
18
+ export const DEFAULT_SYSTEM_ROOT = "C:\\Windows";
19
+ /** What `process.platform` calls windows. */
20
+ export const WINDOWS_PLATFORM = "win32";
21
+ /** The windows tar, under the system root; Git's GNU tar can come first on PATH and cannot read a zip. */
22
+ export const WINDOWS_TAR = "\\System32\\tar.exe";
23
+ /** The hash characters a mismatch line shows. */
24
+ export const SHOWN_HASH = 8;
25
+ /** The exit code actionlint gives when it found problems. */
26
+ export const EXIT_PROBLEMS = 1;
27
+ /** The octal mode of the cached binary. */
28
+ export const BINARY_MODE = 0o755;
29
+
30
+ /** What `process.platform` calls an os that actionlint publishes, mapped to the name its archives use. */
31
+ export const OS_NAMES: Record<string, string> = { darwin: "darwin", linux: "linux", win32: "windows" };
32
+ /** What `process.arch` calls an architecture that actionlint publishes, mapped to the name its archives use. */
33
+ export const ARCH_NAMES: Record<string, string> = { x64: "amd64", arm64: "arm64" };
34
+
35
+ /** The sha256 of each archive of the release, from its checksums.txt, by `<os>_<arch>`. */
36
+ export const HASHES: Record<string, string> = {
37
+ darwin_amd64: "5b44c3bc2255115c9b69e30efc0fecdf498fdb63c5d58e17084fd5f16324c644",
38
+ darwin_arm64: "aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f",
39
+ linux_amd64: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8",
40
+ linux_arm64: "325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6",
41
+ windows_amd64: "6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f6e9",
42
+ windows_arm64: "cadcf7ea4efe3a68728893813643cebe1185e5b1d4be5b96245f65c9a4d5ea41",
43
+ };
@@ -0,0 +1,108 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, beforeEach, expect, it, vi } from "vitest";
5
+ import { childEnv } from "./env.ts";
6
+ import { checkActionlintAt, type Spawn } from "./actionlint.ts";
7
+
8
+ const dirs: string[] = [];
9
+ const temp = (): string => {
10
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "workerstack-actionlint-"));
11
+ dirs.push(dir);
12
+ return dir;
13
+ };
14
+ afterEach(() => {
15
+ vi.unstubAllEnvs();
16
+ for (const dir of dirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
17
+ });
18
+
19
+ const EXE = process.platform === "win32" ? "actionlint.exe" : "actionlint";
20
+ type Call = { command: string; args: string[]; cwd: string; env: NodeJS.ProcessEnv };
21
+ let calls: Call[] = [];
22
+ beforeEach(() => {
23
+ calls = [];
24
+ });
25
+ const answering =
26
+ (code: number, stdout: string, stderr = ""): Spawn =>
27
+ (command, args, options) => {
28
+ calls.push({ command, args, ...options });
29
+ return { code, stdout, stderr };
30
+ };
31
+ const failFetch = () => Promise.reject(new Error("the cache should have been used"));
32
+
33
+ /** A repo with a config of `packs`, the workflow files named, and a cached binary. */
34
+ function repo(packs: string, workflows: string[]): { root: string; cacheDir: string } {
35
+ const root = temp();
36
+ const cacheDir = temp();
37
+ fs.writeFileSync(path.join(cacheDir, EXE), "binary");
38
+ fs.writeFileSync(path.join(root, "workerstack.config.ts"), `export default { packs: ${packs} };\n`);
39
+ const flows = path.join(root, ".github", "workflows");
40
+ fs.mkdirSync(flows, { recursive: true });
41
+ for (const name of workflows) fs.writeFileSync(path.join(flows, name), "name: x\n");
42
+ return { root, cacheDir };
43
+ }
44
+
45
+ it("runs nothing without a config, without the release pack or without a workflow", async () => {
46
+ const bare = temp();
47
+ const spawn = answering(0, "");
48
+ expect(await checkActionlintAt(bare, { spawn, fetch: failFetch })).toEqual({ problems: [] });
49
+ const off = repo('["standards"]', ["ci.yml"]);
50
+ expect(await checkActionlintAt(off.root, { spawn, fetch: failFetch, cacheDir: off.cacheDir })).toEqual({
51
+ problems: [],
52
+ });
53
+ const none = repo('["release"]', ["notes.txt"]);
54
+ fs.rmSync(path.join(none.root, ".github", "workflows", "notes.txt"));
55
+ expect(await checkActionlintAt(none.root, { spawn, fetch: failFetch, cacheDir: none.cacheDir })).toEqual({
56
+ problems: [],
57
+ });
58
+ fs.rmSync(path.join(none.root, ".github"), { recursive: true });
59
+ expect(await checkActionlintAt(none.root, { spawn, fetch: failFetch, cacheDir: none.cacheDir })).toEqual({
60
+ problems: [],
61
+ });
62
+ expect(calls).toEqual([]);
63
+ });
64
+
65
+ it("lints every .yml and .yaml workflow with the cached binary, in the repo root, and nothing else", async () => {
66
+ vi.stubEnv("GIT_DIR", "/elsewhere");
67
+ const { root, cacheDir } = repo('["release"]', ["b.yaml", "a.yml", "notes.txt"]);
68
+ const out = await checkActionlintAt(root, { spawn: answering(0, "[]"), fetch: failFetch, cacheDir });
69
+ expect(out).toEqual({ problems: [] });
70
+ expect(calls).toHaveLength(1);
71
+ const [call] = calls;
72
+ expect(call?.command).toBe(path.join(cacheDir, EXE));
73
+ expect(call?.args).toEqual([
74
+ "-format",
75
+ "{{json .}}",
76
+ "-shellcheck=",
77
+ "-pyflakes=",
78
+ ".github/workflows/a.yml",
79
+ ".github/workflows/b.yaml",
80
+ ]);
81
+ expect(call?.cwd).toBe(root);
82
+ expect(call?.env.PATH).toBe(childEnv().PATH);
83
+ expect(call?.env.GIT_DIR).toBeUndefined();
84
+ });
85
+
86
+ it("prints one line per problem actionlint finds", async () => {
87
+ const { root, cacheDir } = repo('["release"]', ["release.yml"]);
88
+ const items = [
89
+ { message: 'unexpected key "runs_on" for "job" section', filepath: "release.yml", line: 40, column: 9 },
90
+ { message: "bad", filepath: "release.yml", line: 2, column: 1, kind: "expression" },
91
+ ];
92
+ const spawn = answering(1, JSON.stringify(items.map((item) => ({ kind: "syntax-check", ...item }))));
93
+ const out = await checkActionlintAt(root, { spawn, fetch: failFetch, cacheDir });
94
+ expect(out.problems).toEqual([
95
+ 'actionlint: release.yml:40:9: unexpected key "runs_on" for "job" section [syntax-check]',
96
+ "actionlint: release.yml:2:1: bad [expression]",
97
+ ]);
98
+ });
99
+
100
+ it.each([
101
+ [{ name: "another exit code", code: 2, stdout: "", stderr: "boom\n", line: "actionlint: exited 2: boom" }],
102
+ [{ name: "unparsable output", code: 1, stdout: "not json", stderr: "oops\n", line: "actionlint: exited 1: oops" }],
103
+ [{ name: "json of another shape", code: 1, stdout: '{"a":1}', stderr: "", line: "actionlint: exited 1: " }],
104
+ ])("reports $name as a failure line", async ({ code, stdout, stderr, line }) => {
105
+ const { root, cacheDir } = repo('["release"]', ["ci.yml"]);
106
+ const out = await checkActionlintAt(root, { spawn: answering(code, stdout, stderr), fetch: failFetch, cacheDir });
107
+ expect(out.problems).toEqual([line]);
108
+ });