forge-workflow 0.0.9 → 0.1.0-beta.2

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 (479) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +3 -0
  5. package/.forge/hooks/forge-native-hook.js +245 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -106
  10. package/QUICKSTART.md +171 -0
  11. package/README.md +271 -363
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +466 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +118 -0
  29. package/docs/guides/SUPPORT.md +185 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +205 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/reference/TOOLCHAIN.md +658 -0
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +115 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/adapter-cli.js +307 -0
  67. package/lib/adapters/beads-issue-adapter.js +127 -0
  68. package/lib/adapters/beads-kernel-compat.js +1042 -0
  69. package/lib/adapters/greptile-review-adapter.js +141 -0
  70. package/lib/adapters/kernel-issue-adapter.js +101 -0
  71. package/lib/adapters/pr-state-adapter.js +484 -0
  72. package/lib/adoption-profiles.js +126 -0
  73. package/lib/agents/README.md +2 -6
  74. package/lib/agents/claude.plugin.json +3 -8
  75. package/lib/agents/codex.plugin.json +9 -1
  76. package/lib/agents/cursor.plugin.json +2 -6
  77. package/lib/agents/hermes.plugin.json +22 -0
  78. package/lib/agents-config.js +39 -1236
  79. package/lib/audit-evidence.js +282 -0
  80. package/lib/beads-setup.js +225 -28
  81. package/lib/beads-sync-scaffold.js +36 -107
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +744 -70
  84. package/lib/commands/_manifest.js +91 -0
  85. package/lib/commands/_registry.js +85 -34
  86. package/lib/commands/_resolve-command-opts.js +261 -0
  87. package/lib/commands/_serve-security.js +270 -0
  88. package/lib/commands/adapter.js +12 -0
  89. package/lib/commands/add.js +118 -0
  90. package/lib/commands/audit.js +70 -0
  91. package/lib/commands/blocked.js +5 -0
  92. package/lib/commands/board.js +64 -0
  93. package/lib/commands/claim.js +21 -2
  94. package/lib/commands/claims.js +7 -0
  95. package/lib/commands/clean.js +485 -75
  96. package/lib/commands/close.js +2 -2
  97. package/lib/commands/comment.js +5 -0
  98. package/lib/commands/control.js +148 -0
  99. package/lib/commands/create.js +2 -2
  100. package/lib/commands/dev.js +185 -7
  101. package/lib/commands/doc-gate.js +336 -0
  102. package/lib/commands/doctor.js +156 -0
  103. package/lib/commands/explain.js +15 -0
  104. package/lib/commands/export.js +237 -0
  105. package/lib/commands/gate.js +192 -0
  106. package/lib/commands/hooks.js +242 -0
  107. package/lib/commands/inbox.js +118 -0
  108. package/lib/commands/init.js +598 -0
  109. package/lib/commands/insights.js +79 -0
  110. package/lib/commands/issue.js +12 -1
  111. package/lib/commands/issues.js +66 -0
  112. package/lib/commands/lint.js +5 -0
  113. package/lib/commands/list.js +2 -2
  114. package/lib/commands/merge.js +312 -0
  115. package/lib/commands/migrate.js +523 -0
  116. package/lib/commands/new.js +12 -0
  117. package/lib/commands/options.js +241 -0
  118. package/lib/commands/orient.js +13 -0
  119. package/lib/commands/orphans.js +5 -0
  120. package/lib/commands/patch.js +67 -0
  121. package/lib/commands/plan.js +436 -24
  122. package/lib/commands/preflight.js +211 -0
  123. package/lib/commands/prime.js +13 -0
  124. package/lib/commands/push.js +69 -2
  125. package/lib/commands/ready.js +2 -2
  126. package/lib/commands/recall.js +116 -0
  127. package/lib/commands/recap.js +61 -0
  128. package/lib/commands/recommend.js +22 -2
  129. package/lib/commands/release.js +91 -0
  130. package/lib/commands/remember.js +74 -0
  131. package/lib/commands/role.js +99 -0
  132. package/lib/commands/serve.js +581 -0
  133. package/lib/commands/setup.js +851 -979
  134. package/lib/commands/shepherd.js +436 -0
  135. package/lib/commands/ship.js +23 -1
  136. package/lib/commands/show.js +2 -2
  137. package/lib/commands/stage.js +192 -0
  138. package/lib/commands/stale.js +5 -0
  139. package/lib/commands/status.js +329 -11
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +15 -2
  142. package/lib/commands/test.js +58 -7
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +56 -25
  146. package/lib/commands/worktree.js +308 -128
  147. package/lib/config-writer.js +202 -0
  148. package/lib/control-plane.js +236 -0
  149. package/lib/core/runtime-graph.js +946 -0
  150. package/lib/dep-guard/keyword-ripple.js +184 -0
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +42 -17
  154. package/lib/doc-gate/declaration.js +177 -0
  155. package/lib/doc-gate/detect.js +289 -0
  156. package/lib/doc-gate/gate.js +375 -0
  157. package/lib/doc-gate/okf-config.js +128 -0
  158. package/lib/doc-gate/okf.js +429 -0
  159. package/lib/docs-command.js +1161 -6
  160. package/lib/forge-issues.js +697 -0
  161. package/lib/forge-lock.js +262 -0
  162. package/lib/gate-events.js +193 -0
  163. package/lib/global-flags.js +74 -0
  164. package/lib/greptile-match.js +7 -63
  165. package/lib/harness-capability-matrix.js +380 -0
  166. package/lib/hook-global-installer.js +347 -0
  167. package/lib/hook-renderer.js +451 -0
  168. package/lib/inbox.js +391 -0
  169. package/lib/insights.js +397 -0
  170. package/lib/issue-adapter.js +156 -0
  171. package/lib/issue-backend.js +145 -0
  172. package/lib/issue-render.js +220 -0
  173. package/lib/issue-sync/authority.js +100 -0
  174. package/lib/issue-sync/github-pull.js +184 -0
  175. package/lib/issue-sync/import-primitives.js +98 -0
  176. package/lib/issue-sync/legacy-link-bridge.js +436 -0
  177. package/lib/issue-sync/link-store.js +292 -0
  178. package/lib/issue-sync/project-github.js +123 -0
  179. package/lib/issue-sync/reconcile.js +195 -0
  180. package/lib/issue-sync/schema.js +126 -0
  181. package/lib/kernel/backing-issue.js +305 -0
  182. package/lib/kernel/broker.js +1218 -0
  183. package/lib/kernel/cli-broker-factory.js +130 -0
  184. package/lib/kernel/conflict-signal.js +82 -0
  185. package/lib/kernel/evaluators.js +195 -0
  186. package/lib/kernel/fs-class.js +495 -0
  187. package/lib/kernel/issue-command-contract.js +559 -0
  188. package/lib/kernel/issue-id-resolver.js +186 -0
  189. package/lib/kernel/lease-enforcer.js +158 -0
  190. package/lib/kernel/migrations.js +333 -0
  191. package/lib/kernel/planning-buckets-schema.js +109 -0
  192. package/lib/kernel/projection-jsonl-writer.js +450 -0
  193. package/lib/kernel/readiness-model.js +329 -0
  194. package/lib/kernel/schema.js +356 -0
  195. package/lib/kernel/sqlite-driver.js +2504 -0
  196. package/lib/kernel/taxonomy-validator.js +394 -0
  197. package/lib/lefthook-check.js +8 -4
  198. package/lib/lefthook-wiring.js +413 -0
  199. package/lib/mcp-config-renderer.js +288 -0
  200. package/lib/memory/graphiti-mcp.js +106 -0
  201. package/lib/memory/router.js +387 -0
  202. package/lib/memory/typed-api.js +102 -0
  203. package/lib/memory-digest.js +195 -0
  204. package/lib/merge-rules.js +395 -0
  205. package/lib/migrate-dry-run.js +466 -0
  206. package/lib/orientation.js +863 -0
  207. package/lib/package-manager-remediation.js +103 -0
  208. package/lib/package-root.js +381 -0
  209. package/lib/patch-intent.js +890 -0
  210. package/lib/plugin-catalog.js +3 -4
  211. package/lib/plugin-manager.js +0 -5
  212. package/lib/pr-bundle.js +186 -0
  213. package/lib/pr-monitor/differ.js +195 -0
  214. package/lib/pr-monitor/events.js +0 -0
  215. package/lib/pr-monitor/gather.js +124 -0
  216. package/lib/pr-monitor/journal.js +299 -0
  217. package/lib/pr-monitor/monitor.js +146 -0
  218. package/lib/pr-monitor/render-sticky.js +157 -0
  219. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  220. package/lib/pr-monitor/watch.js +247 -0
  221. package/lib/pr-pull.js +1273 -0
  222. package/lib/pr-shepherd.js +494 -0
  223. package/lib/pr-state-validator.js +59 -0
  224. package/lib/preflight/gates.js +237 -0
  225. package/lib/preflight/runner.js +116 -0
  226. package/lib/project-discovery.js +0 -53
  227. package/lib/project-memory.js +166 -0
  228. package/lib/protected-path-manifest.js +281 -0
  229. package/lib/protected-state-surfaces.js +387 -0
  230. package/lib/release-readiness.js +2089 -0
  231. package/lib/reset.js +59 -45
  232. package/lib/review-adapter.js +68 -0
  233. package/lib/rules-sync.js +260 -0
  234. package/lib/runtime-health.js +332 -23
  235. package/lib/safety-config-renderer.js +268 -0
  236. package/lib/setup-action-log.js +1 -7
  237. package/lib/setup.js +27 -65
  238. package/lib/shell-utils.js +76 -6
  239. package/lib/skills-sync.js +330 -0
  240. package/lib/smart-status/conflicts.js +205 -0
  241. package/lib/smart-status/scoring.js +191 -0
  242. package/lib/status/beads-snapshot.js +145 -0
  243. package/lib/status/presenter.js +216 -0
  244. package/lib/status/snapshot.js +186 -0
  245. package/lib/sync-backend.js +202 -0
  246. package/lib/untrusted-content.js +52 -0
  247. package/lib/upgrade-safety.js +199 -0
  248. package/lib/workflow/enforce-stage.js +298 -47
  249. package/lib/workflow/stage-transition.js +115 -0
  250. package/lib/workflow/stages.js +30 -6
  251. package/lib/workflow/state-manager.js +159 -14
  252. package/lib/workflow/state.js +23 -1
  253. package/lib/workflow-profiles.js +17 -5
  254. package/package.json +46 -36
  255. package/rules/documentation.md +19 -0
  256. package/rules/kernel-tracking.md +26 -0
  257. package/rules/security.md +22 -0
  258. package/rules/tdd.md +20 -0
  259. package/rules/workflow.md +27 -0
  260. package/scripts/auto-backing-issue.js +47 -0
  261. package/scripts/beads-context.sh +165 -22
  262. package/scripts/beads-migrate-to-dolt.sh +7 -0
  263. package/scripts/beads-upgrade-smoke.sh +284 -0
  264. package/scripts/behavioral-judge.sh +115 -11
  265. package/scripts/benchmark.js +349 -63
  266. package/scripts/bootstrap-windows-tools.sh +78 -0
  267. package/scripts/branch-protection.js +2 -3
  268. package/scripts/check-agents.js +34 -137
  269. package/scripts/commitlint.js +3 -1
  270. package/scripts/conflict-detect.sh +3 -0
  271. package/scripts/dep-guard-analyze.js +52 -17
  272. package/scripts/dep-guard-keyword-ripple.js +29 -0
  273. package/scripts/dep-guard-render-review.js +86 -0
  274. package/scripts/dep-guard.sh +64 -232
  275. package/scripts/file-index.sh +3 -0
  276. package/scripts/forge-team/lib/claim.sh +34 -18
  277. package/scripts/forge-team/lib/dashboard.sh +61 -86
  278. package/scripts/forge-team/lib/epic.sh +99 -263
  279. package/scripts/forge-team/lib/hooks.sh +26 -28
  280. package/scripts/forge-team/lib/identity.sh +4 -4
  281. package/scripts/forge-team/lib/sync-github.sh +144 -47
  282. package/scripts/forge-team/lib/verify.sh +93 -83
  283. package/scripts/forge-team/lib/workload.sh +41 -65
  284. package/scripts/forge-team/tests/claim.test.sh +25 -19
  285. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  286. package/scripts/forge-team/tests/epic.test.sh +52 -71
  287. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  288. package/scripts/forge-team/tests/identity.test.sh +3 -3
  289. package/scripts/forge-team/tests/integration.test.sh +44 -66
  290. package/scripts/forge-team/tests/sync-github.test.sh +183 -79
  291. package/scripts/forge-team/tests/verify.test.sh +37 -46
  292. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  293. package/scripts/forge-team/tests/workload.test.sh +32 -66
  294. package/scripts/gen-command-manifest.js +153 -0
  295. package/scripts/gen-embedded-assets.mjs +129 -0
  296. package/scripts/install.ps1 +139 -0
  297. package/scripts/install.sh +268 -0
  298. package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
  299. package/scripts/lib/release-asset.mjs +84 -0
  300. package/scripts/parity-check.mjs +145 -0
  301. package/scripts/parity-check.test.mjs +58 -0
  302. package/scripts/pin-agentic-workflow-images.js +112 -0
  303. package/scripts/pr-coordinator.sh +3 -0
  304. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  305. package/scripts/preflight.sh +108 -0
  306. package/scripts/protected-state-check.js +104 -0
  307. package/scripts/smart-status-score.js +31 -0
  308. package/scripts/smart-status-sessions.js +51 -0
  309. package/scripts/smart-status.sh +117 -369
  310. package/scripts/spikes/config-race-bench.js +111 -0
  311. package/scripts/spikes/harness-capability-matrix.js +13 -0
  312. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  313. package/scripts/spikes/protected-path-manifest.js +20 -0
  314. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  315. package/scripts/sync-agent-skills.js +62 -0
  316. package/scripts/sync-agentic-workflow.js +48 -0
  317. package/scripts/sync-utils.sh +3 -0
  318. package/scripts/test-ci-shard.js +251 -0
  319. package/scripts/test-dashboard.js +188 -52
  320. package/scripts/test-full-suite.js +186 -0
  321. package/scripts/test-profile.js +278 -0
  322. package/scripts/test.js +302 -28
  323. package/scripts/validate.js +143 -0
  324. package/scripts/validate.sh +18 -1
  325. package/skills/claim-safety/SKILL.md +102 -0
  326. package/skills/claim-safety/evals/evals.json +46 -0
  327. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
  328. package/skills/dev/evals/evals.json +50 -0
  329. package/skills/hermes-forge/SKILL.md +185 -0
  330. package/skills/hermes-forge/evals/evals.json +46 -0
  331. package/skills/issue-basics/SKILL.md +111 -0
  332. package/skills/issue-basics/evals/evals.json +46 -0
  333. package/skills/kernel/SKILL.md +166 -0
  334. package/skills/kernel/evals/evals.json +50 -0
  335. package/skills/memory/SKILL.md +102 -0
  336. package/skills/parallel-deep-research/SKILL.md +14 -11
  337. package/skills/parallel-deep-research/evals/evals.json +11 -27
  338. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
  339. package/skills/plan/evals/evals.json +42 -0
  340. package/skills/research/SKILL.md +195 -0
  341. package/skills/research/evals/evals.json +42 -0
  342. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  343. package/skills/review/evals/evals.json +42 -0
  344. package/skills/rollback/SKILL.md +110 -0
  345. package/skills/rollback/evals/evals.json +46 -0
  346. package/skills/rollback/references/methods.md +204 -0
  347. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  348. package/skills/shepherd/SKILL.md +66 -0
  349. package/skills/shepherd/evals/evals.json +42 -0
  350. package/skills/ship/SKILL.md +251 -0
  351. package/skills/ship/evals/evals.json +42 -0
  352. package/skills/smith/SKILL.md +142 -0
  353. package/skills/smith/evals/evals.json +46 -0
  354. package/skills/smith/references/autonomy-and-gates.md +94 -0
  355. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  356. package/skills/sonarcloud/evals/evals.json +46 -0
  357. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  358. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  359. package/skills/status/SKILL.md +102 -0
  360. package/skills/status/evals/evals.json +50 -0
  361. package/skills/triage-ready/SKILL.md +121 -0
  362. package/skills/triage-ready/evals/evals.json +42 -0
  363. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  364. package/skills/validate/evals/evals.json +42 -0
  365. package/skills/verify/SKILL.md +299 -0
  366. package/skills/verify/evals/evals.json +50 -0
  367. package/.claude/commands/dev.md +0 -345
  368. package/.claude/commands/plan.md +0 -566
  369. package/.claude/commands/premerge.md +0 -186
  370. package/.claude/commands/research.md +0 -42
  371. package/.claude/commands/review.md +0 -451
  372. package/.claude/commands/rollback.md +0 -721
  373. package/.claude/commands/ship.md +0 -213
  374. package/.claude/commands/sonarcloud.md +0 -152
  375. package/.claude/commands/status.md +0 -90
  376. package/.claude/commands/validate.md +0 -288
  377. package/.claude/commands/verify.md +0 -269
  378. package/.claude/rules/workflow.md +0 -121
  379. package/.cline/workflows/dev.md +0 -342
  380. package/.cline/workflows/plan.md +0 -563
  381. package/.cline/workflows/premerge.md +0 -183
  382. package/.cline/workflows/research.md +0 -39
  383. package/.cline/workflows/review.md +0 -448
  384. package/.cline/workflows/rollback.md +0 -718
  385. package/.cline/workflows/ship.md +0 -210
  386. package/.cline/workflows/sonarcloud.md +0 -146
  387. package/.cline/workflows/status.md +0 -87
  388. package/.cline/workflows/validate.md +0 -285
  389. package/.cline/workflows/verify.md +0 -266
  390. package/.codex/config.toml +0 -11
  391. package/.codex/skills/dev/SKILL.md +0 -345
  392. package/.codex/skills/plan/SKILL.md +0 -566
  393. package/.codex/skills/premerge/SKILL.md +0 -186
  394. package/.codex/skills/research/SKILL.md +0 -42
  395. package/.codex/skills/review/SKILL.md +0 -451
  396. package/.codex/skills/rollback/SKILL.md +0 -721
  397. package/.codex/skills/ship/SKILL.md +0 -213
  398. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  399. package/.codex/skills/status/SKILL.md +0 -90
  400. package/.codex/skills/validate/SKILL.md +0 -288
  401. package/.codex/skills/verify/SKILL.md +0 -269
  402. package/.cursor/commands/dev.md +0 -342
  403. package/.cursor/commands/plan.md +0 -563
  404. package/.cursor/commands/premerge.md +0 -183
  405. package/.cursor/commands/research.md +0 -39
  406. package/.cursor/commands/review.md +0 -448
  407. package/.cursor/commands/ship.md +0 -210
  408. package/.cursor/commands/sonarcloud.md +0 -146
  409. package/.cursor/commands/status.md +0 -87
  410. package/.cursor/commands/validate.md +0 -285
  411. package/.cursor/commands/verify.md +0 -266
  412. package/.cursorrules +0 -149
  413. package/.github/prompts/premerge.prompt.md +0 -188
  414. package/.github/prompts/research.prompt.md +0 -44
  415. package/.github/prompts/rollback.prompt.md +0 -723
  416. package/.github/prompts/ship.prompt.md +0 -215
  417. package/.github/prompts/status.prompt.md +0 -92
  418. package/.github/prompts/verify.prompt.md +0 -271
  419. package/.github/workflows/beads-to-github.yml +0 -56
  420. package/.github/workflows/github-to-beads.yml +0 -97
  421. package/.kilocode/workflows/dev.md +0 -346
  422. package/.kilocode/workflows/plan.md +0 -567
  423. package/.kilocode/workflows/premerge.md +0 -187
  424. package/.kilocode/workflows/research.md +0 -43
  425. package/.kilocode/workflows/review.md +0 -452
  426. package/.kilocode/workflows/rollback.md +0 -722
  427. package/.kilocode/workflows/ship.md +0 -214
  428. package/.kilocode/workflows/sonarcloud.md +0 -150
  429. package/.kilocode/workflows/status.md +0 -91
  430. package/.kilocode/workflows/validate.md +0 -289
  431. package/.kilocode/workflows/verify.md +0 -270
  432. package/.opencode/commands/dev.md +0 -345
  433. package/.opencode/commands/plan.md +0 -566
  434. package/.opencode/commands/premerge.md +0 -186
  435. package/.opencode/commands/research.md +0 -42
  436. package/.opencode/commands/review.md +0 -451
  437. package/.opencode/commands/rollback.md +0 -721
  438. package/.opencode/commands/ship.md +0 -213
  439. package/.opencode/commands/sonarcloud.md +0 -149
  440. package/.opencode/commands/status.md +0 -90
  441. package/.opencode/commands/validate.md +0 -288
  442. package/.opencode/commands/verify.md +0 -269
  443. package/.roo/commands/dev.md +0 -346
  444. package/.roo/commands/plan.md +0 -567
  445. package/.roo/commands/premerge.md +0 -187
  446. package/.roo/commands/research.md +0 -43
  447. package/.roo/commands/review.md +0 -452
  448. package/.roo/commands/rollback.md +0 -722
  449. package/.roo/commands/ship.md +0 -214
  450. package/.roo/commands/sonarcloud.md +0 -150
  451. package/.roo/commands/status.md +0 -91
  452. package/.roo/commands/validate.md +0 -289
  453. package/.roo/commands/verify.md +0 -270
  454. package/docs/BEADS_GITHUB_SYNC.md +0 -255
  455. package/docs/GREPTILE_SETUP.md +0 -400
  456. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  457. package/docs/SETUP.md +0 -663
  458. package/docs/VALIDATION.md +0 -363
  459. package/lib/agents/cline.plugin.json +0 -29
  460. package/lib/agents/copilot.plugin.json +0 -24
  461. package/lib/agents/kilocode.plugin.json +0 -22
  462. package/lib/agents/opencode.plugin.json +0 -23
  463. package/lib/agents/roo.plugin.json +0 -30
  464. package/lib/beads-health-check.js +0 -143
  465. package/lib/commands/commands-reset.js +0 -147
  466. package/opencode.json +0 -67
  467. package/scripts/beads-context.test.js +0 -567
  468. package/scripts/github-beads-sync/comment.mjs +0 -64
  469. package/scripts/github-beads-sync/config.mjs +0 -148
  470. package/scripts/github-beads-sync/github-api.mjs +0 -131
  471. package/scripts/github-beads-sync/index.mjs +0 -332
  472. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  473. package/scripts/github-beads-sync/mapping.mjs +0 -78
  474. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  475. package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
  476. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  477. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  478. package/scripts/github-beads-sync.config.json +0 -26
  479. package/scripts/sync-commands.js +0 -600
