@khoavu882/doflow 2.0.0-beta.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  3. package/core/.antigravity-plugin/plugin.json +1 -3
  4. package/core/.claude-plugin/plugin.json +1 -1
  5. package/core/.codex-plugin/plugin.json +1 -1
  6. package/core/.plugin/plugin.json +1 -1
  7. package/core/harnesses/claude/hooks/lib.sh +43 -24
  8. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  9. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  10. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  11. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  12. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  13. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  14. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  15. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  16. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  17. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  18. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  19. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  20. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  21. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  22. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  23. package/core/harnesses/claude/settings/settings.json +18 -0
  24. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  25. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  26. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  27. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  28. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  29. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  30. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  31. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  32. package/core/harnesses/codex/hooks/lib.sh +44 -25
  33. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  34. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  35. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  36. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  37. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  38. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  39. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  40. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  41. package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
  42. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  43. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  44. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  45. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  46. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  47. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  48. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  49. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  50. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  51. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  52. package/core/harnesses/kiro/agents/doflow.json +1 -1
  53. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  54. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
  55. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
  56. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
  57. package/core/harnesses/kiro/hooks/session-start.sh +12 -5
  58. package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
  59. package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
  60. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
  61. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  62. package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
  63. package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
  64. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  65. package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
  66. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  67. package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
  68. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  69. package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
  70. package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
  71. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  72. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  73. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  74. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  75. package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
  76. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
  77. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
  78. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
  79. package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
  80. package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
  81. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
  82. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  83. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
  84. package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
  85. package/core/harnesses/shared/locator/doflow-run +2 -3
  86. package/core/registry/advice.json +3 -3
  87. package/core/registry/assets.json +72 -80
  88. package/core/registry/capabilities.json +12 -0
  89. package/core/registry/contracts.json +60 -33
  90. package/core/registry/contribution-points.json +32 -0
  91. package/core/registry/gates.json +32 -0
  92. package/core/registry/guidance-caps.json +40 -0
  93. package/core/registry/harnesses.json +1542 -277
  94. package/core/registry/lifecycle.json +2837 -37
  95. package/core/registry/mcp.json +5 -1
  96. package/core/registry/models.json +7 -1
  97. package/core/registry/package-templates/README.md +19 -0
  98. package/core/registry/package-templates/agent/files/agent.md +9 -0
  99. package/core/registry/package-templates/agent/template.json +4 -0
  100. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  101. package/core/registry/package-templates/capability-provider/template.json +11 -0
  102. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  103. package/core/registry/package-templates/debate-preset/template.json +4 -0
  104. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  105. package/core/registry/package-templates/review-preset/template.json +4 -0
  106. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  107. package/core/registry/package-templates/skill/template.json +4 -0
  108. package/core/registry/package-templates/worker/files/worker.json +92 -0
  109. package/core/registry/package-templates/worker/template.json +11 -0
  110. package/core/registry/packages/doflow/doflow-package.json +15 -0
  111. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  112. package/core/registry/protocols.json +61 -0
  113. package/core/registry/routes.json +17 -0
  114. package/core/registry/rules.json +431 -0
  115. package/core/registry/settings.json +276 -0
  116. package/core/registry/storage.json +701 -30
  117. package/core/registry/telemetry.json +236 -0
  118. package/core/registry/transcripts.json +142 -0
  119. package/core/registry/verification.json +76 -1
  120. package/core/registry/workflows.json +55 -0
  121. package/core/shared/agent-specs/core-implementer.md +3 -0
  122. package/core/shared/agent-specs/delivery-lead.md +68 -0
  123. package/core/shared/agent-specs/quality-guardian.md +5 -1
  124. package/core/shared/agent-specs/research-writer.md +10 -3
  125. package/core/shared/agent-specs/reviewer.md +53 -0
  126. package/core/shared/agent-specs/spec-analyst.md +5 -6
  127. package/core/shared/agent-specs/system-architect.md +5 -1
  128. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  129. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  130. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  131. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  132. package/core/shared/guidance/pointers/codex.md +1 -4
  133. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  134. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  135. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  136. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  137. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  138. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  139. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  140. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  141. package/core/shared/guidance/rules/RULES.md +45 -0
  142. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  143. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  144. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  145. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  146. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  147. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  148. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  149. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  150. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  151. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  152. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  153. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  154. package/core/shared/scripts/doflow/bin/doflow-run +154 -35
  155. package/core/shared/skills/do/SKILL.md +12 -10
  156. package/core/shared/skills/do/references/advice_calls.md +26 -0
  157. package/core/shared/skills/do/references/pm_routing.md +1 -2
  158. package/core/shared/skills/do/references/program_mode.md +31 -0
  159. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  160. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  161. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  162. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  163. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  164. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  165. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  166. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  167. package/core/shared/skills/do-design/SKILL.md +24 -23
  168. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  169. package/core/shared/skills/do-document/SKILL.md +11 -1
  170. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  171. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  172. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  173. package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
  174. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  175. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  176. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
  177. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  178. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  179. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  180. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  181. package/core/shared/skills/do-flow/SKILL.md +8 -9
  182. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  183. package/core/shared/skills/do-git/SKILL.md +4 -0
  184. package/core/shared/skills/do-implement/SKILL.md +7 -9
  185. package/core/shared/skills/do-plan/SKILL.md +29 -19
  186. package/core/shared/skills/do-test/SKILL.md +7 -4
  187. package/core/shared/templates/doflow/constitution-template.md +9 -3
  188. package/core/shared/templates/doflow/data-model-template.md +1 -1
  189. package/core/shared/templates/doflow/design-template.md +113 -16
  190. package/core/shared/templates/doflow/plan-template.md +11 -10
  191. package/core/shared/templates/doflow/question-log-template.md +14 -2
  192. package/core/shared/templates/doflow/requirement-template.md +1 -1
  193. package/core/worker-evidence/antigravity/checks.json +14 -0
  194. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  195. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  196. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  197. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  198. package/core/worker-evidence/codex/checks.json +38 -0
  199. package/package.json +10 -6
  200. package/src/adapters/antigravity/index.js +226 -113
  201. package/src/adapters/claude/index.js +39 -5
  202. package/src/adapters/codex/agents.js +25 -15
  203. package/src/adapters/codex/hooks.js +40 -5
  204. package/src/adapters/codex/index.js +100 -22
  205. package/src/adapters/codex/native-projection.js +6 -2
  206. package/src/adapters/copilot/index.js +58 -23
  207. package/src/adapters/copy-tree.js +294 -48
  208. package/src/adapters/guidance-manifest.js +71 -0
  209. package/src/adapters/guidance-pointer.js +19 -0
  210. package/src/adapters/hook-commands.js +6 -10
  211. package/src/adapters/index.js +1 -0
  212. package/src/adapters/kiro/index.js +38 -13
  213. package/src/adapters/opencode/index.js +8 -8
  214. package/src/adapters/pi/hooks.js +120 -0
  215. package/src/adapters/pi/index.js +48 -16
  216. package/src/adapters/resolve-source.js +26 -2
  217. package/src/cli/commands/install.js +3 -2
  218. package/src/cli/commands/reconcile.js +53 -9
  219. package/src/cli/commands/remove.js +2 -2
  220. package/src/cli/commands/rollback.js +6 -3
  221. package/src/cli/commands/self-update.js +18 -9
  222. package/src/cli/commands/status.js +21 -2
  223. package/src/cli/commands/ui.js +99 -0
  224. package/src/cli/commands/update.js +3 -2
  225. package/src/cli/index.js +338 -33
  226. package/src/cli/runtime-commands.js +243 -52
  227. package/src/cli/shared.js +0 -2
  228. package/src/helper/git.js +1 -1
  229. package/src/helper/guardrails.js +1 -1
  230. package/src/helper/marker-merge.js +31 -13
  231. package/src/helper/package-ids.js +37 -0
  232. package/src/helper/semver.js +94 -0
  233. package/src/install/backup.js +105 -15
  234. package/src/install/context.js +1 -1
  235. package/src/install/targets.js +31 -16
  236. package/src/lifecycle/coverage.js +74 -0
  237. package/src/lifecycle/hook-step.js +61 -0
  238. package/src/lifecycle/hook-tier.js +160 -0
  239. package/src/lifecycle/index.js +434 -86
  240. package/src/lifecycle/retired-harnesses.js +318 -0
  241. package/src/lifecycle/view.js +35 -15
  242. package/src/registry/index.js +686 -32
  243. package/src/registry/worker-ids.js +45 -0
  244. package/src/release/build-package.js +0 -3
  245. package/src/release/docs-blocks.js +492 -0
  246. package/src/release/preflight.js +425 -0
  247. package/src/release/rehearse.js +401 -0
  248. package/src/release/release-workflow.js +733 -0
  249. package/src/release/sync-plugin-versions.js +43 -7
  250. package/src/runtime/advice/bench-gate.js +25 -0
  251. package/src/runtime/advice/cli.js +59 -16
  252. package/src/runtime/advice/client.js +32 -9
  253. package/src/runtime/advice/config.js +98 -133
  254. package/src/runtime/advice/doctor.js +1 -1
  255. package/src/runtime/advice/elevation.js +97 -0
  256. package/src/runtime/advice/endpoint-guard.js +9 -3
  257. package/src/runtime/advice/mode.js +37 -0
  258. package/src/runtime/advice/record.js +18 -5
  259. package/src/runtime/advice/registry.js +4 -1
  260. package/src/runtime/advice/shadow-config.js +229 -0
  261. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  262. package/src/runtime/brief-rules.js +81 -0
  263. package/src/runtime/brief-tools.js +111 -34
  264. package/src/runtime/candidate.js +514 -0
  265. package/src/runtime/capabilities/executor.js +450 -0
  266. package/src/runtime/capabilities/loopback.js +41 -0
  267. package/src/runtime/capabilities/sanitizer.js +88 -0
  268. package/src/runtime/capability-router.js +135 -3
  269. package/src/runtime/checkouts.js +73 -11
  270. package/src/runtime/claims.js +27 -12
  271. package/src/runtime/cli-result.js +11 -4
  272. package/src/runtime/cli.js +221 -277
  273. package/src/runtime/config-home.js +42 -0
  274. package/src/runtime/context/arbitrate.js +767 -0
  275. package/src/runtime/context/budget.js +203 -0
  276. package/src/runtime/context/compose.js +1367 -0
  277. package/src/runtime/context/delivery-store.js +100 -0
  278. package/src/runtime/context/emission-rules.js +118 -0
  279. package/src/runtime/context/identity.js +227 -0
  280. package/src/runtime/context/mandatory.js +594 -0
  281. package/src/runtime/context/pointer.js +142 -0
  282. package/src/runtime/context/primary-command.js +117 -0
  283. package/src/runtime/context/primary.js +344 -0
  284. package/src/runtime/context/summary-store.js +655 -0
  285. package/src/runtime/context-pack.js +1106 -245
  286. package/src/runtime/decision-register.js +13 -4
  287. package/src/runtime/dispatch/admission.js +392 -0
  288. package/src/runtime/dispatch/attempt-end.js +274 -0
  289. package/src/runtime/dispatch/command.js +628 -0
  290. package/src/runtime/dispatch/controls.js +904 -0
  291. package/src/runtime/dispatch/envelope.js +276 -0
  292. package/src/runtime/dispatch/grants.js +310 -0
  293. package/src/runtime/dispatch/integrate.js +617 -0
  294. package/src/runtime/dispatch/loop.js +1835 -0
  295. package/src/runtime/dispatch/store.js +352 -0
  296. package/src/runtime/dispatch/verify-run.js +201 -0
  297. package/src/runtime/evidence-fields.js +39 -0
  298. package/src/runtime/evidence-ledger.js +41 -7
  299. package/src/runtime/failure/capture.js +3 -1
  300. package/src/runtime/failure/cli.js +25 -9
  301. package/src/runtime/failure/home.js +57 -30
  302. package/src/runtime/failure/store.js +7 -2
  303. package/src/runtime/feature-pointer.js +174 -0
  304. package/src/runtime/feature-resolve.js +13 -2
  305. package/src/runtime/fleet/command.js +77 -0
  306. package/src/runtime/fleet/projects.js +178 -0
  307. package/src/runtime/fleet/tree.js +383 -0
  308. package/src/runtime/freshness.js +77 -10
  309. package/src/runtime/git-memo.js +46 -0
  310. package/src/runtime/guidance/ablation.js +427 -0
  311. package/src/runtime/health.js +565 -17
  312. package/src/runtime/history-compactor.js +13 -3
  313. package/src/runtime/implementation-gate.js +277 -22
  314. package/src/runtime/inventory/coverage.js +1 -1
  315. package/src/runtime/inventory/index.js +1 -1
  316. package/src/runtime/inventory/precedence.js +2 -2
  317. package/src/runtime/inventory/siblings.js +6 -4
  318. package/src/runtime/knowledge/code-corpus.js +277 -0
  319. package/src/runtime/knowledge/code-lexical.js +130 -0
  320. package/src/runtime/knowledge/code-retrieval.js +307 -0
  321. package/src/runtime/knowledge/code-validation.js +106 -0
  322. package/src/runtime/knowledge/gp3-gate.js +74 -0
  323. package/src/runtime/knowledge/graph-adapter.js +288 -0
  324. package/src/runtime/knowledge/graph-fallback.js +394 -0
  325. package/src/runtime/knowledge/hybrid.js +136 -8
  326. package/src/runtime/knowledge/retrieval.js +34 -1
  327. package/src/runtime/knowledge/rtk-compress.js +77 -0
  328. package/src/runtime/knowledge/semble-bridge.py +106 -0
  329. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  330. package/src/runtime/knowledge/semble-provider.js +365 -0
  331. package/src/runtime/lifecycle/cli.js +16 -6
  332. package/src/runtime/lifecycle/event-store.js +8 -6
  333. package/src/runtime/lifecycle/followup.js +6 -1
  334. package/src/runtime/lifecycle/overview.js +24 -1
  335. package/src/runtime/lifecycle/report-store.js +8 -10
  336. package/src/runtime/lifecycle/retention.js +95 -31
  337. package/src/runtime/lifecycle/root.js +9 -1
  338. package/src/runtime/lifecycle/status.js +34 -17
  339. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  340. package/src/runtime/mask.js +134 -7
  341. package/src/runtime/mcp-consultations.js +40 -6
  342. package/src/runtime/model-router.js +79 -7
  343. package/src/runtime/models/catalogue.js +191 -0
  344. package/src/runtime/models/command.js +153 -0
  345. package/src/runtime/off-words.js +12 -0
  346. package/src/runtime/outcome.js +3 -3
  347. package/src/runtime/packages/command.js +1009 -0
  348. package/src/runtime/packages/contributions.js +116 -0
  349. package/src/runtime/packages/digest.js +132 -0
  350. package/src/runtime/packages/fetch.js +182 -0
  351. package/src/runtime/packages/grants.js +192 -0
  352. package/src/runtime/packages/index-file.js +134 -0
  353. package/src/runtime/packages/instruction.js +90 -0
  354. package/src/runtime/packages/manifest.js +331 -0
  355. package/src/runtime/packages/points.js +477 -0
  356. package/src/runtime/packages/programs.js +216 -0
  357. package/src/runtime/packages/scaffold.js +277 -0
  358. package/src/runtime/packages/source.js +90 -0
  359. package/src/runtime/packages/state.js +164 -0
  360. package/src/runtime/packages/store.js +449 -0
  361. package/src/runtime/packages/tar.js +106 -0
  362. package/src/runtime/packages/view.js +406 -0
  363. package/src/runtime/plan-check.js +59 -0
  364. package/src/runtime/plan-content.js +324 -0
  365. package/src/runtime/policy-init.js +118 -0
  366. package/src/runtime/profiles/doctor.js +96 -0
  367. package/src/runtime/profiles/resolve.js +244 -0
  368. package/src/runtime/profiles/rollback.js +254 -0
  369. package/src/runtime/profiles/sampler.js +41 -0
  370. package/src/runtime/readiness-record.js +16 -11
  371. package/src/runtime/readiness.js +127 -16
  372. package/src/runtime/research-request.js +50 -25
  373. package/src/runtime/retrieval-plan.js +402 -73
  374. package/src/runtime/retrieval-record.js +293 -0
  375. package/src/runtime/review/command.js +246 -0
  376. package/src/runtime/review/dominance.js +68 -0
  377. package/src/runtime/review/findings.js +434 -0
  378. package/src/runtime/review/identity.js +255 -0
  379. package/src/runtime/review/protocol-command.js +109 -0
  380. package/src/runtime/review/protocols.js +405 -0
  381. package/src/runtime/review/resolve-protocol.js +302 -0
  382. package/src/runtime/review/start.js +530 -0
  383. package/src/runtime/roadmap/facts.js +479 -0
  384. package/src/runtime/roadmap/gates.js +270 -0
  385. package/src/runtime/roadmap/loop.js +1636 -0
  386. package/src/runtime/roadmap/merge.js +381 -0
  387. package/src/runtime/roadmap/rows.js +1113 -0
  388. package/src/runtime/roadmap/seed.js +951 -0
  389. package/src/runtime/rules-report.js +336 -0
  390. package/src/runtime/scaffold/artifacts.js +63 -9
  391. package/src/runtime/scaffold/generate.js +21 -9
  392. package/src/runtime/settings/autopilot.js +106 -0
  393. package/src/runtime/settings/cli.js +280 -0
  394. package/src/runtime/settings/config-write.js +314 -0
  395. package/src/runtime/settings/files.js +271 -0
  396. package/src/runtime/settings/legacy.js +154 -0
  397. package/src/runtime/settings/lexical.js +56 -0
  398. package/src/runtime/settings/prepared-run.js +269 -0
  399. package/src/runtime/settings/project-layers.js +118 -0
  400. package/src/runtime/settings/refs.js +288 -0
  401. package/src/runtime/settings/registry.js +553 -0
  402. package/src/runtime/settings/resolve.js +713 -0
  403. package/src/runtime/settings/shared.js +37 -0
  404. package/src/runtime/settings/worker-billing.js +107 -0
  405. package/src/runtime/settings/write.js +269 -0
  406. package/src/runtime/slot-command.js +78 -0
  407. package/src/runtime/slots.js +671 -0
  408. package/src/runtime/source-version.js +290 -0
  409. package/src/runtime/state-root.js +217 -0
  410. package/src/runtime/storage/cli.js +12 -5
  411. package/src/runtime/storage/engine.js +9 -2
  412. package/src/runtime/storage/registry.js +14 -1
  413. package/src/runtime/storage/roots.js +66 -16
  414. package/src/runtime/storage/rules.js +82 -10
  415. package/src/runtime/storage/upkeep.js +10 -2
  416. package/src/runtime/storage/work-status.js +245 -26
  417. package/src/runtime/task-attempts.js +589 -69
  418. package/src/runtime/task-classifier.js +47 -11
  419. package/src/runtime/task-graph.js +373 -0
  420. package/src/runtime/task-run.js +778 -0
  421. package/src/runtime/task-scope.js +202 -56
  422. package/src/runtime/task-sources.js +147 -0
  423. package/src/runtime/task-state.js +440 -65
  424. package/src/runtime/team/admission.js +301 -0
  425. package/src/runtime/team/envelope.js +324 -0
  426. package/src/runtime/team/review-guard.js +156 -0
  427. package/src/runtime/trace/analyze.js +427 -0
  428. package/src/runtime/trace/baseline.js +770 -0
  429. package/src/runtime/trace/ledger.js +63 -140
  430. package/src/runtime/trace/otlp-export.js +568 -0
  431. package/src/runtime/trace/otlp-map.js +481 -0
  432. package/src/runtime/trace/spans.js +745 -0
  433. package/src/runtime/trace/telemetry.js +225 -0
  434. package/src/runtime/trace/views.js +1 -0
  435. package/src/runtime/transcripts/command.js +98 -0
  436. package/src/runtime/transcripts/cursor.js +326 -0
  437. package/src/runtime/transcripts/read.js +126 -0
  438. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  439. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  440. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  441. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  442. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  443. package/src/runtime/transcripts/registry.js +161 -0
  444. package/src/runtime/transcripts/stats.js +161 -0
  445. package/src/runtime/verification/check-key.js +628 -0
  446. package/src/runtime/verification/contract-runner.js +546 -11
  447. package/src/runtime/verification/engine.js +499 -9
  448. package/src/runtime/verification/impact.js +617 -0
  449. package/src/runtime/verification/inflight.js +205 -0
  450. package/src/runtime/verification/registry.js +176 -0
  451. package/src/runtime/verification/reuse.js +184 -0
  452. package/src/runtime/verification/telemetry-emit.js +254 -0
  453. package/src/runtime/verification/test-selection.js +67 -7
  454. package/src/runtime/worker/active.js +90 -0
  455. package/src/runtime/worker/cancel.js +222 -0
  456. package/src/runtime/worker/catalogue.js +122 -0
  457. package/src/runtime/worker/command.js +677 -0
  458. package/src/runtime/worker/evidence.js +288 -0
  459. package/src/runtime/worker/launch.js +288 -0
  460. package/src/runtime/worker/models.js +137 -0
  461. package/src/runtime/worker/parsers.js +218 -0
  462. package/src/runtime/worker/probes/index.js +706 -0
  463. package/src/runtime/worker/process-tree.js +415 -0
  464. package/src/runtime/worker/quota.js +43 -0
  465. package/src/runtime/worker/readiness.js +713 -0
  466. package/src/runtime/worker/run-store.js +166 -0
  467. package/src/runtime/worker/supervisor.js +541 -0
  468. package/src/runtime/worker/worktree-check.js +252 -0
  469. package/src/runtime/workflow-engine.js +5 -1
  470. package/src/runtime/workflow-orchestrator.js +578 -63
  471. package/src/runtime/worktree.js +149 -22
  472. package/src/state/index.js +1 -1
  473. package/src/state/lockfile.js +64 -10
  474. package/src/state/run-lock.js +1 -1
  475. package/types/node-commonjs.d.ts +106 -7
  476. package/core/.antigravity-plugin/hooks.json +0 -39
  477. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  478. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  479. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
  480. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  481. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
  482. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  483. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
  484. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  485. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  486. package/core/harnesses/gemini/hooks/session-start.sh +0 -5
  487. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  488. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  489. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  490. package/core/harnesses/shared/hooks/hooks.json +0 -47
  491. package/core/shared/guidance/FLAGS.md +0 -25
  492. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  493. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  494. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  495. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  496. package/src/adapters/gemini/hooks.js +0 -210
  497. package/src/adapters/gemini/index.js +0 -390
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ const { isQualifiedId } = require('../helper/package-ids.js');
4
+ /** IC-002 and 106 IC-005: maximum worker ids in an enabled list or dispatch ladder. */
5
+ const LADDER_MAX = 8;
6
+ const WORKER_ID_MAX = 32;
7
+ const BUILTIN_WORKER_ID_RE = /^[a-z][a-z0-9-]{0,31}$/;
8
+ const RESERVED_WORKER_IDS = Object.freeze(['primary', 'auto', 'subagent', 'sequential', 'manual', 'held', 'none', 'unknown']);
9
+
10
+
11
+ /** IC-001: validate worker id shape without checking catalogue membership. */
12
+ function workerIdFault(id ) {
13
+ if (typeof id === 'string' && RESERVED_WORKER_IDS.includes(id)) return { code: 'reserved', reason: 'worker id is reserved' };
14
+ if (typeof id === 'string' && (BUILTIN_WORKER_ID_RE.test(id) || isQualifiedId(id))) return null;
15
+ return { code: 'type', reason: 'worker id must be a lowercase built-in or qualified id' };
16
+ }
17
+
18
+
19
+
20
+ /** IC-001, 127 IC-005: classify worker shape and membership independently; a retired registry id is `retired`, not `dangling`. */
21
+ function classifyWorkerId(id , known , retired = []) {
22
+ const fault = workerIdFault(id);
23
+ if (fault?.code === 'type') return 'malformed';
24
+ if (fault?.code === 'reserved') return 'reserved';
25
+ if (known.includes(id )) return 'ok';
26
+ return retired.some((entry) => entry.id === id) ? 'retired' : 'dangling';
27
+ }
28
+
29
+ /** 127 IC-005: the one advisory line for a retired id a setting names. */
30
+ function retiredAdvisory(key , entry ) {
31
+ return `${key}: '${entry.id}' is retired; use '${entry.replacement}'`;
32
+ }
33
+
34
+ /** IC-001: return built-in harness ids that carry a worker object, in registry order. */
35
+ function shippedWorkerIds(registry ) {
36
+ return (registry.harnesses || []).filter((h ) => h.worker && typeof h.worker === 'object').map((h ) => h.id);
37
+ }
38
+
39
+ /** IC-001 and IC-019: read worker membership from the catalogue seam. */
40
+ function knownWorkerIds({ registry, view } = {}) {
41
+ const { workerCatalogue } = require('../runtime/worker/catalogue.js');
42
+ return { ids: workerCatalogue({ registry, view }).map((entry ) => entry.id) };
43
+ }
44
+
45
+ module.exports = { LADDER_MAX, WORKER_ID_MAX, BUILTIN_WORKER_ID_RE, RESERVED_WORKER_IDS, isQualifiedId, workerIdFault, classifyWorkerId, retiredAdvisory, shippedWorkerIds, knownWorkerIds };
@@ -20,9 +20,6 @@ function walkFiles(root ) {
20
20
  }
