@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,63 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { ghFor, runGate, tagRelease } from "../fixtures/gate-world.ts";
3
+ import { removePlanted } from "../fixtures/planted-repo.ts";
4
+ import { commit, gitIn, NAME, world } from "../fixtures/release-world.ts";
5
+
6
+ afterEach(removePlanted);
7
+
8
+ const LIST = "databaseId,status,conclusion,event,headBranch,createdAt";
9
+
10
+ describe("repo release gate passes", () => {
11
+ it("prints one line: the tag, version, changelog and the ci run it waited for", async () => {
12
+ const w = world({}, false);
13
+ const short = tagRelease(w, { version: "0.2.0" });
14
+ expect(await runGate(w, [`${NAME}@0.2.0`])).toEqual({
15
+ code: 0,
16
+ out: `repo release gate: ${NAME}@0.2.0 is ${short}, version 0.2.0, changelog ok · waiting for ci on ${short} (run 7)… · passed\n`,
17
+ err: "",
18
+ });
19
+ });
20
+
21
+ it("waits on the tagged commit even when the checkout is on a later one", async () => {
22
+ const w = world({}, false);
23
+ tagRelease(w, { version: "0.2.0" });
24
+ const sha = gitIn(w.work, "rev-parse", "HEAD");
25
+ commit(w, "chore: later", { "later.txt": "x\n" });
26
+ const calls: string[] = [];
27
+ expect((await runGate(w, [`${NAME}@0.2.0`], ghFor("main", {}, calls))).code).toBe(0);
28
+ expect(calls).toContain(`run list --commit ${sha} --workflow ci.yml --json ${LIST}`);
29
+ });
30
+ });
31
+
32
+ describe("repo release gate on a ci that did not pass", () => {
33
+ const setup = () => {
34
+ const w = world({}, false);
35
+ return { w, short: tagRelease(w, { version: "0.2.0" }) };
36
+ };
37
+
38
+ it("prints the failed jobs and tells you to release the next version", async () => {
39
+ const { w, short } = setup();
40
+ const got = await runGate(w, [`${NAME}@0.2.0`], ghFor("main", { conclusion: "failure" }));
41
+ expect(got.code).toBe(1);
42
+ expect(got.out).toContain("failed: test; logs below\nboom\n");
43
+ expect(got.err).toBe(
44
+ `repo release gate: CI failed on ${short}; a tag is never moved, so fix it in a new commit and release the next version\n`,
45
+ );
46
+ });
47
+
48
+ it("says a newer push cancelled it", async () => {
49
+ const { w, short } = setup();
50
+ const got = await runGate(w, [`${NAME}@0.2.0`], ghFor("main", { conclusion: "cancelled" }));
51
+ expect(got.err).toBe(
52
+ `repo release gate: a newer push cancelled CI on ${short}; rerun ci on it, then rerun this job\n`,
53
+ );
54
+ });
55
+
56
+ it("says there was no run", async () => {
57
+ const { w, short } = setup();
58
+ const got = await runGate(w, [`${NAME}@0.2.0`], ghFor("main", { run: false }));
59
+ expect(got.err).toBe(
60
+ `repo release gate: no CI result for ${short} (no ci run after 120s); rerun this job once ci has run\n`,
61
+ );
62
+ });
63
+ });
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ import { readJson, waitFor, type Target } from "../scripts/ci-wait.ts";
3
+ import { read, type Tools } from "./git.ts";
4
+ import { parseManifest } from "./manifest.ts";
5
+ import { sectionFor } from "./changelog-parse.ts";
6
+ import { refuse } from "./refuse.ts";
7
+ import type { ReleaseIo } from "./release.ts";
8
+ import {
9
+ CHANGELOG_FILE,
10
+ GH_DEFAULT_BRANCH,
11
+ GIT_ON_BRANCH,
12
+ GIT_SHOW,
13
+ GIT_TAG_REF,
14
+ MANIFEST_FILE,
15
+ SHA_SHOWN,
16
+ } from "./release.constants.ts";
17
+ import {
18
+ GATE_PREFIX,
19
+ WAIT_USAGE,
20
+ gateCiLines,
21
+ gateOk,
22
+ nameDiffers,
23
+ noDefaultBranch,
24
+ noManifestAt,
25
+ noSection,
26
+ notOnBranch,
27
+ tagMissing,
28
+ versionDiffers,
29
+ } from "./release-messages.constants.ts";
30
+ import { splitSpec, type Spec } from "./wait.ts";
31
+
32
+ const branchSchema = () => z.object({ defaultBranchRef: z.object({ name: z.string() }) });
33
+
34
+ /** The commit the tag names when its package.json, changelog and branch agree with the tag; else the refusal line. */
35
+ function check(t: Tools, tag: string, parts: Spec): string | Target {
36
+ const sha = read(t, GIT_TAG_REF(tag));
37
+ if (sha === "") return tagMissing(tag);
38
+ const short = sha.slice(0, SHA_SHOWN);
39
+ const manifest = parseManifest(read(t, GIT_SHOW(sha, MANIFEST_FILE)));
40
+ if (manifest === null) return noManifestAt(short);
41
+ if (manifest.name !== parts.name) return nameDiffers(parts.name, short, manifest.name);
42
+ if (manifest.version !== parts.version) return versionDiffers(parts.version, short, manifest.version);
43
+ if (sectionFor(read(t, GIT_SHOW(sha, CHANGELOG_FILE)), parts.version) === null)
44
+ return noSection(short, parts.version);
45
+ const got = t.gh([...GH_DEFAULT_BRANCH], t.cwd);
46
+ const named = readJson(got, branchSchema());
47
+ if (named === null) return noDefaultBranch(got.stderr.trim());
48
+ const branch = named.defaultBranchRef.name;
49
+ return t.git(GIT_ON_BRANCH(sha, branch), t.cwd).code === 0 ? { sha, branch } : notOnBranch(short, branch);
50
+ }
51
+
52
+ /** `repo release gate <name>@<version>`: the tag matches the package and changelog on the default branch, and CI passed. */
53
+ export async function gate(argv: readonly string[], io: ReleaseIo): Promise<number> {
54
+ const tag = argv[0] ?? "";
55
+ const parts = argv.length === 1 ? splitSpec(tag) : null;
56
+ if (parts === null) return refuse(io, GATE_PREFIX, WAIT_USAGE);
57
+ const tools: Tools = { cwd: io.cwd, git: io.git, gh: io.gh };
58
+ const target = check(tools, tag, parts);
59
+ if (typeof target === "string") return refuse(io, GATE_PREFIX, target);
60
+ const short = target.sha.slice(0, SHA_SHOWN);
61
+ const printed: string[] = [];
62
+ const outcome = await waitFor({ ...io, stdout: (text) => printed.push(text.trim()), target });
63
+ if (outcome.kind !== "passed") {
64
+ io.stdout(`${printed.join("\n")}\n`);
65
+ const detail = outcome.kind === "error" ? outcome.detail : "";
66
+ return refuse(io, GATE_PREFIX, gateCiLines[outcome.kind](short, detail));
67
+ }
68
+ io.stdout(`${GATE_PREFIX}${[gateOk(tag, short, parts.version), ...printed].join(" · ")}\n`);
69
+ return 0;
70
+ }
@@ -0,0 +1,66 @@
1
+ import type { Runner } from "../scripts/ci-wait.ts";
2
+ import type { Commit } from "./version.ts";
3
+ import {
4
+ BARE_TAG_OF,
5
+ COMMIT_END,
6
+ COMMIT_SEPARATOR,
7
+ GIT_AHEAD_BEHIND,
8
+ GIT_COMMITS,
9
+ GIT_ORIGIN_HEAD,
10
+ GIT_RELEASE_LOG,
11
+ GIT_TAG_REF,
12
+ MAIN_FALLBACK,
13
+ ORIGIN_HEAD_PREFIX,
14
+ TAG_OF,
15
+ } from "./release.constants.ts";
16
+ import { gitFailed } from "./release-messages.constants.ts";
17
+
18
+ /** The checkout `repo release` works in, and the git and gh it runs there. */
19
+ export type Tools = { cwd: string; git: Runner; gh: Runner };
20
+
21
+ /** What git prints for `args`, trimmed; empty when it fails. */
22
+ export const read = (t: Tools, args: readonly string[]): string => t.git([...args], t.cwd).stdout.trim();
23
+
24
+ /** Runs git with `args` and throws a line naming the failure; the flow's boundary prints it. */
25
+ export function must(t: Tools, args: readonly string[]): string {
26
+ const got = t.git([...args], t.cwd);
27
+ if (got.code !== 0)
28
+ throw new Error(gitFailed(args[0] /* every git call here names a subcommand */ as string, got.stderr.trim()));
29
+ return got.stdout.trim();
30
+ }
31
+
32
+ /** The default branch: what origin's HEAD names, else main. */
33
+ export function defaultBranch(t: Tools): string {
34
+ const ref = read(t, GIT_ORIGIN_HEAD);
35
+ return ref.startsWith(ORIGIN_HEAD_PREFIX) ? ref.slice(ORIGIN_HEAD_PREFIX.length) : MAIN_FALLBACK;
36
+ }
37
+
38
+ /** How many commits `branch` is ahead of and behind origin's. */
39
+ export function divergence(t: Tools, branch: string): { ahead: number; behind: number } {
40
+ const [ahead, behind] = read(t, GIT_AHEAD_BEHIND(branch)).split(/\s+/);
41
+ return { ahead: Number(ahead), behind: Number(behind) };
42
+ }
43
+
44
+ /** The tag that marks `version` as released, `<name>@<version>` or `v<version>`; null when neither exists. */
45
+ export function releaseTag(t: Tools, name: string, version: string): string | null {
46
+ return [TAG_OF(name, version), BARE_TAG_OF(version)].find((tag) => read(t, GIT_TAG_REF(tag)) !== "") ?? null;
47
+ }
48
+
49
+ /** The sha of the commit in HEAD's history whose subject is exactly `subject`; null when there is none. */
50
+ export function commitWithSubject(t: Tools, subject: string): string | null {
51
+ const lines = read(t, GIT_RELEASE_LOG(subject)).split("\n");
52
+ const hit = lines.map((line) => line.split("\t")).find(([, found]) => found === subject);
53
+ return hit?.[0] ?? null;
54
+ }
55
+
56
+ /** The commits after `since` (every commit when null), newest first. */
57
+ export function commitsSince(t: Tools, since: string | null): Commit[] {
58
+ return read(t, GIT_COMMITS(since))
59
+ .split(COMMIT_END)
60
+ .map((entry) => entry.trim())
61
+ .filter((entry) => entry !== "")
62
+ .map((entry) => {
63
+ const at = entry.indexOf(COMMIT_SEPARATOR);
64
+ return { subject: entry.slice(0, at), body: entry.slice(at + 1) };
65
+ });
66
+ }
@@ -0,0 +1,128 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { removePlanted, tempDir, write } from "../fixtures/planted-repo.ts";
3
+ import type { Runner } from "../scripts/ci-wait.ts";
4
+ import { releaseGithub } from "./github.ts";
5
+
6
+ afterEach(removePlanted);
7
+
8
+ const CHANGELOG = `# Changelog
9
+
10
+ ## [Unreleased]
11
+
12
+ ## [0.2.0-rc.1] - 2026-11-09
13
+
14
+ ### Added
15
+
16
+ - a thing
17
+
18
+ ### Fixed
19
+
20
+ - another
21
+
22
+ ## [0.1.0] - 2026-10-01
23
+
24
+ ### Added
25
+
26
+ - first
27
+ `;
28
+ const TAG = "@acme/app@0.1.0";
29
+ const NOTES = "### Added\n\n- first";
30
+ const FORMAT = "repo release github: name the tag as <name>@<version>, e.g. @workerstack/repo@1.0.0\n";
31
+
32
+ /** A gh whose `release view` finds the release when `exists` is true, or once `create` ran when it is "after-create". */
33
+ function fakeGh(exists: boolean | "after-create", created = 0): { gh: Runner; calls: string[] } {
34
+ const calls: string[] = [];
35
+ let made = false;
36
+ const gh: Runner = (args) => {
37
+ calls.push(args.join(" "));
38
+ if (args[1] === "view") {
39
+ const found = exists === true || (exists === "after-create" && made);
40
+ return { code: found ? 0 : 1, stdout: "", stderr: "" };
41
+ }
42
+ made = true;
43
+ return { code: created, stdout: "", stderr: created === 0 ? "" : "denied" };
44
+ };
45
+ return { gh, calls };
46
+ }
47
+
48
+ async function github(argv: string[], gh: Runner, changelog: string | null = CHANGELOG) {
49
+ const dir = tempDir();
50
+ if (changelog !== null) write(dir, { "CHANGELOG.md": changelog });
51
+ const out: string[] = [];
52
+ const err: string[] = [];
53
+ const code = await releaseGithub(argv, { cwd: dir, stdout: (s) => out.push(s), stderr: (s) => err.push(s), gh });
54
+ return { code, out: out.join(""), err: err.join("") };
55
+ }
56
+
57
+ describe("repo release github", () => {
58
+ it("creates the release from the changelog section of the version", async () => {
59
+ const { gh, calls } = fakeGh(false);
60
+ expect(await github([TAG], gh)).toEqual({
61
+ code: 0,
62
+ out: `repo release github: created release ${TAG} from CHANGELOG.md\n`,
63
+ err: "",
64
+ });
65
+ expect(calls).toEqual([
66
+ `release view ${TAG}`,
67
+ `release create ${TAG} --verify-tag --title ${TAG} --notes ${NOTES}`,
68
+ ]);
69
+ });
70
+
71
+ it("marks a prerelease", async () => {
72
+ const tag = "@acme/app@0.2.0-rc.1";
73
+ const { gh, calls } = fakeGh(false);
74
+ await github([tag], gh);
75
+ const notes = "### Added\n\n- a thing\n\n### Fixed\n\n- another";
76
+ expect(calls[1]).toBe(`release create ${tag} --verify-tag --title ${tag} --notes ${notes} --prerelease`);
77
+ });
78
+ });
79
+
80
+ describe("repo release github reruns", () => {
81
+ it("leaves a release that exists alone", async () => {
82
+ const { gh, calls } = fakeGh(true);
83
+ expect(await github([TAG], gh)).toEqual({
84
+ code: 0,
85
+ out: `repo release github: release ${TAG} already exists\n`,
86
+ err: "",
87
+ });
88
+ expect(calls).toEqual([`release view ${TAG}`]);
89
+ });
90
+
91
+ it("counts a failed create as done when the release exists by then", async () => {
92
+ const { gh } = fakeGh("after-create", 1);
93
+ expect(await github([TAG], gh)).toMatchObject({
94
+ code: 0,
95
+ out: `repo release github: release ${TAG} already exists\n`,
96
+ });
97
+ });
98
+ });
99
+
100
+ describe("repo release github refuses", () => {
101
+ it("a failed create with gh's reason", async () => {
102
+ const { gh } = fakeGh(false, 1);
103
+ expect(await github([TAG], gh)).toEqual({
104
+ code: 1,
105
+ out: "",
106
+ err: "repo release github: gh release create failed: denied\n",
107
+ });
108
+ });
109
+
110
+ it("a changelog without the version's section", async () => {
111
+ const { gh, calls } = fakeGh(false);
112
+ expect(await github(["@acme/app@9.9.9"], gh)).toEqual({
113
+ code: 1,
114
+ out: "",
115
+ err: "repo release github: CHANGELOG.md has no ## [9.9.9] section to make the notes from\n",
116
+ });
117
+ expect(calls).toEqual([]);
118
+ });
119
+
120
+ it("a missing changelog the same way", async () => {
121
+ expect((await github([TAG], fakeGh(false).gh, null)).code).toBe(1);
122
+ });
123
+
124
+ it("a missing or malformed argument with the tag format", async () => {
125
+ expect((await github([], fakeGh(false).gh)).err).toBe(FORMAT);
126
+ expect((await github(["x"], fakeGh(false).gh)).err).toBe(FORMAT);
127
+ });
128
+ });
@@ -0,0 +1,46 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import type { Runner } from "../scripts/ci-wait.ts";
4
+ import { sectionFor, trimBlank } from "./changelog-parse.ts";
5
+ import { refuse } from "./refuse.ts";
6
+ import { CHANGELOG_FILE, GH_RELEASE_CREATE, GH_RELEASE_VIEW } from "./release.constants.ts";
7
+ import {
8
+ GITHUB_PREFIX,
9
+ WAIT_USAGE,
10
+ createFailed,
11
+ noNotes,
12
+ releaseCreated,
13
+ releaseExists,
14
+ } from "./release-messages.constants.ts";
15
+ import { splitSpec } from "./wait.ts";
16
+
17
+ /** Where `repo release github` reads the changelog and prints, and the gh it works through. */
18
+ export type GithubIo = { cwd: string; stdout: (text: string) => void; stderr: (text: string) => void; gh: Runner };
19
+
20
+ /** The text of CHANGELOG.md in `cwd`; empty when it cannot be read. */
21
+ function changelogText(cwd: string): string {
22
+ try {
23
+ return fs.readFileSync(path.join(cwd, CHANGELOG_FILE), "utf8");
24
+ } catch {
25
+ return "";
26
+ }
27
+ }
28
+
29
+ /** `repo release github <name>@<version>`: creates the GitHub release of the tag from its changelog section, once. */
30
+ export function releaseGithub(argv: readonly string[], io: GithubIo): number {
31
+ const tag = argv[0] ?? "";
32
+ const parts = argv.length === 1 ? splitSpec(tag) : null;
33
+ if (parts === null) return refuse(io, GITHUB_PREFIX, WAIT_USAGE);
34
+ const section = sectionFor(changelogText(io.cwd), parts.version);
35
+ if (section === null) return refuse(io, GITHUB_PREFIX, noNotes(parts.version));
36
+ const exists = (): boolean => io.gh(GH_RELEASE_VIEW(tag), io.cwd).code === 0;
37
+ let line = releaseExists(tag);
38
+ if (!exists()) {
39
+ const notes = trimBlank(section.body).join("\n");
40
+ const made = io.gh(GH_RELEASE_CREATE(tag, notes, parts.version.includes("-")), io.cwd);
41
+ if (made.code === 0) line = releaseCreated(tag);
42
+ else if (!exists()) return refuse(io, GITHUB_PREFIX, createFailed(made.stderr.trim()));
43
+ }
44
+ io.stdout(`${GITHUB_PREFIX}${line}\n`);
45
+ return 0;
46
+ }
@@ -0,0 +1,11 @@
1
+ import { afterEach, expect, it } from "vitest";
2
+ import { removePlanted, tempDir, write } from "../fixtures/planted-repo.ts";
3
+ import { listDir } from "./list-dir.ts";
4
+
5
+ afterEach(removePlanted);
6
+
7
+ it("lists the names in a directory", () => {
8
+ const dir = tempDir();
9
+ write(dir, { "a.tgz": "x", "b.txt": "y" });
10
+ expect(listDir(dir).sort()).toEqual(["a.tgz", "b.txt"]);
11
+ });
@@ -0,0 +1,6 @@
1
+ import fs from "node:fs";
2
+
3
+ /** The names in a directory. */
4
+ export function listDir(dir: string): string[] {
5
+ return fs.readdirSync(dir);
6
+ }
@@ -0,0 +1,48 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { main } from "./main.ts";
3
+
4
+ const writeErr = process.stderr.write;
5
+ const writeOut = process.stdout.write;
6
+ afterEach(() => {
7
+ process.stderr.write = writeErr;
8
+ process.stdout.write = writeOut;
9
+ });
10
+
11
+ /** Runs `main` with stdout and stderr captured. */
12
+ async function run(argv: string[]) {
13
+ const out: string[] = [];
14
+ const err: string[] = [];
15
+ const into =
16
+ (lines: string[]) =>
17
+ (text: string): boolean => (lines.push(text), true);
18
+ process.stdout.write = into(out) /* the code writes strings only */ as typeof process.stdout.write;
19
+ process.stderr.write = into(err) /* the code writes strings only */ as typeof process.stderr.write;
20
+ const code = await main(argv);
21
+ return { code, out: out.join(""), err: err.join("") };
22
+ }
23
+
24
+ const TAG_FORMAT = "name the tag as <name>@<version>, e.g. @workerstack/repo@1.0.0\n";
25
+ const USAGE = "usage: repo release [--stable | --pre <id>] | wait | gate | pack | publish | github <name>@<version>\n";
26
+
27
+ describe("repo release", () => {
28
+ it("prints the usage for help", async () => {
29
+ expect(await run(["help"])).toEqual({ code: 0, out: USAGE, err: "" });
30
+ });
31
+
32
+ it("sends `wait` to the npm poll", async () => {
33
+ expect(await run(["wait"])).toEqual({ code: 1, out: "", err: `repo release wait: ${TAG_FORMAT}` });
34
+ });
35
+
36
+ it.each(["gate", "publish", "github"])("sends `%s` to its command", async (command) => {
37
+ expect(await run([command])).toEqual({ code: 1, out: "", err: `repo release ${command}: ${TAG_FORMAT}` });
38
+ });
39
+
40
+ it("sends `pack` to the packer", async () => {
41
+ const got = await run(["pack", "extra"]);
42
+ expect(got.err).toBe("repo release pack: takes no arguments; it packs the package in this checkout\n");
43
+ });
44
+
45
+ it("sends anything else to the release", async () => {
46
+ expect(await run(["--bogus"])).toEqual({ code: 1, out: "", err: `repo release: ${USAGE}` });
47
+ });
48
+ });
@@ -0,0 +1,51 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import { readEnv } from "../env.ts";
4
+ import { inheritRunner, realSleep, spawnRunner } from "../scripts/ci-wait-run.ts";
5
+ import { gate } from "./gate.ts";
6
+ import { releaseGithub } from "./github.ts";
7
+ import { listDir } from "./list-dir.ts";
8
+ import { releasePack } from "./pack.ts";
9
+ import { releasePublish } from "./publish.ts";
10
+ import {
11
+ GATE_COMMAND,
12
+ GITHUB_ACTIONS_ON,
13
+ GITHUB_COMMAND,
14
+ HELP_COMMAND,
15
+ PACK_COMMAND,
16
+ PUBLISH_COMMAND,
17
+ WAIT_COMMAND,
18
+ } from "./release.constants.ts";
19
+ import { releaseMain } from "./release.ts";
20
+ import { USAGE } from "./release-messages.constants.ts";
21
+ import { releaseWait } from "./wait.ts";
22
+
23
+ const packTools = () => ({
24
+ bun: spawnRunner("bun"),
25
+ tar: spawnRunner("tar"),
26
+ tmp: os.tmpdir(),
27
+ mkdtemp: fs.mkdtempSync,
28
+ list: listDir,
29
+ });
30
+
31
+ /** `repo release [--stable | --pre <id>]`, or one of `help`, `wait`, `gate`, `pack`, `publish` and `github`. */
32
+ export function main(argv: string[]): Promise<number> {
33
+ const io = {
34
+ stdout: process.stdout.write.bind(process.stdout),
35
+ stderr: process.stderr.write.bind(process.stderr),
36
+ now: Date.now,
37
+ sleep: realSleep,
38
+ };
39
+ const run = { ...io, cwd: process.cwd(), git: spawnRunner("git"), gh: spawnRunner("gh") };
40
+ const rest = argv.slice(1);
41
+ const ci = readEnv().GITHUB_ACTIONS === GITHUB_ACTIONS_ON;
42
+ if (argv[0] === HELP_COMMAND) return (io.stdout(`${USAGE}\n`), Promise.resolve(0));
43
+ if (argv[0] === WAIT_COMMAND) return releaseWait(rest, { ...io, fetch });
44
+ if (argv[0] === GATE_COMMAND) return gate(rest, run);
45
+ if (argv[0] === PACK_COMMAND) return Promise.resolve(releasePack(rest, { ...run, ...packTools() }));
46
+ if (argv[0] === GITHUB_COMMAND) return Promise.resolve(releaseGithub(rest, run));
47
+ if (argv[0] === PUBLISH_COMMAND) {
48
+ return releasePublish(rest, { ...run, ...packTools(), npm: inheritRunner("npm"), fetch, ci });
49
+ }
50
+ return releaseMain(argv, run);
51
+ }
@@ -0,0 +1,24 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { removePlanted, tempDir, write } from "../fixtures/planted-repo.ts";
3
+ import { parseManifest, readManifest } from "./manifest.ts";
4
+
5
+ afterEach(removePlanted);
6
+
7
+ describe("the manifest", () => {
8
+ it("is parsed from text, private unless it says otherwise", () => {
9
+ expect(parseManifest('{"name":"a","version":"1.0.0"}')).toEqual({ name: "a", version: "1.0.0", private: false });
10
+ expect(parseManifest('{"name":"a","version":"1.0.0","private":true}')?.private).toBe(true);
11
+ });
12
+
13
+ it("is null for text that is not a manifest", () => {
14
+ expect(parseManifest("not json")).toBeNull();
15
+ expect(parseManifest('{"name":"a"}')).toBeNull();
16
+ });
17
+
18
+ it("is read from the root's package.json, null when there is none", () => {
19
+ const dir = tempDir();
20
+ expect(readManifest(dir)).toBeNull();
21
+ write(dir, { "package.json": { name: "a", version: "2.0.0" } });
22
+ expect(readManifest(dir)).toEqual({ name: "a", version: "2.0.0", private: false });
23
+ });
24
+ });
@@ -0,0 +1,34 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { z } from "zod";
4
+ import { MANIFEST_FILE, VERSION_LINE } from "./release.constants.ts";
5
+
6
+ /** The fields of the root package.json a release reads. */
7
+ export type Manifest = { name: string; version: string; private: boolean };
8
+
9
+ const schema = () => z.object({ name: z.string(), version: z.string(), private: z.boolean().optional() });
10
+
11
+ /** The manifest in `text`; null when it is not JSON or lacks a name or version. */
12
+ export function parseManifest(text: string): Manifest | null {
13
+ try {
14
+ const parsed = schema().safeParse(JSON.parse(text));
15
+ return parsed.success ? { ...parsed.data, private: parsed.data.private === true } : null;
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+
21
+ /** The manifest at `root`; null when it is missing, not JSON or lacks a name or version. */
22
+ export function readManifest(root: string): Manifest | null {
23
+ try {
24
+ return parseManifest(fs.readFileSync(path.join(root, MANIFEST_FILE), "utf8"));
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+
30
+ /** Rewrites the `version` line of the manifest at `root`, and nothing else in the file. */
31
+ export function writeVersion(root: string, version: string): void {
32
+ const file = path.join(root, MANIFEST_FILE);
33
+ fs.writeFileSync(file, fs.readFileSync(file, "utf8").replace(VERSION_LINE, `$1"${version}"`));
34
+ }
@@ -0,0 +1,107 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+ import { removePlanted, tempDir, write } from "../fixtures/planted-repo.ts";
5
+ import { spawnRunner } from "../scripts/ci-wait-run.ts";
6
+ import type { Runner } from "../scripts/ci-wait.ts";
7
+ import { releasePack } from "./pack.ts";
8
+
9
+ afterEach(removePlanted);
10
+
11
+ const ok = (stdout: string) => ({ code: 0, stdout, stderr: "" });
12
+
13
+ /** Runs `release pack` in `cwd`; `bun` and `tar` default to the real tools. */
14
+ type Tools = { bun?: Runner; tar?: Runner; mkdtemp?: (prefix: string) => string; list?: (dir: string) => string[] };
15
+
16
+ async function pack(cwd: string, tools: Tools = {}, argv: string[] = []) {
17
+ const out: string[] = [];
18
+ const err: string[] = [];
19
+ const tmp = tempDir();
20
+ const code = await releasePack(argv, {
21
+ cwd,
22
+ stdout: (s) => out.push(s),
23
+ stderr: (s) => err.push(s),
24
+ bun: tools.bun ?? spawnRunner("bun"),
25
+ tar: tools.tar ?? spawnRunner("tar"),
26
+ tmp,
27
+ mkdtemp: tools.mkdtemp ?? fs.mkdtempSync,
28
+ list: tools.list ?? ((dir) => fs.readdirSync(dir)),
29
+ });
30
+ return { code, out: out.join(""), err: err.join(""), tmp };
31
+ }
32
+
33
+ /** Fakes bun's packer to name a tarball and tar to show `manifest` as its package.json. */
34
+ const fakes = (manifest: unknown, stdout = ""): Tools => ({
35
+ bun: () => ok(stdout),
36
+ tar: () => ok(JSON.stringify(manifest)),
37
+ mkdtemp: (prefix) => `${prefix}x1`,
38
+ list: () => ["acme-app-0.1.0.tgz"],
39
+ });
40
+
41
+ describe("repo release pack", () => {
42
+ it("packs the package and prints the tarball path", async () => {
43
+ const dir = tempDir();
44
+ write(dir, { "package.json": { name: "@acme/app", version: "0.1.0" }, "index.js": "x\n" });
45
+ const got = await pack(dir);
46
+ expect(got.code).toBe(0);
47
+ const file = got.out.trim();
48
+ expect(got.err).toBe(`${file}\n`);
49
+ expect(path.basename(file)).toBe("acme-app-0.1.0.tgz");
50
+ expect(path.dirname(path.dirname(file))).toBe(got.tmp);
51
+ expect(fs.existsSync(file)).toBe(true);
52
+ });
53
+
54
+ it("takes the path from the pack directory and shows the prepack output on stderr", async () => {
55
+ const prepack = "$ bun run \x62uild\nrepo sync: up to date\n";
56
+ const got = await pack("/repo", fakes({ name: "@acme/app", version: "0.1.0" }, prepack));
57
+ const file = path.join(got.tmp, "repo-pack-x1", "acme-app-0.1.0.tgz");
58
+ expect(got).toMatchObject({ code: 0, out: `${file}\n`, err: prepack });
59
+ });
60
+ });
61
+
62
+ describe("repo release pack refuses", () => {
63
+ it.each([
64
+ ["dependencies", "workspace:^", "@workerstack/x"],
65
+ ["peerDependencies", "catalog:", "react"],
66
+ ["optionalDependencies", "workspace:*", "y"],
67
+ ["devDependencies", "catalog:default", "z"],
68
+ ])("refuses a packed %s entry naming %s", async (field, spec, dep) => {
69
+ const got = await pack("/repo", fakes({ name: "@acme/app", version: "0.1.0", [field]: { [dep]: spec } }));
70
+ expect(got).toMatchObject({
71
+ code: 1,
72
+ out: "",
73
+ err: `repo release pack: the packed package.json names ${spec} for ${dep}; pin it before releasing\n`,
74
+ });
75
+ });
76
+ });
77
+
78
+ describe("repo release pack refuses what it cannot pack", () => {
79
+ it("when bun cannot pack", async () => {
80
+ const got = await pack("/repo", { bun: () => ({ code: 1, stdout: "", stderr: "no package.json\n" }) });
81
+ expect(got).toMatchObject({ code: 1, out: "", err: "repo release pack: bun pm pack failed: no package.json\n" });
82
+ });
83
+
84
+ it.each([[[]], [["a.tgz", "b.tgz"]]])("a pack directory holding %j instead of one tarball", async (names) => {
85
+ const got = await pack("/repo", { ...fakes({}), list: () => [...names, "notes.txt"] });
86
+ expect(got.err).toMatch(
87
+ new RegExp(
88
+ `^repo release pack: bun pm pack left ${names.length} tarballs in .*repo-pack-x1; expected exactly one\\n$`,
89
+ ),
90
+ );
91
+ });
92
+
93
+ it("a tarball whose package.json cannot be read", async () => {
94
+ const got = await pack("/repo", { ...fakes({}), tar: () => ok("not json") });
95
+ expect(got).toMatchObject({
96
+ code: 1,
97
+ out: "",
98
+ err: "repo release pack: the packed tarball has no readable package.json with a name and a version\n",
99
+ });
100
+ });
101
+
102
+ it("arguments with the usage line", async () => {
103
+ expect((await pack("/repo", {}, ["x"])).err).toBe(
104
+ "repo release pack: takes no arguments; it packs the package in this checkout\n",
105
+ );
106
+ });
107
+ });