@@ -7,7 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
7
7
  TEST_TMP="$(mktemp -d)"
8
8
  trap 'rm -rf "$TEST_TMP"' EXIT
9
9
 
10
- # Override TEAM_MAP_ROOT so tests don't touch real .beads/
10
+ # Override TEAM_MAP_ROOT so tests don't touch real .forge/
11
11
  export TEAM_MAP_ROOT="$TEST_TMP"
12
12
 
13
13
  # Fix "now" so stale detection is deterministic
@@ -27,75 +27,41 @@ esac
27
27
  MOCK
28
28
  chmod +x "$mock_dir/gh"
29
29
 
30
- # ── Create mock bd ───────────────────────────────────────────────────────
31
- cat > "$mock_dir/bd" << 'MOCK'
30
+ # ── Create mock forge ─────────────────────────────────────────────────────
31
+ # Emits Kernel-shaped JSON ({"data":{"issues":[...]}}) for `issue list`.
32
+ cat > "$mock_dir/forge" << 'MOCK'
32
33
  #!/usr/bin/env bash
33
- case "$1 $2" in
34
- "list --status=open,in_progress")
35
- exit 1
36
- ;;
37
- "list --status=open")
38
- echo "forge-bbb · Feature B"
39
- ;;
40
- "list --status=in_progress")
41
- echo "◐ forge-aaa · Feature A"
42
- echo "forge-ccc · Feature C"
43
- echo "◐ forge-m1n8.6 · Sub-feature X (dotted ID)"
44
- ;;
45
- "show forge-aaa")
46
- echo " forge-aaa · Feature A [● P2 · IN_PROGRESS]"
47
- echo "Owner: devone"
48
- echo "Updated: 2026-03-27T10:00:00Z"
49
- ;;
50
- "show forge-bbb")
51
- echo "○ forge-bbb · Feature B [● P2 · OPEN]"
52
- echo "Owner: devtwo"
53
- echo "Updated: 2026-03-25T10:00:00Z"
54
- ;;
55
- "show forge-ccc")
56
- echo "◐ forge-ccc · Feature C [● P1 · IN_PROGRESS]"
57
- echo "Owner: devone"
58
- echo "Updated: 2026-03-27T09:00:00Z"
59
- echo ""
60
- echo "DEPENDS ON"
61
- echo " → forge-aaa: Feature A"
62
- ;;
63
- "show forge-m1n8.6")
64
- # Dotted sub-ID — bd show must receive the full ID including .6
65
- echo "◐ forge-m1n8.6 · Sub-feature X [● P2 · IN_PROGRESS]"
66
- echo "Owner: devthree"
67
- echo "Updated: 2026-03-27T08:00:00Z"
68
- ;;
69
- "show forge-m1n8")
70
- # Parent epic — if a buggy parser truncates forge-m1n8.6 to forge-m1n8,
71
- # the workload would get this WRONG owner and trigger the guard below.
72
- echo "◐ forge-m1n8 · PARENT EPIC (WRONG — dotted ID was truncated)"
73
- echo "Owner: WRONG_OWNER_DO_NOT_USE"
74
- echo "Updated: 2026-03-20T10:00:00Z"
75
- ;;
76
- esac
34
+ args="$*"
35
+ emit() { printf '{"data":{"issues":%s}}\n' "$1"; }
36
+
37
+ # forge-aaa BLOCKS forge-ccc (kernel: the blocker carries the dependency edge),
38
+ # so forge-ccc is the blocked one — blocked_by resolves to forge-aaa by reverse scan.
39
+ aaa='{"id":"forge-aaa","title":"Feature A","status":"in_progress","assignee":"devone","updated_at":"2026-03-27T10:00:00Z","dependencies":["forge-ccc"]}'
40
+ bbb='{"id":"forge-bbb","title":"Feature B","status":"open","assignee":"devtwo","updated_at":"2026-03-25T10:00:00Z","dependencies":[]}'
41
+ ccc='{"id":"forge-ccc","title":"Feature C","status":"in_progress","assignee":"devone","updated_at":"2026-03-27T09:00:00Z","dependencies":[]}'
42
+ # Dotted sub-ID: the id flows through verbatim from JSON, never truncated.
43
+ m1n8='{"id":"forge-m1n8.6","title":"Sub-feature X (dotted ID)","status":"in_progress","assignee":"devthree","updated_at":"2026-03-27T08:00:00Z","dependencies":[]}'
44
+
45
+ if [[ "$args" == *"issue list"* ]]; then
46
+ if [[ "$args" == *"--status=open"* ]]; then emit "[$bbb]"
47
+ elif [[ "$args" == *"--status=in_progress"* ]]; then emit "[$aaa,$ccc,$m1n8]"
48
+ else emit "[]"; fi
49
+ exit 0
50
+ fi
51
+ exit 0
77
52
  MOCK