21
21
 
22
22
  function buildPackage(outDir , { repoRoot = REPO_ROOT } = {}) {
23
- if (typeof stripTypeScriptTypes !== 'function') {
24
- throw new Error('Node does not provide node:module.stripTypeScriptTypes');
25
- }
26
23
  const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
27
24
  const output = path.resolve(outDir);
28
25
  fs.rmSync(output, { recursive: true, force: true });
@@ -0,0 +1,492 @@
1
+ 'use strict';
2
+
3
+ // ── Generated documentation blocks (087b IC-001, IC-002, IC-003, IC-009) ─────────────────────────
4
+ //
5
+ // Every table whose truth already lives in a registry or in a help text is written into the docs
6
+ // between a pair of marker lines, and docs/llms.txt is written whole from the mkdocs.yml nav and
7
+ // each nav page's front-matter `description`. Run directly by Node native type-stripping:
8
+ //
9
+ // node src/release/docs-blocks.js --write rewrite every block span and docs/llms.txt
10
+ // node src/release/docs-blocks.js --check write nothing; exit 0 current, 1 findings, 2 usage
11
+ //
12
+ // A marker pair is `<!-- BEGIN GENERATED:<id> -->` / `<!-- END GENERATED:<id> -->`, each on its own
13
+ // line; the span between them is the renderer's output plus one newline, and text outside a span is
14
+ // never touched. BLOCKS below is the only list of block ids; the docs guard (G61) derives
15
+ // everything from it. Every function takes a repo-shaped `root` so a test can seed a fault in a
16
+ // copy. Output depends on no clock, version, locale or terminal width.
17
+
18
+ const fs = require('node:fs');
19
+ const os = require('node:os');
20
+ const path = require('node:path');
21
+ const { spawnSync } = require('node:child_process');
22
+ const { REPO_ROOT } = require('../helper/repo-root.js');
23
+ const { renderEnforcementMatrix, renderBehaviourMatrix } = require('../lifecycle/coverage.js');
24
+ const { loadSettingsRegistry } = require('../runtime/settings/registry.js');
25
+ const { REF_META } = require('../runtime/settings/refs.js');
26
+
27
+ const LLMS_CAP = 4096;
28
+ const MARKER_ID = /^[a-z][a-z0-9-]*$/;
29
+ // A marker is a whole line in exactly this form. MARKER_LIKE also finds one with indentation or
30
+ // trailing white space (or a CR), so markerFindings can report a mangled marker instead of passing it.
31
+ const MARKER_LINE = /^<!-- (BEGIN|END) GENERATED:(\S+) -->$/gm;
32
+ const MARKER_LIKE = /^[ \t]*<!-- (BEGIN|END) GENERATED:(\S+) -->[ \t\r]*$/gm;
33
+ const USAGE = 'usage: node src/release/docs-blocks.js --write|--check\n';
34
+ const DOFLOW_BIN = 'bin/doflow.js';
35
+ const DOFLOW_RUN_BIN = 'core/shared/scripts/doflow/bin/doflow-run';
36
+
37
+ const NUMBER_WORDS = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve'];
38
+
39
+ // Display labels for capability keys; a key without a label is shown capitalized, so a capability
40
+ // added to the registry still gets its row.
41
+ const CAPABILITY_LABELS = {
42
+ instructions: 'Instructions', skills: 'Skills', agents: 'Agents', scripts: 'Scripts', templates: 'Templates',
43
+ modes: 'Modes', settings: 'Settings', hooks: 'Hooks', mcp: 'MCP', plugin: 'Plugin / extension',
44
+ };
45
+
46
+
47
+
48
+
49
+ function readJson(root , rel ) {
50
+ return JSON.parse(fs.readFileSync(path.join(root, rel), 'utf8'));
51
+ }
52
+
53
+ // ── Cell text ─────────────────────────────────────────────────────────────────────────────────────
54
+ //
55
+ // Every value that reaches a table comes from a registry or a help text. `text()` turns it into
56
+ // literal cell text: line breaks become spaces (a newline would end the row); `&`, `<` and `>` become
57
+ // entities (else a `<plan path>` operand vanishes as a tag and a `<script>` runs); and a backslash goes
58
+ // before `\`, a backtick, `|`, `[`, `]`, `(`, `)`, `!`, `*` and `_` (else a pipe splits the row, a
59
+ // backtick opens a code span, and `[x](y)`, `![x](y)`, `*x*` or `_x_` render as a live link, an image
60
+ // or emphasis). Python-Markdown and GitHub both read those backslash escapes as the literal character.
61
+ // `code()` wraps a value in a code span only when it holds no pipe, backtick or line break (inside a
62
+ // code span nothing is interpreted), and otherwise falls back to `text()`. A marker line can never
63
+ // appear inside a cell: a cell is part of a `|` row and markers are whole lines.
64
+
65
+ function oneLine(value ) {
66
+ return String(value ?? '').replace(/\r?\n|\r/g, ' ');
67
+ }
68
+
69
+ /** Plain cell text with Markdown and HTML made inert; empty is shown as an em dash. */
70
+ function text(value ) {
71
+ const raw = oneLine(value);
72
+ if (raw === '') return '—';
73
+ return raw
74
+ .replace(/&/g, '&amp;')
75
+ .replace(/</g, '&lt;')
76
+ .replace(/>/g, '&gt;')
77
+ .replace(/\\/g, '\\\\')
78
+ .replace(/[`|[\]()!*_]/g, (character) => `\\${character}`);
79
+ }
80
+
81
+ /** A value as a code span; one holding a pipe, backtick or line break is shown as plain text. */
82
+ function code(value ) {
83
+ if (value === null || value === undefined || value === '') return '—';
84
+ const raw = String(value);
85
+ return /[|`\r\n]/.test(raw) ? text(raw) : `\`${raw}\``;
86
+ }
87
+
88
+ /** Headers go through `text()`; row cells are already formatted by `text()` or `code()`. */
89
+ function table(headers , rows ) {
90
+ return [
91
+ `| ${headers.map(text).join(' | ')} |`,
92
+ `|${headers.map(() => '---').join('|')}|`,
93
+ ...rows.map((row) => `| ${row.join(' | ')} |`),
94
+ ].join('\n');
95
+ }
96
+
97
+ /** A copy of registry data with every string made inert, for renderers that format cells themselves. */
98
+ function inert(value ) {
99
+ if (typeof value === 'string') return value === '' ? value : text(value);
100
+ if (Array.isArray(value)) return value.map(inert);
101
+ if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, inert(entry)]));
102
+ return value;
103
+ }
104
+
105
+ function capitalize(text ) {
106
+ return text ? text[0].toUpperCase() + text.slice(1) : text;
107
+ }
108
+
109
+ // ── Renderers (one pure function of the root per block id) ──────────────────────────────────────
110
+
111
+ function renderCapabilityMatrix(root ) {
112
+ const harnesses = readJson(root, 'core/registry/harnesses.json').harnesses;
113
+ if (!Array.isArray(harnesses) || harnesses.length === 0) throw new Error('harnesses.json has no harnesses');
114
+ const keys = Object.keys(harnesses[0].capabilities || {});
115
+ if (keys.length === 0) throw new Error('harnesses.json declares no capabilities');
116
+ const status = (harness , key ) => {
117
+ const value = harness.capabilities?.[key]?.status;
118
+ if (typeof value !== 'string' || !value) return '—';
119
+ const target = harness.nativeTargets?.[key];
120
+ const label = text(capitalize(value));
121
+ return target && (value === 'supported' || value === 'different') ? `${label} — ${code(target)}` : label;
122
+ };
123
+ return table(
124
+ ['Capability', ...harnesses.map((harness ) => oneLine(harness.displayName))],
125
+ keys.map((key) => [text(CAPABILITY_LABELS[key] || capitalize(key)), ...harnesses.map((harness ) => status(harness, key))]),
126
+ );
127
+ }
128
+
129
+ function renderTaskClasses(root ) {
130
+ const classes = readJson(root, 'core/registry/workflows.json').classes;
131
+ const templates = new Set(Object.keys(readJson(root, 'core/registry/readiness-templates.json').templates || {}));
132
+ if (!classes || typeof classes !== 'object' || Object.keys(classes).length === 0) throw new Error('workflows.json has no classes');
133
+ return table(['Class', 'Stages', 'Gates', 'Readiness'], Object.entries(classes).map(([id, workflow] ) => {
134
+ const gates = Array.isArray(workflow.gates) ? workflow.gates.length : 0;
135
+ const template = workflow.stages.find((stage ) => stage.readinessTemplate)?.readinessTemplate;
136
+ return [
137
+ code(id),
138
+ text(workflow.stages.map((stage ) => stage.id).join(' → ')),
139
+ gates ? String(gates) : '—',
140
+ templates.has(template) ? code(template) : '—',
141
+ ];
142
+ }));
143
+ }
144
+
145
+ // The Default cell shows the resolved default: a `defaultRef` (the advice endpoint, the retention
146
+ // window) is resolved through the settings registry exactly as G44 S2 resolves it.
147
+ function resolvedDefault(key , spec , registry , resolving = new Set()) {
148
+ if (Object.hasOwn(spec, 'default')) return spec.default;
149
+ if (!spec.defaultRef || resolving.has(key)) return undefined;
150
+ const next = new Set(resolving).add(key);
151
+ const dependencies = new Set (REF_META[spec.defaultRef]?.dependsOn || []);
152
+ for (const requirement of Array.isArray(spec.requires) ? spec.requires : []) {
153
+ if (typeof requirement?.key === 'string') dependencies.add(requirement.key);
154
+ }
155
+ const deps = {};
156
+ for (const dependency of dependencies) {
157
+ const dependencySpec = registry.keys.get(dependency);
158
+ if (dependencySpec) deps[dependency] = resolvedDefault(dependency, dependencySpec, registry, next);
159
+ }
160
+ return registry.resolveRef(spec.defaultRef, deps);
161
+ }
162
+
163
+ /** A list default shown as its JSON text (`[]`), as `String([])` would be empty. */
164
+ function listAsJson(value ) {
165
+ return Array.isArray(value) ? JSON.stringify(value) : value;
166
+ }
167
+
168
+ function renderSettingsKeys(root ) {
169
+ // Passing the file system bypasses the loader's per-process cache, so a changed registry is read.
170
+ const loaded = loadSettingsRegistry({ repoRoot: root, fsImpl: fs, verifyRefs: true });
171
+ if (!loaded.ok) throw new Error(loaded.detail);
172
+ const raw = readJson(root, 'core/registry/settings.json');
173
+ return table(['Key', 'Type', 'Default', 'Scope', 'Env', 'Legacy file', 'Description'], Object.entries(raw.keys).map(([key, spec] ) => [
174
+ code(key),
175
+ text(spec.type),
176
+ code(listAsJson(resolvedDefault(key, spec, loaded.registry))),
177
+ text(spec.scope),
178
+ code(spec.env?.name),
179
+ code([...new Set((spec.legacy || []).map((entry ) => entry.path).filter(Boolean))].join(', ')),
180
+ text(spec.description),
181
+ ]));
182
+ }
183
+
184
+ function stripAnsi(text ) {
185
+ return text.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '');
186
+ }
187
+
188
+ // A command row in both help texts is exactly two spaces, the verb, then two or more spaces before
189
+ // the summary; `doflow` may put an argument hint such as `[path]` between the verb and the gap. A
190
+ // line indented further continues the row above it and is not read. Any other line in a command
191
+ // section that is not blank (a verb with no summary, a single-space gap, a tab, a one-space
192
+ // sub-header, an example line naming `doflow` or `doflow-run`) is refused, so a help layout the
193
+ // parser does not understand fails loudly instead of dropping a verb from the index.
194
+ const HELP_ROW = /^ {2}([a-z][a-z0-9-]*)(?: [^ ].*?)? {2,}(\S.*?)\s*$/;
195
+ const NOT_A_VERB = new Set(['doflow', 'doflow-run']);
196
+
197
+ function unreadable(source , line ) {
198
+ return new Error(`${source} --help: cannot read the line ${JSON.stringify(line)}`);
199
+ }
200
+
201
+ function helpRow(line , source ) {
202
+ if (line.trim() === '') return null;
203
+ if (/^ {3,}\S/.test(line)) return null;
204
+ const match = line.match(HELP_ROW);
205
+ // A summary that starts like an argument hint (` install [path]`) means the description is missing.
206
+ if (!match || NOT_A_VERB.has(match[1]) || /^[[<]/.test(match[2])) throw unreadable(source, line);
207
+ return { verb: match[1], summary: match[2] };
208
+ }
209
+
210
+ // `Commands:` ends at a blank line followed by an unindented heading; an unindented line straight
211
+ // after a command row is a heading inside the section, which this parser does not read, so it fails.
212
+ function parseDoflowHelp(text ) {
213
+ const rows = [];
214
+ let inCommands = false;
215
+ let afterBlank = false;
216
+ for (const line of stripAnsi(text).split(/\r?\n/)) {
217
+ if (!inCommands) {
218
+ if (line.trim() === 'Commands:' && /^\S/.test(line)) inCommands = true;
219
+ continue;
220
+ }
221
+ if (line.trim() === '') { afterBlank = true; continue; }
222
+ if (/^\S/.test(line)) {
223
+ if (afterBlank) break;
224
+ throw unreadable(DOFLOW_BIN, line);
225
+ }
226
+ afterBlank = false;
227
+ const row = helpRow(line, DOFLOW_BIN);
228
+ if (row) rows.push(row);
229
+ }
230
+ return rows;
231
+ }
232
+
233
+ // Before the first group heading only unindented prose may appear; after it every unindented line
234
+ // is a group heading ending in a colon, and every indented line belongs to the current group.
235
+ function parseDoflowRunHelp(text ) {
236
+ const rows = [];
237
+ let group = '';
238
+ let seenGroup = false;
239
+ for (const line of stripAnsi(text).split(/\r?\n/)) {
240
+ if (line.trim() === '') continue;
241
+ if (/^\S/.test(line)) {
242
+ if (/:$/.test(line)) { group = line.slice(0, -1); seenGroup = true; continue; }
243
+ if (seenGroup) throw unreadable(DOFLOW_RUN_BIN, line);
244
+ continue;
245
+ }
246
+ if (!seenGroup) throw unreadable(DOFLOW_RUN_BIN, line);
247
+ const row = helpRow(line, DOFLOW_RUN_BIN);
248
+ if (row) rows.push({ ...row, group });
249
+ }
250
+ return rows;
251
+ }
252
+
253
+ function helpFailure(source , run ) {
254
+ const stderr = String(run.stderr || '').trim().split(/\r?\n/).slice(0, 3).join(' / ');
255
+ const why = run.error ? run.error.message : `exit ${run.status === null ? `signal ${run.signal}` : run.status}`;
256
+ return `${source} --help failed (${why})${stderr ? `: ${stderr}` : ''}`;
257
+ }
258
+
259
+ /** Both help texts, captured in a pinned environment; a failed run, an unreadable line or no verb throws. */
260
+ function captureHelp(root ) {
261
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'doflow-docs-blocks-home-'));
262
+ try {
263
+ const env = { PATH: process.env.PATH || '', HOME: home, XDG_CONFIG_HOME: path.join(home, '.config'), NO_COLOR: '1', COLUMNS: '200', LANG: 'C', LC_ALL: 'C', TZ: 'UTC' };
264
+ const run = (command , args ) => spawnSync(command, args, { cwd: root, env, encoding: 'utf8', timeout: 60_000 });
265
+ const doflow = run(process.execPath, [path.join(root, DOFLOW_BIN), '--help']);
266
+ if (doflow.error || doflow.status !== 0) throw new Error(helpFailure(DOFLOW_BIN, doflow));
267
+ const doflowRun = run('bash', [path.join(root, DOFLOW_RUN_BIN), '--help']);
268
+ if (doflowRun.error || doflowRun.status !== 0) throw new Error(helpFailure(DOFLOW_RUN_BIN, doflowRun));
269
+ const result = { doflow: parseDoflowHelp(doflow.stdout), doflowRun: parseDoflowRunHelp(doflowRun.stdout) };
270
+ if (result.doflow.length === 0) throw new Error(`${DOFLOW_BIN} --help yielded no command`);
271
+ if (result.doflowRun.length === 0) throw new Error(`${DOFLOW_RUN_BIN} --help yielded no verb`);
272
+ return result;
273
+ } finally {
274
+ fs.rmSync(home, { recursive: true, force: true });
275
+ }
276
+ }
277
+
278
+ // Cells hold the bare verb under a header naming the command, so no index row starts with
279
+ // "| `doflow <verb>" and a reader that finds a verb's full reference row by that prefix still lands
280
+ // on the hand-written row below the index.
281
+ function renderCommandIndex(root ) {
282
+ const help = captureHelp(root);
283
+ return [
284
+ table(['Command (doflow)', 'Summary'], help.doflow.map((row) => [code(row.verb), text(row.summary)])),
285
+ '',
286
+ table(['Verb (doflow-run)', 'Group', 'Summary'], help.doflowRun.map((row) => [code(row.verb), text(row.group), text(row.summary)])),
287
+ ].join('\n');
288
+ }
289
+
290
+ const BLOCKS = Object.freeze([
291
+ { id: 'capability-matrix', page: 'capability-map.md', sources: ['core/registry/harnesses.json'], render: renderCapabilityMatrix },
292
+ { id: 'enforcement-matrix', page: 'capability-map.md', sources: ['core/registry/lifecycle.json'], render: (root ) => renderEnforcementMatrix(inert(readJson(root, 'core/registry/lifecycle.json').coverage)) },
293
+ { id: 'context-behaviour-matrix', page: 'capability-map.md', sources: ['core/registry/lifecycle.json'], render: (root ) => renderBehaviourMatrix(inert(readJson(root, 'core/registry/lifecycle.json').coverage)) },
294
+ { id: 'task-classes', page: 'how-doflow-work.md', sources: ['core/registry/workflows.json', 'core/registry/readiness-templates.json'], render: renderTaskClasses },
295
+ { id: 'settings-keys', page: 'settings.md', sources: ['core/registry/settings.json'], render: renderSettingsKeys },
296
+ { id: 'command-index', page: 'reference.md', sources: [DOFLOW_BIN, DOFLOW_RUN_BIN], render: renderCommandIndex },
297
+ ]);
298
+
299
+ function renderBlock(id , root ) {
300
+ const block = BLOCKS.find((candidate) => candidate.id === id);
301
+ if (!block) throw new Error(`unknown generated block: ${id}`);
302
+ return block.render(root);
303
+ }
304
+
305
+ // ── Nav and llms.txt ─────────────────────────────────────────────────────────────────────────────
306
+
307
+ /** The mkdocs.yml nav as leaf pages in nav order; `section` is the leaf's top-level entry. */
308
+ function readNav(root ) {
309
+ const lines = fs.readFileSync(path.join(root, 'mkdocs.yml'), 'utf8').split(/\r?\n/);
310
+ const start = lines.findIndex((line) => /^nav:\s*$/.test(line));
311
+ if (start < 0) throw new Error('mkdocs.yml has no nav');
312
+ const pages = [];
313
+ let topIndent = -1;
314
+ let section = '';
315
+ for (const line of lines.slice(start + 1)) {
316
+ if (line.trim() === '' || /^\s*#/.test(line)) continue;
317
+ if (/^\S/.test(line)) break;
318
+ const match = line.match(/^(\s*)-\s+(.+?):\s*(\S.*?)?\s*$/);
319
+ if (!match) continue;
320
+ const indent = match[1].length;
321
+ if (topIndent < 0) topIndent = indent;
322
+ if (indent === topIndent) section = match[2].trim();
323
+ const target = match[3]?.replace(/^["']|["']$/g, '');
324
+ if (target && target.endsWith('.md')) pages.push({ section, title: match[2].trim(), path: target });
325
+ }
326
+ return pages;
327
+ }
328
+
329
+ function siteUrl(root ) {
330
+ const match = fs.readFileSync(path.join(root, 'mkdocs.yml'), 'utf8').match(/^site_url:\s*["']?([^\s"']+)/m);
331
+ if (!match) throw new Error('mkdocs.yml has no site_url');
332
+ return match[1].replace(/\/$/, '');
333
+ }
334
+
335
+ /** The front-matter `description` of a page: the block must open the file. */
336
+ function pageDescription(text ) {
337
+ const front = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
338
+ if (!front) return null;
339
+ const line = front[1].split(/\r?\n/).find((entry) => /^description:/.test(entry));
340
+ if (!line) return null;
341
+ let value = line.slice('description:'.length).trim();
342
+ if (value.length >= 2 && ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))) value = value.slice(1, -1);
343
+ return value || null;
344
+ }
345
+
346
+ /**
347
+ * The llms.txt summary line: the prose is fixed, the facts in it come from their sources (harness
348
+ * names from harnesses.json, the package name and Node range from package.json), so a renamed
349
+ * harness or a new Node floor makes llms.txt stale instead of silently wrong.
350
+ */
351
+ function llmsSummary(root ) {
352
+ const pkg = readJson(root, 'package.json');
353
+ const names = (readJson(root, 'core/registry/harnesses.json').harnesses || []).map((harness ) => oneLine(harness.displayName)).filter(Boolean);
354
+ const range = pkg?.engines?.node;
355
+ if (!pkg?.name || names.length === 0 || typeof range !== 'string') throw new Error('package.json name or engines.node, or the harness list, is missing');
356
+ const list = names.length > 1 ? `${names.slice(0, -1).join(', ')}, and ${names[names.length - 1]}` : names[0];
357
+ const count = NUMBER_WORDS[names.length] || String(names.length);
358
+ return `> DoFlow is a registry-driven installer (\`npx ${pkg.name}\`) that projects one source of engineering guidance, skills, specialist agents, hooks, and MCP configuration into ${count} AI coding tools: ${list}. It needs Node.js ${range}; a source checkout runs its TypeScript through Node's type stripping, and the npm package ships JavaScript built by \`npm run build:package\`.`;
359
+ }
360
+
361
+ function renderLlms(root ) {
362
+ const base = siteUrl(root);
363
+ const sections = new Map ();
364
+ for (const page of readNav(root)) {
365
+ const description = pageDescription(fs.readFileSync(path.join(root, 'docs', page.path), 'utf8'));
366
+ if (!description) throw new Error(`docs/${page.path} has no front-matter description`);
367
+ const url = page.path === 'index.md' ? `${base}/` : `${base}/${page.path.replace(/\.md$/, '')}/`;
368
+ if (!sections.has(page.section)) sections.set(page.section, []);
369
+ sections.get(page.section) .push(`- [${page.title}](${url}): ${description}`);
370
+ }
371
+ const lines = ['# DoFlow', '', llmsSummary(root), ''];
372
+ for (const [section, entries] of sections) lines.push(`## ${section}`, '', ...entries, '');
373
+ return `${lines.join('\n').replace(/\n+$/, '')}\n`;
374
+ }
375
+
376
+ // ── Check and write ──────────────────────────────────────────────────────────────────────────────
377
+
378
+ /**
379
+ * Marker findings for one page: a marker id that is malformed, not registered for this page, unpaired
380
+ * (a lone BEGIN or END, an END before its BEGIN, a doubled pair) or nested inside another span is an
381
+ * orphan. Markers are whole lines only, so marker-shaped text inside a table row is never one.
382
+ */
383
+ function markerFindings(page , text ) {
384
+ const markers = [...text.matchAll(MARKER_LIKE)].map((match) => ({ kind: match[1], id: match[2], index: match.index ?? 0, exact: match[0] === `<!-- ${match[1]} GENERATED:${match[2]} -->` }));
385
+ const bad = new Set (markers.filter((marker) => !marker.exact).map((marker) => marker.id));
386
+ const counts = new Map ();
387
+ let open = null;
388
+ for (const marker of markers) {
389
+ const entry = counts.get(marker.id) || { begin: 0, end: 0 };
390
+ if (marker.kind === 'BEGIN') {
391
+ entry.begin += 1;
392
+ if (open !== null) { bad.add(open); bad.add(marker.id); }
393
+ open = marker.id;
394
+ } else {
395
+ entry.end += 1;
396
+ if (open !== marker.id) bad.add(marker.id);
397
+ if (open === marker.id) open = null;
398
+ }
399
+ counts.set(marker.id, entry);
400
+ }
401
+ if (open !== null) bad.add(open);
402
+ const findings = [];
403
+ for (const [id, { begin, end }] of counts) {
404
+ const registered = MARKER_ID.test(id) && BLOCKS.some((block) => block.id === id && block.page === page);
405
+ if (!registered || begin !== 1 || end !== 1 || bad.has(id)) findings.push({ page, id, kind: 'orphan' });
406
+ }
407
+ return findings;
408
+ }
409
+
410
+ /** The text between a block's marker lines, or null when the pair is absent or malformed. */
411
+ function spanOf(body , id ) {
412
+ // Markers count only as whole lines (MARKER_LINE), never as text inside a line.
413
+ const lines = [...body.matchAll(MARKER_LINE)].filter((match) => match[2] === id);
414
+ if (lines.length !== 2 || lines[0][1] !== 'BEGIN' || lines[1][1] !== 'END') return null;
415
+ const start = (lines[0].index ?? 0) + lines[0][0].length + 1;
416
+ const end = lines[1].index ?? 0;
417
+ return end >= start ? { start, end } : null;
418
+ }
419
+
420
+ function readDoc(root , page ) {
421
+ try { return fs.readFileSync(path.join(root, 'docs', page), 'utf8'); } catch { return null; }
422
+ }
423
+
424
+ /** Every finding for the registered blocks and llms.txt; empty when the docs are current. */
425
+ function checkAll(root ) {
426
+ const findings = [];
427
+ const pages = [...new Set(BLOCKS.map((block) => block.page))];
428
+ const texts = new Map(pages.map((page) => [page, readDoc(root, page)]));
429
+ for (const page of pages) {
430
+ const text = texts.get(page);
431
+ if (text !== null && text !== undefined) findings.push(...markerFindings(page, text));
432
+ }
433
+ for (const block of BLOCKS) {
434
+ const text = texts.get(block.page);
435
+ const span = typeof text === 'string' ? spanOf(text, block.id) : null;
436
+ if (!span) {
437
+ if (!findings.some((f) => f.page === block.page && f.id === block.id)) findings.push({ page: block.page, id: block.id, kind: 'missing' });
438
+ continue;
439
+ }
440
+ let expected ;
441
+ try { expected = block.render(root); } catch (error ) { findings.push({ page: block.page, id: block.id, kind: 'source-unreadable', detail: error?.message }); continue; }
442
+ if ((text ).slice(span.start, span.end) !== `${expected}\n`) findings.push({ page: block.page, id: block.id, kind: 'stale' });
443
+ }
444
+ const committed = readDoc(root, 'llms.txt');
445
+ let expected = null;
446
+ try { expected = renderLlms(root); } catch (error ) { findings.push({ page: 'llms.txt', id: 'llms.txt', kind: 'source-unreadable', detail: error?.message }); }
447
+ if (committed === null) findings.push({ page: 'llms.txt', id: 'llms.txt', kind: 'missing' });
448
+ else {
449
+ if (expected !== null && committed !== expected) findings.push({ page: 'llms.txt', id: 'llms.txt', kind: 'stale' });
450
+ if (Buffer.byteLength(committed) > LLMS_CAP) findings.push({ page: 'llms.txt', id: 'llms.txt', kind: 'over-cap' });
451
+ }
452
+ if (expected !== null && Buffer.byteLength(expected) > LLMS_CAP && !findings.some((f) => f.id === 'llms.txt' && f.kind === 'over-cap')) {
453
+ findings.push({ page: 'llms.txt', id: 'llms.txt', kind: 'over-cap' });
454
+ }
455
+ return findings;
456
+ }
457
+
458
+ /** Rewrites every well-formed span and llms.txt, then returns what `checkAll` still finds. */
459
+ function writeAll(root ) {
460
+ for (const block of BLOCKS) {
461
+ const file = path.join(root, 'docs', block.page);
462
+ const text = readDoc(root, block.page);
463
+ if (text === null || markerFindings(block.page, text).some((f) => f.id === block.id)) continue;
464
+ const span = spanOf(text, block.id);
465
+ if (!span) continue;
466
+ let rendered ;
467
+ try { rendered = block.render(root); } catch { continue; }
468
+ const next = `${text.slice(0, span.start)}${rendered}\n${text.slice(span.end)}`;
469
+ if (next !== text) fs.writeFileSync(file, next);
470
+ }
471
+ try {
472
+ const llms = renderLlms(root);
473
+ if (readDoc(root, 'llms.txt') !== llms) fs.writeFileSync(path.join(root, 'docs', 'llms.txt'), llms);
474
+ } catch { /* reported by checkAll as source-unreadable */ }
475
+ return checkAll(root);
476
+ }
477
+
478
+ /** The CLI: 0 current (or written clean), 1 findings, 2 usage. */
479
+ function main(argv , root = REPO_ROOT) {
480
+ if (argv.length !== 1 || (argv[0] !== '--write' && argv[0] !== '--check')) {
481
+ process.stderr.write(USAGE);
482
+ return 2;
483
+ }
484
+ const findings = argv[0] === '--write' ? writeAll(root) : checkAll(root);
485
+ for (const finding of findings) process.stderr.write(`docs-blocks: ${finding.page}: ${finding.id}: ${finding.kind}${finding.detail ? ` (${oneLine(finding.detail)})` : ''}\n`);
486
+ if (findings.length && argv[0] === '--check') process.stderr.write('docs-blocks: run node src/release/docs-blocks.js --write, then review the diff\n');
487
+ return findings.length ? 1 : 0;
488
+ }
489
+
490
+ module.exports = { BLOCKS, LLMS_CAP, MARKER_ID, markerFindings, text, code, inert, renderBlock, renderLlms, readNav, captureHelp, pageDescription, checkAll, writeAll, main };
491
+
492
+ if (require.main === module) process.exitCode = main(process.argv.slice(2));