78
- chmod +x "$mock_dir/bd"
53
+ chmod +x "$mock_dir/forge"
79
54
 
80
- # ── Create mock bd with no issues ────────────────────────────────────────
81
- cat > "$mock_dir/bd-empty" << 'MOCK'
55
+ # ── Create mock forge with no issues ──────────────────────────────────────
56
+ cat > "$mock_dir/forge-empty" << 'MOCK'
82
57
  #!/usr/bin/env bash
83
- case "$1 $2" in
84
- "list --status=open,in_progress")
85
- echo ""
86
- ;;
87
- "list --status=open")
88
- echo "○ forge-should-not-appear · Open fallback sentinel"
89
- ;;
90
- "list --status=in_progress")
91
- echo "◐ forge-should-not-appear-2 · In-progress fallback sentinel"
92
- ;;
93
- esac
58
+ printf '{"data":{"issues":[]}}\n'
59
+ exit 0
94
60
  MOCK
95
- chmod +x "$mock_dir/bd-empty"
61
+ chmod +x "$mock_dir/forge-empty"
96
62
 
97
63
  export GH_CMD="$mock_dir/gh"
98
- export BD_CMD="$mock_dir/bd"
64
+ export FORGE_CMD="$mock_dir/forge"
99
65
 
100
66
  # Source the library under test
101
67
  source "$SCRIPT_DIR/lib/workload.sh"
@@ -183,13 +149,13 @@ assert_not_contains "excludes devtwo" "Developer: devtwo" "$output"
183
149
  echo ""
184
150
  echo "── Test 4: No issues → No active work ──"
185
151
  unset _GITHUB_USER_CACHE
186
- export BD_CMD="$mock_dir/bd-empty"
152
+ export FORGE_CMD="$mock_dir/forge-empty"
187
153
  rc=0
188
154
  output="$(cmd_workload 2>/dev/null)" || rc=$?
189
155
  assert_exit "exits 0" 0 "$rc"
190
156
  assert_contains "shows no active work message" "No active work" "$output"
191
- assert_not_contains "does not fall back when combined query succeeds with no issues" "forge-should-not-appear" "$output"
192
- export BD_CMD="$mock_dir/bd"
157
+ assert_not_contains "empty issue lists yield no issue entries" "forge-" "$output"
158
+ export FORGE_CMD="$mock_dir/forge"
193
159
 
194
160
  # ── Test 5: Stale assignment flagged (>48h) ──────────────────────────────
195
161
  echo ""
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Generate the static command manifest — scripts/gen-command-manifest.js
6
+ *
7
+ * Static bundlers (`bun build --compile`) need a static require graph, but the
8
+ * command registry discovers commands at runtime via `fs.readdirSync` +
9
+ * `require(filePath)`. This generator walks `lib/commands/*.js` and emits
10
+ * `lib/commands/_manifest.js`, a checked-in module that `require`s every command
11
+ * module by a *static* relative path. The registry loads the manifest as its
12
+ * fast, bundleable path and keeps readdir auto-discovery as a dev/extension
13
+ * fallback (see lib/commands/_registry.js).
14
+ *
15
+ * The output is deterministic (sorted by filename) and timestamp-free, and the
16
+ * file is only rewritten when its content actually changes, so no-op
17
+ * regeneration never dirties the working tree.
18
+ *
19
+ * Usage:
20
+ * node scripts/gen-command-manifest.js # write if changed
21
+ * node scripts/gen-command-manifest.js --check # exit 1 if out of date
22
+ *
23
+ * @module scripts/gen-command-manifest
24
+ */
25
+
26
+ const fs = require('node:fs');
27
+ const path = require('node:path');
28
+
29
+ const COMMANDS_DIR = path.resolve(__dirname, '..', 'lib', 'commands');
30
+ const MANIFEST_PATH = path.join(COMMANDS_DIR, '_manifest.js');
31
+
32
+ /**
33
+ * List the command module filenames the manifest should enumerate.
34
+ *
35
+ * Mirrors the registry's own filter: `.js` files that do not start with `_`
36
+ * (underscore-prefixed files are registry internals, not commands), sorted for
37
+ * deterministic output.
38
+ *
39
+ * @param {string} [commandsDir] - Directory to scan (defaults to lib/commands)
40
+ * @returns {string[]} Sorted command filenames (e.g. `['add.js', 'audit.js']`)
41
+ */
42
+ function listCommandFiles(commandsDir = COMMANDS_DIR) {
43
+ return fs
44
+ .readdirSync(commandsDir)
45
+ .filter(f => f.endsWith('.js') && !f.startsWith('_'))
46
+ .sort();
47
+ }
48
+
49
+ /**
50
+ * Render the manifest source from a list of command filenames.
51
+ *
52
+ * @param {string[]} files - Sorted command filenames
53
+ * @returns {string} Full source text for lib/commands/_manifest.js
54
+ */
55
+ function renderManifest(files) {
56
+ const header = [
57
+ '/**',
58
+ ' * Static Command Manifest — GENERATED FILE, DO NOT EDIT.',
59
+ ' *',
60
+ ' * Regenerate with: node scripts/gen-command-manifest.js',
61
+ ' * Drift is enforced by test/structural/command-manifest-drift.test.js.',
62
+ ' *',
63
+ ' * This module `require`s every command by a static relative path so',
64
+ ' * `bun build --compile` can statically bundle the command graph. The registry',
65
+ ' * (lib/commands/_registry.js) consumes `commands` as its fast, bundleable path',
66
+ ' * and falls back to `fs.readdirSync` auto-discovery for dev/extension commands.',
67
+ ' *',
68
+ ' * @module commands/_manifest',
69
+ ' */',
70
+ '',
71
+ "'use strict';",
72
+ '',
73
+ '/**',
74
+ ' * @typedef {Object} ManifestEntry',
75
+ ' * @property {string} file - Command filename (e.g. `status.js`)',
76
+ ' * @property {import("./_registry").CommandModule} module - The required command module',
77
+ ' */',
78
+ '',
79
+ '/** @type {ManifestEntry[]} */',
80
+ 'const commands = [',
81
+ ];
82
+
83
+ const entries = files.map(file => {
84
+ const modPath = `./${file.replace(/\.js$/, '')}`;
85
+ return ` { file: ${JSON.stringify(file)}, module: require(${JSON.stringify(modPath)}) },`;
86
+ });
87
+
88
+ const footer = [
89
+ '];',
90
+ '',
91
+ 'module.exports = {',
92
+ ' // Absolute path of the canonical commands directory this manifest describes.',
93
+ ' // The registry applies the manifest only when asked to load this exact dir.',
94
+ ' dir: __dirname,',
95
+ ' commands,',
96
+ '};',
97
+ '',
98
+ ];
99
+
100
+ return [...header, ...entries, ...footer].join('\n');
101
+ }
102
+
103
+ /**
104
+ * Generate (or check) the manifest.
105
+ *
106
+ * @param {{ check?: boolean, commandsDir?: string, manifestPath?: string }} [opts]
107
+ * @returns {{ changed: boolean, files: string[], path: string }}
108
+ */
109
+ function generate(opts = {}) {
110
+ const commandsDir = opts.commandsDir ?? COMMANDS_DIR;
111
+ const manifestPath = opts.manifestPath ?? MANIFEST_PATH;
112
+ const files = listCommandFiles(commandsDir);
113
+ const next = renderManifest(files);
114
+
115
+ const current = fs.existsSync(manifestPath)
116
+ ? fs.readFileSync(manifestPath, 'utf8')
117
+ : null;
118
+
119
+ const changed = current !== next;
120
+
121
+ if (changed && !opts.check) {
122
+ fs.writeFileSync(manifestPath, next, 'utf8');
123
+ }
124
+
125
+ return { changed, files, path: manifestPath };
126
+ }
127
+
128
+ module.exports = { listCommandFiles, renderManifest, generate, COMMANDS_DIR, MANIFEST_PATH };
129
+
130
+ // CLI entry point
131
+ if (require.main === module) {
132
+ const check = process.argv.includes('--check');
133
+ const result = generate({ check });
134
+
135
+ if (check && result.changed) {
136
+ console.error(
137
+ '[gen-command-manifest] lib/commands/_manifest.js is out of date.\n' +
138
+ 'Run: node scripts/gen-command-manifest.js'
139
+ );
140
+ process.exit(1);
141
+ }
142
+
143
+ if (result.changed) {
144
+ console.log(
145
+ `[gen-command-manifest] wrote ${path.relative(process.cwd(), result.path)} ` +
146
+ `(${result.files.length} commands)`
147
+ );
148
+ } else {
149
+ console.log(
150
+ `[gen-command-manifest] up to date (${result.files.length} commands)`
151
+ );
152
+ }
153
+ }
@@ -0,0 +1,129 @@
1
+ // Generate lib/embedded-assets.generated.mjs — a Bun-embeddable manifest that
2
+ // statically imports every runtime asset (with { type: "file" }) and maps its
3
+ // real relative POSIX path to the embedded-file path Bun resolves at compile time.
4
+ //
5
+ // WHY a generated per-file map instead of `bun build --compile ... skills/**`:
6
+ // Bun's `embeddedFiles` exposes only a HASHED, directory-less `blob.name`
7
+ // (e.g. `SKILL-a1b2c3.md`), so every skill's `SKILL.md` would collide and the
8
+ // tree could not be reconstructed. A generated map keyed by the true relative
9
+ // path preserves the directory structure exactly; each value is that specific
10
+ // file's `/$bunfs/…` path, so `Bun.file(value)` / `fs` reads the right bytes.
11
+ //
12
+ // The output is gitignored and regenerated by the `build:binary` script right
13
+ // before `bun build --compile`, so it can never drift from the source tree. The
14
+ // completeness test (test/embedded-assets-drift.test.js) walks the same roots and
15
+ // asserts the embed set == the on-disk asset set, both ways.
16
+
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+ import crypto from 'node:crypto';
20
+ import { fileURLToPath } from 'node:url';
21
+ import pkgRoot from '../lib/package-root.js';
22
+
23
+ const { ASSET_ROOTS } = pkgRoot;
24
+
25
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
+ const REPO_ROOT = path.resolve(__dirname, '..');
27
+ const OUT_FILE = path.join(REPO_ROOT, 'lib', 'embedded-assets.generated.mjs');
28
+
29
+ // Deterministic, locale-INDEPENDENT codepoint comparator. Locale-default sort would
30
+ // diverge by machine locale and produce different embed bytes across the step-3
31
+ // cross-compile targets, so sorting is pinned to codepoint order everywhere.
32
+ const byteCompare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
33
+
34
+ /** True when the asset needs the executable bit restored on extraction (files-only embedding drops mode). */
35
+ export function isExecutableAsset(relPosixPath) {
36
+ return relPosixPath.endsWith('.sh') || relPosixPath.startsWith('.forge/hooks/');
37
+ }
38
+
39
+ /** Recursively list files under an absolute path, returning repo-relative posix paths. Symlinks are skipped (materialize content, never embed links). */
40
+ function walk(absPath, relPath, out) {
41
+ const stat = fs.lstatSync(absPath);
42
+ if (stat.isSymbolicLink()) return;
43
+ if (stat.isDirectory()) {
44
+ for (const entry of fs.readdirSync(absPath).sort(byteCompare)) {
45
+ walk(path.join(absPath, entry), `${relPath}/${entry}`, out);
46
+ }
47
+ } else if (stat.isFile()) {
48
+ out.push(relPath);
49
+ }
50
+ }
51
+
52
+ /** Collect every embeddable asset as a sorted list of repo-relative posix paths. */
53
+ export function collectAssetFiles(repoRoot = REPO_ROOT, roots = ASSET_ROOTS) {
54
+ const files = [];
55
+ for (const root of roots) {
56
+ const abs = path.join(repoRoot, root);
57
+ if (!fs.existsSync(abs)) {
58
+ // Fail CLOSED: a configured asset root that is absent would silently ship an
59
+ // incomplete manifest. Error instead so the build stops loudly.
60
+ throw new Error(
61
+ `Configured ASSET_ROOT does not exist: ${root} (looked in ${abs}). ` +
62
+ 'Fix ASSET_ROOTS in lib/package-root.js or restore the missing path.'
63
+ );
64
+ }
65
+ walk(abs, root.split(path.sep).join('/'), files);
66
+ }
67
+ return [...new Set(files)].sort(byteCompare);
68
+ }
69
+
70
+ /**
71
+ * Content fingerprint over the asset SET: sha256 of each relpath + its bytes, in
72
+ * sorted order. Emitted into the manifest so the runtime cache dir identity
73
+ * changes whenever any asset content or the set changes (invalidates stale
74
+ * same-version extractions). Deterministic across machines.
75
+ */
76
+ export function assetFingerprint(repoRoot = REPO_ROOT, files = collectAssetFiles(repoRoot)) {
77
+ const h = crypto.createHash('sha256');
78
+ for (const rel of files) {
79
+ h.update(rel);
80
+ h.update('\0');
81
+ h.update(fs.readFileSync(path.join(repoRoot, ...rel.split('/'))));
82
+ }
83
+ return h.digest('hex');
84
+ }
85
+
86
+ function render(files, fingerprint) {
87
+ const imports = [];
88
+ const entries = [];
89
+ const execEntries = [];
90
+ files.forEach((rel, i) => {
91
+ const ident = `a${i}`;
92
+ // Specifier is relative to lib/embedded-assets.generated.mjs → prefix `../`.
93
+ const specifier = JSON.stringify(`../${rel}`);
94
+ imports.push(`import ${ident} from ${specifier} with { type: "file" };`);
95
+ entries.push(` ${JSON.stringify(rel)}: ${ident},`);
96
+ if (isExecutableAsset(rel)) execEntries.push(` ${JSON.stringify(rel)},`);
97
+ });
98
+ return [
99
+ '// AUTO-GENERATED by scripts/gen-embedded-assets.mjs — DO NOT EDIT.',
100
+ '// Gitignored; regenerated before every `bun build --compile`.',
101
+ ...imports,
102
+ '',
103
+ 'export const EMBEDDED_ASSETS = {',
104
+ ...entries,
105
+ '};',
106
+ '',
107
+ 'export const EXECUTABLE_ASSETS = [',
108
+ ...execEntries,
109
+ '];',
110
+ '',
111
+ `export const ASSET_FINGERPRINT = ${JSON.stringify(fingerprint)};`,
112
+ '',
113
+ ].join('\n');
114
+ }
115
+
116
+ function main() {
117
+ const files = collectAssetFiles();
118
+ const fingerprint = assetFingerprint(REPO_ROOT, files);
119
+ fs.mkdirSync(path.dirname(OUT_FILE), { recursive: true });
120
+ fs.writeFileSync(OUT_FILE, render(files, fingerprint), 'utf8');
121
+ const execCount = files.filter(isExecutableAsset).length;
122
+ console.log(
123
+ `Embedded ${files.length} asset file(s) (${execCount} executable), fingerprint ${fingerprint.slice(0, 16)} → ${path.relative(REPO_ROOT, OUT_FILE)}`
124
+ );
125
+ }
126
+
127
+ if (import.meta.main || process.argv[1] === fileURLToPath(import.meta.url)) {
128
+ main();
129
+ }
@@ -0,0 +1,139 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Forge single-binary installer for Windows (x64).
4
+
5
+ .DESCRIPTION
6
+ Downloads the forge-windows-x64.exe binary from the GitHub Release, installs it
7
+ to %LOCALAPPDATA%\Programs\forge\forge.exe, and prints a PATH note plus the next
8
+ step. The binary bundles Forge's JavaScript but NOT its external prerequisites:
9
+ git, gh (GitHub CLI) and Git Bash must still be installed separately.
10
+
11
+ .PARAMETER Version
12
+ Release tag to install (default: latest), e.g. v1.2.3.
13
+
14
+ .PARAMETER Dir
15
+ Install directory (default: $env:LOCALAPPDATA\Programs\forge).
16
+
17
+ .PARAMETER PrintAsset
18
+ Print the resolved asset name and exit (used for verification/testing).
19
+
20
+ .EXAMPLE
21
+ irm https://raw.githubusercontent.com/harshanandak/forge/master/scripts/install.ps1 | iex
22
+
23
+ .EXAMPLE
24
+ # Pin a version (download the script, then run it with an argument):
25
+ $s = irm https://raw.githubusercontent.com/harshanandak/forge/master/scripts/install.ps1
26
+ & ([scriptblock]::Create($s)) -Version v1.2.3
27
+ #>
28
+ [CmdletBinding()]
29
+ param(
30
+ [string]$Version = 'latest',
31
+ [string]$Dir = (Join-Path $env:LOCALAPPDATA 'Programs\forge'),
32
+ [switch]$PrintAsset,
33
+ [switch]$SkipChecksum
34
+ )
35
+
36
+ $ErrorActionPreference = 'Stop'
37
+ Set-StrictMode -Version Latest
38
+
39
+ $Repo = if ($env:FORGE_REPO) { $env:FORGE_REPO } else { 'harshanandak/forge' }
40
+ if ($Version -eq 'latest' -and $env:FORGE_VERSION) { $Version = $env:FORGE_VERSION }
41
+
42
+ # Canonical asset-name mapping for Windows - the only target this script installs.
43
+ # Mirrors scripts/lib/release-asset.mjs (forge-<os>-<arch>[.exe]).
44
+ $Arch = if ($env:FORGE_ARCH) { $env:FORGE_ARCH } else { 'x64' }
45
+ if ($Arch -ne 'x64') {
46
+ throw "forge-install: unsupported architecture '$Arch'. Only windows-x64 is published."
47
+ }
48
+ $Asset = "forge-windows-$Arch.exe"
49
+
50
+ if ($PrintAsset) {
51
+ Write-Output $Asset
52
+ return
53
+ }
54
+
55
+ # Resolve version + URL. The /releases/latest/download/<asset> path redirects to
56
+ # the newest release, so no JSON API call is needed to download.
57
+ if ($Version -eq 'latest') {
58
+ $Url = "https://github.com/$Repo/releases/latest/download/$Asset"
59
+ try {
60
+ $rel = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" `
61
+ -Headers @{ 'User-Agent' = 'forge-install' }
62
+ $ResolvedVersion = $rel.tag_name
63
+ } catch {
64
+ $ResolvedVersion = 'latest'
65
+ }
66
+ } else {
67
+ $Url = "https://github.com/$Repo/releases/download/$Version/$Asset"
68
+ $ResolvedVersion = $Version
69
+ }
70
+
71
+ Write-Host "forge-install: platform windows / $Arch -> asset $Asset"
72
+ Write-Host "forge-install: downloading $Asset ($ResolvedVersion)"
73
+
74
+ New-Item -ItemType Directory -Force -Path $Dir | Out-Null
75
+ $Dest = Join-Path $Dir 'forge.exe'
76
+ $Tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("forge-install-" + [System.Guid]::NewGuid().ToString('N') + ".exe")
77
+
78
+ try {
79
+ Invoke-WebRequest -Uri $Url -OutFile $Tmp -UseBasicParsing -TimeoutSec 300
80
+ } catch {
81
+ throw "forge-install: download failed from $Url - is the release published for windows-x64? ($_)"
82
+ }
83
+ if (-not (Test-Path $Tmp) -or (Get-Item $Tmp).Length -eq 0) {
84
+ throw "forge-install: downloaded file is empty - asset '$Asset' may not exist in release '$ResolvedVersion'."
85
+ }
86
+
87
+ # Integrity verification (SHA-256) BEFORE installing. A mismatch is fatal
88
+ # (corrupted or substituted download). If the manifest cannot be fetched we warn
89
+ # and continue; pass -SkipChecksum or set FORGE_SKIP_CHECKSUM=1 to bypass.
90
+ if ($SkipChecksum -or $env:FORGE_SKIP_CHECKSUM -eq '1') {
91
+ Write-Host "forge-install: skipping integrity check (requested)."
92
+ } else {
93
+ $SumsUrl = $Url.Substring(0, $Url.LastIndexOf('/') + 1) + 'checksums.txt'
94
+ $SumsTmp = Join-Path ([System.IO.Path]::GetTempPath()) ("forge-install-" + [System.Guid]::NewGuid().ToString('N') + ".txt")
95
+ $expected = $null
96
+ $manifestFetched = $false
97
+ try {
98
+ Invoke-WebRequest -Uri $SumsUrl -OutFile $SumsTmp -UseBasicParsing -TimeoutSec 30
99
+ $manifestFetched = $true
100
+ foreach ($line in (Get-Content $SumsTmp)) {
101
+ # Lines are "<hash> <name>" (text) or "<hash> *<name>" (binary).
102
+ if ($line -match '^\s*([0-9a-fA-F]{64})\s+\*?(.+?)\s*$' -and $Matches[2] -eq $Asset) {
103
+ $expected = $Matches[1].ToLower()
104
+ break
105
+ }
106
+ }
107
+ } catch {
108
+ Write-Host "forge-install: could not download checksums.txt - skipping integrity check."
109
+ } finally {
110
+ if (Test-Path $SumsTmp) { Remove-Item -Force $SumsTmp }
111
+ }
112
+ if ($expected) {
113
+ $actual = (Get-FileHash -Algorithm SHA256 -Path $Tmp).Hash.ToLower()
114
+ if ($actual -ne $expected) {
115
+ Remove-Item -Force $Tmp
116
+ throw "forge-install: checksum mismatch for $Asset`n expected: $expected`n actual: $actual`nThe download may be corrupted or tampered with - aborting."
117
+ }
118
+ Write-Host "forge-install: checksum verified (sha256)."
119
+ } elseif ($manifestFetched) {
120
+ Write-Host "forge-install: $Asset not listed in checksums.txt - skipping integrity check."
121
+ }
122
+ }
123
+
124
+ Move-Item -Force -Path $Tmp -Destination $Dest
125
+
126
+ Write-Host ""
127
+ Write-Host "forge-install: installed forge $ResolvedVersion -> $Dest"
128
+
129
+ # PATH note: check the user PATH, not just the current process.
130
+ $userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
131
+ if (-not ($userPath -and ($userPath -split ';' | Where-Object { $_ -eq $Dir }))) {
132
+ Write-Host ""
133
+ Write-Host "$Dir is not on your PATH. Add it (new terminals will pick it up):"
134
+ Write-Host " [Environment]::SetEnvironmentVariable('Path', `"$Dir;`$([Environment]::GetEnvironmentVariable('Path','User'))`", 'User')"
135
+ }
136
+
137
+ Write-Host ""
138
+ Write-Host "Next: run 'forge setup' in a git repo to wire up Forge."
139
+ Write-Host "Note: git, gh (GitHub CLI) and Git Bash must be installed separately - the binary bundles Forge, not those tools."