@khoavu882/doflow 2.0.0-beta.5 → 2.0.1

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 (494) hide show
  1. package/README.md +16 -16
  2. package/bin/doflow.js +22 -12
  3. package/core/.antigravity-plugin/plugin.json +1 -2
  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 +4 -1
  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-compact.sh +23 -27
  15. package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
  16. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  17. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  18. package/core/harnesses/claude/settings/settings.json +26 -9
  19. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  20. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  21. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  22. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  23. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  24. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  25. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  26. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  27. package/core/harnesses/codex/hooks/hooks.json +0 -10
  28. package/core/harnesses/codex/hooks/lib.sh +44 -25
  29. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
  30. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  31. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  32. package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
  33. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  34. package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
  35. package/core/harnesses/copilot/hooks/doflow-lib.sh +94 -0
  36. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  37. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  38. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  39. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  40. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  41. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  42. package/core/harnesses/copilot/hooks/doflow.json +51 -0
  43. package/core/harnesses/kiro/agents/doflow.json +1 -11
  44. package/core/harnesses/kiro/hooks/doflow.json +0 -10
  45. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  46. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +8 -3
  47. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +8 -3
  48. package/core/harnesses/kiro/hooks/session-start.sh +1 -0
  49. package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
  50. package/core/harnesses/pi/extensions/doflow-guard.js +11 -2
  51. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -2
  52. package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
  53. package/core/harnesses/shared/hooks/bridge/bridge.sh +126 -23
  54. package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
  55. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  56. package/core/harnesses/shared/hooks/bridge/respond.sh +16 -2
  57. package/core/harnesses/shared/hooks/policies/capture-failure.sh +49 -16
  58. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  59. package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
  60. package/core/harnesses/shared/hooks/policies/deny-json.sh +20 -7
  61. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  62. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  63. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  64. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  65. package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
  66. package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
  67. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +7 -8
  68. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +132 -57
  69. package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
  70. package/core/harnesses/shared/hooks/policies/stop-check.sh +27 -15
  71. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +6 -3
  72. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  73. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
  74. package/core/harnesses/shared/hooks/stream-hook-runner.js +58 -20
  75. package/core/harnesses/shared/locator/doflow-run +2 -3
  76. package/core/registry/advice.json +3 -3
  77. package/core/registry/assets.json +55 -82
  78. package/core/registry/capabilities.json +12 -0
  79. package/core/registry/contracts.json +58 -31
  80. package/core/registry/contribution-points.json +35 -0
  81. package/core/registry/gates.json +32 -0
  82. package/core/registry/guidance-caps.json +40 -0
  83. package/core/registry/harnesses.json +1445 -366
  84. package/core/registry/intake.json +53 -0
  85. package/core/registry/lifecycle.json +599 -996
  86. package/core/registry/mcp.json +5 -1
  87. package/core/registry/models.json +7 -1
  88. package/core/registry/package-templates/README.md +19 -0
  89. package/core/registry/package-templates/agent/files/agent.md +9 -0
  90. package/core/registry/package-templates/agent/template.json +4 -0
  91. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  92. package/core/registry/package-templates/capability-provider/template.json +11 -0
  93. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  94. package/core/registry/package-templates/debate-preset/template.json +4 -0
  95. package/core/registry/package-templates/intake-category/files/category.json +8 -0
  96. package/core/registry/package-templates/intake-category/template.json +4 -0
  97. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  98. package/core/registry/package-templates/review-preset/template.json +4 -0
  99. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  100. package/core/registry/package-templates/skill/template.json +4 -0
  101. package/core/registry/package-templates/worker/files/worker.json +92 -0
  102. package/core/registry/package-templates/worker/template.json +11 -0
  103. package/core/registry/packages/doflow/doflow-package.json +15 -0
  104. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  105. package/core/registry/protocols.json +61 -0
  106. package/core/registry/routes.json +17 -0
  107. package/core/registry/rules.json +431 -0
  108. package/core/registry/settings.json +272 -0
  109. package/core/registry/storage.json +687 -31
  110. package/core/registry/telemetry.json +236 -0
  111. package/core/registry/transcripts.json +142 -0
  112. package/core/registry/verification.json +76 -6
  113. package/core/registry/workflows.json +55 -0
  114. package/core/shared/agent-specs/core-implementer.md +3 -0
  115. package/core/shared/agent-specs/delivery-lead.md +68 -0
  116. package/core/shared/agent-specs/quality-guardian.md +5 -1
  117. package/core/shared/agent-specs/research-writer.md +10 -3
  118. package/core/shared/agent-specs/reviewer.md +53 -0
  119. package/core/shared/agent-specs/spec-analyst.md +5 -6
  120. package/core/shared/agent-specs/system-architect.md +5 -1
  121. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  122. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  123. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  124. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  125. package/core/shared/guidance/pointers/codex.md +1 -4
  126. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  127. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  128. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  129. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  130. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  131. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  132. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  133. package/core/shared/guidance/references/ORCHESTRATION.md +3 -2
  134. package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
  135. package/core/shared/guidance/rules/RULES.md +45 -0
  136. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  137. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  138. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  139. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  140. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  141. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  142. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  143. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  144. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  145. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  146. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  147. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +49 -5
  148. package/core/shared/scripts/doflow/bin/doflow-run +152 -34
  149. package/core/shared/skills/do/SKILL.md +14 -12
  150. package/core/shared/skills/do/references/advice_calls.md +26 -0
  151. package/core/shared/skills/do/references/pm_routing.md +1 -2
  152. package/core/shared/skills/do/references/program_mode.md +31 -0
  153. package/core/shared/skills/do-brainstorm/SKILL.md +45 -97
  154. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +36 -0
  155. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  156. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  157. package/core/shared/skills/do-code-review/SKILL.md +48 -148
  158. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  159. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  160. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  161. package/core/shared/skills/do-design/SKILL.md +23 -20
  162. package/core/shared/skills/do-diagnose/SKILL.md +18 -6
  163. package/core/shared/skills/do-document/SKILL.md +12 -1
  164. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  165. package/core/shared/skills/do-document/references/embedded-research.md +19 -7
  166. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  167. package/core/shared/skills/do-execute-plan/SKILL.md +19 -60
  168. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  169. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  170. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
  171. package/core/shared/skills/do-execute-plan/references/run_position.md +16 -2
  172. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  173. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  174. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  175. package/core/shared/skills/do-flow/SKILL.md +10 -11
  176. package/core/shared/skills/do-flow/references/task_classes.md +5 -5
  177. package/core/shared/skills/do-git/SKILL.md +4 -0
  178. package/core/shared/skills/do-implement/SKILL.md +13 -14
  179. package/core/shared/skills/do-plan/SKILL.md +34 -20
  180. package/core/shared/skills/do-test/SKILL.md +7 -4
  181. package/core/shared/templates/doflow/constitution-template.md +9 -3
  182. package/core/shared/templates/doflow/data-model-template.md +1 -1
  183. package/core/shared/templates/doflow/design-template.md +113 -16
  184. package/core/shared/templates/doflow/plan-template.md +11 -10
  185. package/core/shared/templates/doflow/question-log-template.md +14 -2
  186. package/core/shared/templates/doflow/requirement-template.md +1 -1
  187. package/core/shared/workflows/do-flow-chain.md +2 -0
  188. package/core/worker-evidence/antigravity/checks.json +14 -0
  189. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  190. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  191. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  192. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  193. package/core/worker-evidence/codex/checks.json +38 -0
  194. package/package.json +10 -6
  195. package/src/adapters/antigravity/index.js +143 -66
  196. package/src/adapters/claude/index.js +46 -5
  197. package/src/adapters/codex/agents.js +25 -15
  198. package/src/adapters/codex/hooks.js +39 -3
  199. package/src/adapters/codex/index.js +61 -13
  200. package/src/adapters/codex/native-projection.js +6 -2
  201. package/src/adapters/copilot/index.js +58 -23
  202. package/src/adapters/copy-tree.js +291 -46
  203. package/src/adapters/guidance-manifest.js +71 -0
  204. package/src/adapters/guidance-pointer.js +19 -0
  205. package/src/adapters/hook-commands.js +6 -10
  206. package/src/adapters/index.js +1 -0
  207. package/src/adapters/kiro/index.js +23 -12
  208. package/src/adapters/opencode/index.js +8 -8
  209. package/src/adapters/pi/hooks.js +2 -1
  210. package/src/adapters/pi/index.js +14 -7
  211. package/src/adapters/resolve-source.js +26 -2
  212. package/src/cli/commands/install.js +2 -1
  213. package/src/cli/commands/reconcile.js +53 -9
  214. package/src/cli/commands/remove.js +2 -2
  215. package/src/cli/commands/rollback.js +6 -3
  216. package/src/cli/commands/self-update.js +18 -9
  217. package/src/cli/commands/status.js +22 -3
  218. package/src/cli/commands/ui.js +99 -0
  219. package/src/cli/commands/update.js +2 -1
  220. package/src/cli/index.js +337 -32
  221. package/src/cli/runtime-commands.js +220 -60
  222. package/src/cli/shared.js +0 -2
  223. package/src/helper/git.js +1 -1
  224. package/src/helper/guardrails.js +1 -1
  225. package/src/helper/marker-merge.js +31 -13
  226. package/src/helper/package-ids.js +37 -0
  227. package/src/helper/semver.js +94 -0
  228. package/src/install/backup.js +105 -15
  229. package/src/install/context.js +1 -1
  230. package/src/install/targets.js +31 -16
  231. package/src/lifecycle/coverage.js +4 -16
  232. package/src/lifecycle/hook-tier.js +160 -0
  233. package/src/lifecycle/index.js +412 -78
  234. package/src/lifecycle/retired-harnesses.js +318 -0
  235. package/src/lifecycle/view.js +35 -15
  236. package/src/registry/index.js +551 -26
  237. package/src/registry/worker-ids.js +45 -0
  238. package/src/release/build-package.js +0 -3
  239. package/src/release/docs-blocks.js +472 -0
  240. package/src/release/preflight.js +425 -0
  241. package/src/release/rehearse.js +401 -0
  242. package/src/release/release-workflow.js +733 -0
  243. package/src/release/sync-plugin-versions.js +43 -7
  244. package/src/runtime/advice/bench-gate.js +25 -0
  245. package/src/runtime/advice/cli.js +59 -16
  246. package/src/runtime/advice/client.js +32 -9
  247. package/src/runtime/advice/config.js +98 -133
  248. package/src/runtime/advice/doctor.js +1 -1
  249. package/src/runtime/advice/elevation.js +97 -0
  250. package/src/runtime/advice/endpoint-guard.js +9 -3
  251. package/src/runtime/advice/mode.js +37 -0
  252. package/src/runtime/advice/record.js +18 -5
  253. package/src/runtime/advice/registry.js +4 -1
  254. package/src/runtime/advice/shadow-config.js +229 -0
  255. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  256. package/src/runtime/brief-rules.js +81 -0
  257. package/src/runtime/brief-tools.js +111 -34
  258. package/src/runtime/candidate.js +20 -11
  259. package/src/runtime/capabilities/executor.js +450 -0
  260. package/src/runtime/capabilities/loopback.js +41 -0
  261. package/src/runtime/capabilities/sanitizer.js +88 -0
  262. package/src/runtime/capability-router.js +135 -3
  263. package/src/runtime/checkouts.js +73 -11
  264. package/src/runtime/claims.js +13 -7
  265. package/src/runtime/cli.js +196 -19
  266. package/src/runtime/config-home.js +42 -0
  267. package/src/runtime/context/arbitrate.js +767 -0
  268. package/src/runtime/context/delivery-store.js +4 -6
  269. package/src/runtime/context/emission-rules.js +118 -0
  270. package/src/runtime/context/identity.js +227 -0
  271. package/src/runtime/context/pointer.js +67 -12
  272. package/src/runtime/context/primary-command.js +117 -0
  273. package/src/runtime/context/primary.js +344 -0
  274. package/src/runtime/context/summary-store.js +655 -0
  275. package/src/runtime/context-pack.js +158 -79
  276. package/src/runtime/decision-register.js +13 -4
  277. package/src/runtime/dispatch/admission.js +392 -0
  278. package/src/runtime/dispatch/attempt-end.js +274 -0
  279. package/src/runtime/dispatch/command.js +628 -0
  280. package/src/runtime/dispatch/controls.js +904 -0
  281. package/src/runtime/dispatch/envelope.js +276 -0
  282. package/src/runtime/dispatch/grants.js +310 -0
  283. package/src/runtime/dispatch/integrate.js +617 -0
  284. package/src/runtime/dispatch/loop.js +1835 -0
  285. package/src/runtime/dispatch/store.js +352 -0
  286. package/src/runtime/dispatch/verify-run.js +201 -0
  287. package/src/runtime/evidence-ledger.js +52 -7
  288. package/src/runtime/failure/capture.js +3 -1
  289. package/src/runtime/failure/cli.js +25 -9
  290. package/src/runtime/failure/home.js +57 -30
  291. package/src/runtime/failure/store.js +7 -2
  292. package/src/runtime/feature-pointer.js +174 -0
  293. package/src/runtime/feature-resolve.js +13 -2
  294. package/src/runtime/fleet/command.js +77 -0
  295. package/src/runtime/fleet/projects.js +178 -0
  296. package/src/runtime/fleet/tree.js +383 -0
  297. package/src/runtime/freshness.js +33 -3
  298. package/src/runtime/git-memo.js +46 -0
  299. package/src/runtime/guidance/ablation.js +427 -0
  300. package/src/runtime/health.js +624 -18
  301. package/src/runtime/history-compactor.js +13 -3
  302. package/src/runtime/implementation-gate.js +281 -26
  303. package/src/runtime/intake/engine.js +677 -0
  304. package/src/runtime/intake/index.js +136 -0
  305. package/src/runtime/intake/manifest.js +140 -0
  306. package/src/runtime/intake/taxonomy.js +206 -0
  307. package/src/runtime/inventory/coverage.js +1 -1
  308. package/src/runtime/inventory/index.js +1 -1
  309. package/src/runtime/inventory/precedence.js +2 -2
  310. package/src/runtime/inventory/siblings.js +6 -4
  311. package/src/runtime/knowledge/code-corpus.js +277 -0
  312. package/src/runtime/knowledge/code-lexical.js +130 -0
  313. package/src/runtime/knowledge/code-retrieval.js +307 -0
  314. package/src/runtime/knowledge/code-validation.js +106 -0
  315. package/src/runtime/knowledge/gp3-gate.js +74 -0
  316. package/src/runtime/knowledge/graph-adapter.js +288 -0
  317. package/src/runtime/knowledge/graph-fallback.js +394 -0
  318. package/src/runtime/knowledge/hybrid.js +136 -8
  319. package/src/runtime/knowledge/retrieval.js +34 -1
  320. package/src/runtime/knowledge/rtk-compress.js +77 -0
  321. package/src/runtime/knowledge/semble-bridge.py +106 -0
  322. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  323. package/src/runtime/knowledge/semble-provider.js +365 -0
  324. package/src/runtime/lifecycle/cli.js +88 -10
  325. package/src/runtime/lifecycle/event-store.js +8 -6
  326. package/src/runtime/lifecycle/followup.js +38 -7
  327. package/src/runtime/lifecycle/overview.js +26 -2
  328. package/src/runtime/lifecycle/report-store.js +8 -10
  329. package/src/runtime/lifecycle/retention.js +95 -31
  330. package/src/runtime/lifecycle/root.js +9 -1
  331. package/src/runtime/lifecycle/status.js +34 -17
  332. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  333. package/src/runtime/mask.js +134 -7
  334. package/src/runtime/mcp-consultations.js +40 -6
  335. package/src/runtime/model-router.js +79 -7
  336. package/src/runtime/models/catalogue.js +191 -0
  337. package/src/runtime/models/command.js +153 -0
  338. package/src/runtime/off-words.js +12 -0
  339. package/src/runtime/outcome.js +2 -2
  340. package/src/runtime/packages/command.js +1009 -0
  341. package/src/runtime/packages/contributions.js +116 -0
  342. package/src/runtime/packages/digest.js +132 -0
  343. package/src/runtime/packages/fetch.js +182 -0
  344. package/src/runtime/packages/grants.js +192 -0
  345. package/src/runtime/packages/index-file.js +134 -0
  346. package/src/runtime/packages/instruction.js +90 -0
  347. package/src/runtime/packages/manifest.js +331 -0
  348. package/src/runtime/packages/points.js +487 -0
  349. package/src/runtime/packages/programs.js +216 -0
  350. package/src/runtime/packages/scaffold.js +277 -0
  351. package/src/runtime/packages/source.js +90 -0
  352. package/src/runtime/packages/state.js +164 -0
  353. package/src/runtime/packages/store.js +449 -0
  354. package/src/runtime/packages/tar.js +106 -0
  355. package/src/runtime/packages/view.js +406 -0
  356. package/src/runtime/plan-check.js +59 -0
  357. package/src/runtime/plan-content.js +324 -0
  358. package/src/runtime/policy-init.js +118 -0
  359. package/src/runtime/profiles/doctor.js +96 -0
  360. package/src/runtime/profiles/resolve.js +244 -0
  361. package/src/runtime/profiles/rollback.js +254 -0
  362. package/src/runtime/profiles/sampler.js +41 -0
  363. package/src/runtime/readiness-record.js +16 -11
  364. package/src/runtime/readiness.js +46 -5
  365. package/src/runtime/research-request.js +50 -25
  366. package/src/runtime/retrieval-plan.js +10 -30
  367. package/src/runtime/retrieval-record.js +75 -5
  368. package/src/runtime/review/command.js +246 -0
  369. package/src/runtime/review/dominance.js +68 -0
  370. package/src/runtime/review/findings.js +434 -0
  371. package/src/runtime/review/identity.js +255 -0
  372. package/src/runtime/review/protocol-command.js +109 -0
  373. package/src/runtime/review/protocols.js +405 -0
  374. package/src/runtime/review/resolve-protocol.js +302 -0
  375. package/src/runtime/review/start.js +530 -0
  376. package/src/runtime/roadmap/attendance.js +137 -0
  377. package/src/runtime/roadmap/facts.js +479 -0
  378. package/src/runtime/roadmap/gates.js +273 -0
  379. package/src/runtime/roadmap/loop.js +1719 -0
  380. package/src/runtime/roadmap/merge.js +390 -0
  381. package/src/runtime/roadmap/rows.js +1122 -0
  382. package/src/runtime/roadmap/seed.js +1063 -0
  383. package/src/runtime/roadmap/worktree-cleanup.js +147 -0
  384. package/src/runtime/rules-report.js +336 -0
  385. package/src/runtime/scaffold/artifacts.js +60 -9
  386. package/src/runtime/scaffold/generate.js +21 -9
  387. package/src/runtime/settings/autopilot.js +106 -0
  388. package/src/runtime/settings/cli.js +280 -0
  389. package/src/runtime/settings/config-write.js +314 -0
  390. package/src/runtime/settings/files.js +271 -0
  391. package/src/runtime/settings/legacy.js +154 -0
  392. package/src/runtime/settings/lexical.js +56 -0
  393. package/src/runtime/settings/prepared-run.js +269 -0
  394. package/src/runtime/settings/project-layers.js +118 -0
  395. package/src/runtime/settings/refs.js +288 -0
  396. package/src/runtime/settings/registry.js +553 -0
  397. package/src/runtime/settings/resolve.js +713 -0
  398. package/src/runtime/settings/shared.js +37 -0
  399. package/src/runtime/settings/worker-billing.js +107 -0
  400. package/src/runtime/settings/write.js +269 -0
  401. package/src/runtime/slot-command.js +78 -0
  402. package/src/runtime/slots.js +671 -0
  403. package/src/runtime/source-version.js +1 -0
  404. package/src/runtime/state-root.js +217 -0
  405. package/src/runtime/storage/cli.js +12 -5
  406. package/src/runtime/storage/engine.js +9 -2
  407. package/src/runtime/storage/registry.js +14 -1
  408. package/src/runtime/storage/roots.js +66 -16
  409. package/src/runtime/storage/rules.js +82 -10
  410. package/src/runtime/storage/upkeep.js +10 -2
  411. package/src/runtime/storage/work-status.js +245 -26
  412. package/src/runtime/task-attempts.js +589 -69
  413. package/src/runtime/task-classifier.js +85 -14
  414. package/src/runtime/task-graph.js +373 -0
  415. package/src/runtime/task-run.js +778 -0
  416. package/src/runtime/task-scope.js +176 -55
  417. package/src/runtime/task-sources.js +147 -0
  418. package/src/runtime/task-state.js +373 -64
  419. package/src/runtime/team/admission.js +301 -0
  420. package/src/runtime/team/envelope.js +324 -0
  421. package/src/runtime/team/review-guard.js +156 -0
  422. package/src/runtime/trace/analyze.js +427 -0
  423. package/src/runtime/trace/baseline.js +770 -0
  424. package/src/runtime/trace/ledger.js +63 -140
  425. package/src/runtime/trace/otlp-export.js +568 -0
  426. package/src/runtime/trace/otlp-map.js +481 -0
  427. package/src/runtime/trace/spans.js +745 -0
  428. package/src/runtime/trace/telemetry.js +225 -0
  429. package/src/runtime/trace/views.js +1 -0
  430. package/src/runtime/transcripts/command.js +98 -0
  431. package/src/runtime/transcripts/cursor.js +326 -0
  432. package/src/runtime/transcripts/read.js +126 -0
  433. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  434. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  435. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  436. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  437. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  438. package/src/runtime/transcripts/registry.js +161 -0
  439. package/src/runtime/transcripts/stats.js +161 -0
  440. package/src/runtime/verb-view.js +267 -0
  441. package/src/runtime/verification/check-key.js +628 -0
  442. package/src/runtime/verification/contract-runner.js +546 -11
  443. package/src/runtime/verification/engine.js +499 -54
  444. package/src/runtime/verification/impact.js +617 -0
  445. package/src/runtime/verification/inflight.js +205 -0
  446. package/src/runtime/verification/registry.js +176 -0
  447. package/src/runtime/verification/reuse.js +184 -0
  448. package/src/runtime/verification/telemetry-emit.js +254 -0
  449. package/src/runtime/verification/test-selection.js +67 -7
  450. package/src/runtime/worker/active.js +90 -0
  451. package/src/runtime/worker/cancel.js +222 -0
  452. package/src/runtime/worker/catalogue.js +122 -0
  453. package/src/runtime/worker/command.js +677 -0
  454. package/src/runtime/worker/evidence.js +288 -0
  455. package/src/runtime/worker/launch.js +256 -0
  456. package/src/runtime/worker/models.js +137 -0
  457. package/src/runtime/worker/parsers.js +218 -0
  458. package/src/runtime/worker/probes/index.js +706 -0
  459. package/src/runtime/worker/process-tree.js +415 -0
  460. package/src/runtime/worker/quota.js +43 -0
  461. package/src/runtime/worker/readiness.js +713 -0
  462. package/src/runtime/worker/run-store.js +166 -0
  463. package/src/runtime/worker/supervisor.js +541 -0
  464. package/src/runtime/worker/worktree-check.js +252 -0
  465. package/src/runtime/workflow-engine.js +5 -1
  466. package/src/runtime/workflow-orchestrator.js +610 -67
  467. package/src/runtime/worktree.js +149 -22
  468. package/src/state/index.js +1 -1
  469. package/src/state/lockfile.js +64 -10
  470. package/src/state/run-lock.js +1 -1
  471. package/types/node-commonjs.d.ts +106 -7
  472. package/core/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
  473. package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
  474. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  475. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  476. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
  477. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  478. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
  479. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  480. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
  481. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  482. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  483. package/core/harnesses/gemini/hooks/session-start.sh +0 -7
  484. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  485. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -24
  486. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -91
  487. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -378
  488. package/core/shared/guidance/FLAGS.md +0 -25
  489. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  490. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  491. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  492. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  493. package/src/adapters/gemini/hooks.js +0 -251
  494. package/src/adapters/gemini/index.js +0 -429
@@ -336,13 +336,13 @@ function planSourceStubs({ byPath, scaffoldDir, repoRoot, fsImpl, slug, message,
336
336
  ? reqs.map((r ) => `requirement: ${r.id} — ${r.summary}`)
337
337
  : ['requirement: none — no user story on the owning task resolves to a requirement']),
338
338
  ...(comps.length
339
- ? comps.map((c ) => `component: ${c.id} — ${c.name} (design.md §3)`)
339
+ ? comps.map((c ) => `component: ${c.id} — ${c.name} (design.md Components & Boundaries)`)
340
340
  : ['component: none — no design component serves this task\'s requirements']),
341
341
  ];
342
342
 
343
343
  const bodyParts = [''];
344
344
  if (declared.length) {
345
- header.push(`shape: declared in design.md §4 (${[...new Set(declared.map((d ) => d.component))].join(', ')}), quoted below`);
345
+ header.push(`shape: declared in design.md API / Interface Contracts (${[...new Set(declared.map((d ) => d.component))].join(', ')}), quoted below`);
346
346
  for (const decl of declared) {
347
347
  const lines = decl.block.split('\n');
348
348
  const shown = lines.slice(0, 40);
@@ -354,11 +354,11 @@ function planSourceStubs({ byPath, scaffoldDir, repoRoot, fsImpl, slug, message,
354
354
  ].join('\n'));
355
355
  }
356
356
  } else {
357
- header.push('shape: not declared in design.md §4 — placeholder signature below');
357
+ header.push('shape: not declared in design.md API / Interface Contracts — placeholder signature below');
358
358
  // Not a skip — the file was emitted. Conflating the two would let 29 "we wrote it, but the
359
359
  // design never said what it should look like" rows hide inside a skip count, which is the
360
360
  // opposite of what the manifest is for.
361
- shapeUndeclared.push({ path: path.join('src', file) + STUB_SUFFIX, why: 'design.md §4 declares no interface for this file\'s component — the signature below is a placeholder, not a contract' });
361
+ shapeUndeclared.push({ path: path.join('src', file) + STUB_SUFFIX, why: 'design.md API / Interface Contracts declares no interface for this file\'s component — the signature below is a placeholder, not a contract' });
362
362
  }
363
363
 
364
364
  const pkg = snake(words(path.basename(path.dirname(file)) || 'scaffold')).replace(/_/g, '');
@@ -422,7 +422,7 @@ function planTestStubs({ requirement, componentsForRequirement, language, slug,
422
422
  'reads as coverage. Replace the body, do not delete the failure.',
423
423
  '',
424
424
  `requirement: ${id}${req ? ` — ${req.summary}` : ' — not present in the requirement index'}`,
425
- ...(comps.length ? comps.map((c ) => `component: ${c.id} — ${c.name} (design.md §3)`) : []),
425
+ ...(comps.length ? comps.map((c ) => `component: ${c.id} — ${c.name} (design.md Components & Boundaries)`) : []),
426
426
  `criteria: ${cases.length} from requirement.md §6`,
427
427
  `language: ${language.id} (detected from ${language.signal} by command-detect.ts)`,
428
428
  ];
@@ -442,15 +442,27 @@ function planTestStubs({ requirement, componentsForRequirement, language, slug,
442
442
  /**
443
443
  * contracts/ — partitioned here, generated by the prose algorithm. See the module header for why
444
444
  * the walk-up is not reimplemented in code.
445
+ * A task-id `depends-on:` value (`taskDeps`) is an edge in the task graph, not a service, so it
446
+ * gets no frame. A task whose `external-contract:` is left with no external value after that
447
+ * filter is pushed onto `notEvaluated` as `external-contract-orphaned` rather than dropped (IC-001).
445
448
  * @param {Array<Object>} tasks from `plan.tasks`
446
449
  * @param {Set<string>} ownedPaths every path a task in this plan already owns
450
+ * @param {Array<Object>} notEvaluated mutated with one entry per orphaned `external-contract:`
447
451
  * @returns {Array<Object>} externalDependencies
448
452
  */
449
- function partitionExternalDependencies(tasks , ownedPaths ) {
453
+ function partitionExternalDependencies(tasks , ownedPaths , notEvaluated ) {
450
454
  const externalDependencies = [];
451
455
  const dependencyTasks = new Map ();
452
456
  for (const task of tasks) {
453
- for (const dep of task.dependsOn) {
457
+ const taskDeps = new Set(task.taskDeps || []);
458
+ const external = task.dependsOn.filter((dep ) => !taskDeps.has(dep));
459
+ if (task.externalContract && external.length === 0) {
460
+ notEvaluated.push({
461
+ what: `plan.md → ${task.id} external-contract: ${task.externalContract}`,
462
+ why: 'external-contract-orphaned — no external depends-on: value remains once task-id dependencies are removed, so no contract frame is generated from it',
463
+ });
464
+ }
465
+ for (const dep of external) {
454
466
  if (!dependencyTasks.has(dep)) dependencyTasks.set(dep, []);
455
467
  dependencyTasks.get(dep) .push(task);
456
468
  }
@@ -714,7 +726,7 @@ function generateScaffold(options = {}) {
714
726
  const planned = new Map([...sourcePlanned, ...testPlanned]);
715
727
 
716
728
  const ownedPaths = new Set([...byPath.keys()]);
717
- const externalDependencies = partitionExternalDependencies(plan.tasks, ownedPaths);
729
+ const externalDependencies = partitionExternalDependencies(plan.tasks, ownedPaths, notEvaluated);
718
730
 
719
731
  const { written, unchanged, preserved } = applyPlannedFiles({ planned, blocked, scaffoldDir, fsImpl });
720
732
 
@@ -777,7 +789,7 @@ function renderManifest(result , planned ) {
777
789
  '_Nothing generated._');
778
790
 
779
791
  lines.push('## Emitted without a declared shape', '');
780
- lines.push('These files exist and are traceable, but `design.md` §4 never declared an interface for',
792
+ lines.push('These files exist and are traceable, but `design.md` API / Interface Contracts never declared an interface for',
781
793
  'them, so their signature is a placeholder rather than a contract. Reviewing the placeholder as',
782
794
  'if it were the designed API is the mistake this section exists to prevent.', '');
783
795
  table(['Path', 'Why'], result.shapeUndeclared.map((s ) => [s.path, s.why]),
@@ -0,0 +1,106 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * The roadmap autopilot kill switch (feature 108 IC-011, FR-016; decisions C9, C4, C20). It lives
5
+ * with the settings it reads, so the orchestrator and the roadmap loop share one reader without the
6
+ * orchestrator importing the loop.
7
+ */
8
+
9
+ const fs = require('node:fs');
10
+ const path = require('node:path');
11
+
12
+
13
+
14
+ /** `<config home>/autopilot/off`, or null with no config home (which already reads as off). */
15
+ function sentinelFile(env ) {
16
+ const { configHome } = require('../config-home.js');
17
+ const home = configHome(env).dir;
18
+ return home ? path.join(home, 'autopilot', 'off') : null;
19
+ }
20
+
21
+ /**
22
+ * Read fresh on every call (never memoised: a copied env defeats the resolver cache), so a switch
23
+ * set between two boundaries stops the next one. Any layer saying off wins and is named, in the
24
+ * order env (DOFLOW_AUTOPILOT_ENABLED), the `autopilot/off` sentinel, the settings file. The
25
+ * sentinel is stat'ed here: any error but ENOENT (EACCES, ENOTDIR) reads as present, because the
26
+ * resolver reads every stat error as absent. Absent, invalid or unreadable settings read as off.
27
+ */
28
+ function readKillSwitch(env = process.env, fsImpl = fs) {
29
+ const off = (layer ) => ({ enabled: false, switch: layer });
30
+ try {
31
+ const { loadSettings } = require('./resolve.js');
32
+ const resolution = loadSettings({ global: true, env: { ...env } }).get('autopilot.enabled', 'global');
33
+ if (resolution.candidates.some((c ) => c.layer === 'env' && c.value === 'off')) return off('env');
34
+ const file = sentinelFile(env);
35
+ if (file) {
36
+ try { fsImpl.statSync(file); return off('sentinel'); }
37
+ catch (error ) { if (error?.code !== 'ENOENT') return off('sentinel'); }
38
+ }
39
+ if (resolution.candidates.some((c ) => c.legacy && c.value === 'off')) return off('sentinel');
40
+ return resolution.value === 'on' ? { enabled: true, switch: null } : off('setting');
41
+ } catch {
42
+ return off('setting');
43
+ }
44
+ }
45
+
46
+ /** Seed writes require an independently verified durable off, so uncertainty refuses the write. */
47
+ function readSeedSwitch(env = process.env, fsImpl = fs) {
48
+ const refuse = () => ({ enabled: true, switch: null });
49
+ const off = (layer ) => ({ enabled: false, switch: layer });
50
+ try {
51
+ const { configHome } = require('../config-home.js');
52
+ const home = configHome(env).dir;
53
+ if (!home) return refuse();
54
+
55
+ const settingsEnv = { ...env };
56
+ delete settingsEnv.DOFLOW_AUTOPILOT_ENABLED;
57
+ const { loadSettings } = require('./resolve.js');
58
+ const settings = loadSettings({ global: true, env: settingsEnv, fsImpl });
59
+ const resolution = settings.get('autopilot.enabled', 'global');
60
+ if (resolution.state === 'invalid' || resolution.candidates.some((candidate ) => candidate.status === 'invalid')) {
61
+ return refuse();
62
+ }
63
+ if (settings.errors.some((entry ) => entry.key === 'autopilot.enabled' || entry.key === null)) {
64
+ return refuse();
65
+ }
66
+
67
+ const file = path.join(home, 'autopilot', 'off');
68
+ try {
69
+ fsImpl.statSync(file);
70
+ return off('sentinel');
71
+ } catch (error ) {
72
+ if (error?.code !== 'ENOENT') return off('sentinel');
73
+ }
74
+
75
+ const legacyOff = resolution.candidates.some((candidate ) => candidate.legacy && candidate.value === 'off');
76
+ if (resolution.value === 'off' || legacyOff) return off('setting');
77
+ return refuse();
78
+ } catch {
79
+ return refuse();
80
+ }
81
+ }
82
+
83
+ /**
84
+ * maint-thin: whether a review is unattended, so the class seat floor binds. Fail closed: only a verifiably off
85
+ * switch (the seed reader: a resolved config home whose setting or sentinel says off) reads as attended. An uncertain
86
+ * switch, unreadable settings or no config home count as unattended. The one other attended case is an explicit,
87
+ * process-local `DOFLOW_AUTOPILOT_ENABLED=off`: a caller can always lower its own run that way, and that costs nothing,
88
+ * because roadmap gate-b judges the recorded seats against the class floor itself and denies too few.
89
+ */
90
+ function reviewUnattended(env = process.env, fsImpl = fs) {
91
+ if (env?.DOFLOW_AUTOPILOT_ENABLED === 'off') return false;
92
+ return readSeedSwitch(env, fsImpl).enabled !== false;
93
+ }
94
+
95
+ /** Tripwire (narrowing only): writes the kill-switch sentinel; only a person removes it. */
96
+ function tripSentinel(env , line , fsImpl = fs) {
97
+ const file = sentinelFile(env);
98
+ // No config home already reads as off (needsConfigHome), so there is nothing to narrow.
99
+ if (!file) return false;
100
+ fsImpl.mkdirSync(path.dirname(file), { recursive: true });
101
+ try { fsImpl.writeFileSync(file, `${line}\n`, { flag: 'wx', mode: 0o600 }); }
102
+ catch (error ) { if (error.code !== 'EEXIST') throw error; }
103
+ return true;
104
+ }
105
+
106
+ module.exports = { readKillSwitch, readSeedSwitch, reviewUnattended, tripSentinel };
@@ -0,0 +1,280 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ const path = require('node:path');
6
+ const fs = require('node:fs');
7
+ const { configHome } = require('../config-home.js');
8
+ const { finishRuntime, usageError } = require('../cli-result.js');
9
+ const { REPO_ROOT } = require('../../helper/repo-root.js');
10
+ const { loadSettingsRegistry, REGISTRY_FILE } = require('./registry.js');
11
+ const { loadSettings } = require('./resolve.js');
12
+ const { maskUrl } = require('./shared.js');
13
+
14
+ function safeValue(value , meta ) {
15
+ return meta?.type === 'url' && typeof value === 'string' ? maskUrl(value) : value;
16
+ }
17
+
18
+ function safeResolution(resolution ) {
19
+ const meta = resolution?.meta || {};
20
+ return {
21
+ ...resolution,
22
+ value: safeValue(resolution.value, meta),
23
+ origin: resolution.origin ? { ...resolution.origin } : resolution.origin,
24
+ candidates: (resolution.candidates || []).map((candidate ) => ({
25
+ ...candidate,
26
+ value: safeValue(candidate.value, meta),
27
+ display: meta.type === 'url' && typeof candidate.display === 'string'
28
+ ? maskUrl(candidate.display) : candidate.display,
29
+ })),
30
+ meta: { ...meta, default: safeValue(meta.default, meta) },
31
+ };
32
+ }
33
+
34
+ function safeReport(report ) {
35
+ const keys = {};
36
+ for (const [key, value] of Object.entries(report.keys || {})) {
37
+ if (value && typeof value === 'object' && !('key' in value)
38
+ && 'project' in value && 'global' in value) {
39
+ keys[key] = { project: safeResolution((value ).project), global: safeResolution((value ).global) };
40
+ } else {
41
+ keys[key] = safeResolution(value);
42
+ }
43
+ }
44
+ return { ...report, keys };
45
+ }
46
+
47
+ /** The settings-only part of doctor output; values are omitted, while present files and faults remain. */
48
+ function settingsReport(view ) {
49
+ const report = view.report();
50
+ return {
51
+ schema: 1,
52
+ scope: report.scope,
53
+ configHome: report.configHome,
54
+ project: report.project,
55
+ files: report.files,
56
+ errors: report.errors,
57
+ notes: report.notes,
58
+ };
59
+ }
60
+
61
+ function buildSettingsReport({ repoRoot = REPO_ROOT, projectRoot = process.cwd(), global = false,
62
+ env = process.env, fsImpl = fs } = {}) {
63
+ const loaded = loadSettingsRegistry({ repoRoot, fsImpl });
64
+ if (!loaded.ok) {
65
+ const home = configHome(env);
66
+ return {
67
+ schema: 1,
68
+ scope: global ? 'global' : 'project',
69
+ configHome: { dir: home.dir, ignored: [...home.ignored] },
70
+ project: { root: global ? null : path.resolve(projectRoot), layersFrom: null, checkout: null },
71
+ files: [],
72
+ errors: [{ key: null, layer: 'default', source: path.join(repoRoot, REGISTRY_FILE), legacy: false,
73
+ code: 'settings-registry-invalid', reason: loaded.detail, offset: null }],
74
+ notes: [],
75
+ };
76
+ }
77
+ try {
78
+ const view = loadSettings({ projectRoot, global, env, fsImpl, registry: loaded.registry });
79
+ return settingsReport(view);
80
+ } catch (error ) {
81
+ const home = configHome(env);
82
+ return {
83
+ schema: 1,
84
+ scope: global ? 'global' : 'project',
85
+ configHome: { dir: home.dir, ignored: [...home.ignored] },
86
+ project: { root: global ? null : path.resolve(projectRoot), layersFrom: null, checkout: null },
87
+ files: [],
88
+ errors: [{ key: null, layer: 'default', source: path.join(repoRoot, REGISTRY_FILE), legacy: false,
89
+ code: 'settings-unavailable', reason: error?.message || String(error), offset: null }],
90
+ notes: [],
91
+ };
92
+ }
93
+ }
94
+
95
+ function settingValue(value , meta ) {
96
+ const safe = safeValue(value, meta);
97
+ if (safe === null || safe === undefined) return '(none)';
98
+ if (typeof safe === 'string') return JSON.stringify(safe);
99
+ return JSON.stringify(safe);
100
+ }
101
+
102
+ function originLabel(resolution ) {
103
+ const { origin } = resolution;
104
+ const source = origin.source || (origin.via === 'no-config-home' ? 'no config home' : REGISTRY_FILE);
105
+ return `${origin.layer}: ${source}`;
106
+ }
107
+
108
+ function printResolution(key , resolution , showOrigin , suffix = '') {
109
+ if (resolution.meta?.needsConfigHome && resolution.origin?.via === 'no-config-home') {
110
+ console.log(`${key}${suffix} = ${settingValue(resolution.value, resolution.meta)} (no config home: capture skipped)`);
111
+ } else {
112
+ const origin = showOrigin ? ` (${originLabel(resolution)})` : '';
113
+ console.log(`${key}${suffix} = ${settingValue(resolution.value, resolution.meta)}${origin}`);
114
+ }
115
+ if (!showOrigin) return;
116
+ for (const candidate of resolution.candidates || []) {
117
+ const display = typeof candidate.display === 'string' && candidate.display !== '' ? candidate.display : '(none)';
118
+ const reason = candidate.reason ? `: ${candidate.reason}` : '';
119
+ console.log(` ${candidate.layer} ${candidate.source}: ${display} — ${candidate.status}${reason}`);
120
+ }
121
+ }
122
+
123
+ function printSettingsList(report , showOrigin ) {
124
+ const home = report.configHome;
125
+ const ignored = home.ignored || [];
126
+ const homeLabel = home.dir
127
+ ? `${home.dir}${ignored.length ? ` | ${ignored.join(', ')} ignored` : ''}`
128
+ : `none${ignored.length ? ` (${ignored.join(', ')} ignored)` : ''}`;
129
+ console.log(`config home: ${homeLabel}`);
130
+ for (const name of ignored) console.log(`note: ignored relative ${name}`);
131
+ const project = report.project;
132
+ console.log(`project: ${project.root || '(none)'}${project.checkout ? ` (${project.checkout})` : ''}`);
133
+
134
+ for (const [key, value] of Object.entries(report.keys || {})) {
135
+ const dualView = value && typeof value === 'object' && !('key' in (value ))
136
+ && 'project' in (value ) && 'global' in (value )
137
+ && Array.isArray((value ).project?.meta?.views) && (value ).project.meta.views.includes('global');
138
+ if (dualView) {
139
+ printResolution(key, (value ).project, showOrigin, ' [project roots]');
140
+ printResolution(key, (value ).global, showOrigin, ' [user roots]');
141
+ } else {
142
+ const views = (value )?.meta?.views;
143
+ const suffix = Array.isArray(views) && views.includes('global')
144
+ ? ((value ).view === 'global' ? ' [user roots]' : ' [project roots]')
145
+ : '';
146
+ printResolution(key, value, showOrigin, suffix);
147
+ }
148
+ }
149
+ for (const error of report.errors || []) {
150
+ console.log(`error: ${error.key || 'settings'} (${error.layer}: ${error.source}): ${error.reason}`);
151
+ }
152
+ for (const note of report.notes || []) console.log(`note: ${note.text}`);
153
+ }
154
+
155
+ function securityFault(report ) {
156
+ const check = (resolution ) => {
157
+ if (resolution && resolution.meta?.securityRelevant
158
+ && ['fail-safe', 'cross-key'].includes(resolution.origin?.via)) return true;
159
+ if (resolution && typeof resolution === 'object' && 'project' in resolution && 'global' in resolution) {
160
+ return check(resolution.project) || check(resolution.global);
161
+ }
162
+ return false;
163
+ };
164
+ return Object.values(report.keys || {}).some(check);
165
+ }
166
+
167
+ const CONFIG_ACTIONS = ['list', 'schema', 'validate', 'set', 'unset'];
168
+ const CONFIG_LAYERS = ['user', 'project', 'local'];
169
+ /** Positional words after the action: the key and, for validate and set, a JSON value. */
170
+ const CONFIG_OPERANDS = { schema: 0, validate: 2, set: 2, unset: 1 };
171
+
172
+
173
+
174
+ /** Splits `config [list] [path] | schema | validate <key> <value> | set <key> <value> | unset <key>` and its flags. */
175
+ function configArguments(positional , { layer, global, showOrigin, preparedRun } ) {
176
+ const args = Array.isArray(positional) ? [...positional] : [];
177
+ const fail = (error ) => ({ action: 'list', project: null, key: null, valueText: null, error });
178
+ let action = 'list';
179
+ if (CONFIG_ACTIONS.includes(args[0])) action = args.shift();
180
+ else if (args.length === 1 && !args[0].includes(path.sep) && !fs.existsSync(args[0])) {
181
+ return fail(`unknown action '${args[0]}'; expected ${CONFIG_ACTIONS.join(', ')}`);
182
+ }
183
+ if (layer !== undefined && !CONFIG_LAYERS.includes(layer)) return fail(`--layer must be one of ${CONFIG_LAYERS.join(', ')}`);
184
+ if (action === 'list') {
185
+ if (args.length > 1) return fail('expected config [list] [project path]');
186
+ if (layer !== undefined) return fail('--layer is only for validate, set and unset');
187
+ if (preparedRun !== undefined) return fail('--prepared-run is only for config validate');
188
+ return { action, project: args[0] || null, key: null, valueText: null, error: null };
189
+ }
190
+ if (showOrigin) return fail('--show-origin is only for config list');
191
+ if (global) return fail(`-g is not for config ${action}`);
192
+ if (action === 'schema' && layer !== undefined) return fail('--layer is only for validate, set and unset');
193
+ if (preparedRun !== undefined) {
194
+ if (action !== 'validate') return fail('--prepared-run is only for config validate');
195
+ if (args.length) return fail('config validate --prepared-run <file> takes no key or value');
196
+ if (layer !== undefined) return fail('--layer is not for config validate --prepared-run (its settings are judged at the project layer)');
197
+ return { action, project: null, key: null, valueText: null, error: null };
198
+ }
199
+ const want = CONFIG_OPERANDS[action];
200
+ if (args.length !== want) {
201
+ const usage = { schema: 'config schema', validate: 'config validate <key> <json-value> [--layer user|project|local] | config validate --prepared-run <file>',
202
+ set: 'config set <key> <json-value> --layer user|project|local', unset: 'config unset <key> --layer user|project|local' }[action];
203
+ return fail(`expected ${usage}`);
204
+ }
205
+ if ((action === 'set' || action === 'unset') && layer === undefined) return fail(`config ${action} needs --layer user|project|local`);
206
+ return { action, project: null, key: args[0] ?? null, valueText: args[1] ?? null, error: null };
207
+ }
208
+
209
+ function handleConfigCommand({ json = false, global = false, showOrigin = false, positional = [], layer, preparedRun,
210
+ projectRoot, repoRoot = REPO_ROOT, env = process.env, fsImpl = fs } = {}) {
211
+ const args = configArguments(positional, { layer, global, showOrigin, preparedRun });
212
+ if (args.error) return usageError('config', args.error, json);
213
+ const loaded = loadSettingsRegistry({ repoRoot, fsImpl });
214
+ if (!loaded.ok) {
215
+ const detail = loaded.detail || 'settings registry is invalid';
216
+ if (json) console.log(JSON.stringify({ ok: false, status: 'ERROR', exitCode: 2,
217
+ error: 'settings-registry-invalid', detail }, null, 2));
218
+ else console.error(`doflow config: settings registry unavailable: ${detail}`);
219
+ return finishRuntime(2);
220
+ }
221
+
222
+ if (preparedRun !== undefined) {
223
+ return require('./prepared-run.js').handlePreparedRun({
224
+ file: preparedRun, json, registry: loaded.registry, repoRoot, projectRoot: projectRoot || process.cwd(), env, fsImpl,
225
+ });
226
+ }
227
+
228
+ if (args.action !== 'list') {
229
+ // loaded on demand: the writer and the protocol registry stay off the read-only list path
230
+ return require('./config-write.js').handleConfigAction({
231
+ action: args.action, key: args.key, valueText: args.valueText, layer, json, registry: loaded.registry,
232
+ repoRoot, projectRoot: projectRoot || process.cwd(), env, fsImpl,
233
+ });
234
+ }
235
+
236
+ try {
237
+ const root = global ? null : path.resolve(args.project || projectRoot || process.cwd());
238
+ const view = loadSettings({ projectRoot: root, global, env, fsImpl, registry: loaded.registry });
239
+ const report = safeReport(view.report());
240
+ if (json) console.log(JSON.stringify(report, null, 2));
241
+ else printSettingsList(report, showOrigin);
242
+ return finishRuntime(securityFault(report) ? 1 : 0);
243
+ } catch (error ) {
244
+ const detail = error?.message || String(error);
245
+ if (json) console.log(JSON.stringify({ ok: false, status: 'ERROR', exitCode: 2,
246
+ error: 'settings-registry-invalid', detail }, null, 2));
247
+ else console.error(`doflow config: settings registry unavailable: ${detail}`);
248
+ return finishRuntime(2);
249
+ }
250
+ }
251
+
252
+ function printSettingsReport(report ) {
253
+ console.log('\n[Settings]');
254
+ console.log(` config home: ${report.configHome.dir || 'none'}`);
255
+ for (const name of report.configHome.ignored || []) {
256
+ console.log(` ignored relative ${name} (ignored)`);
257
+ }
258
+ if (report.project.root) {
259
+ console.log(` project: ${report.project.root}${report.project.checkout ? ` (${report.project.checkout})` : ''}`);
260
+ }
261
+ const present = (report.files || []).filter((file ) => file.status !== 'absent');
262
+ if (!present.length) console.log(' files: none');
263
+ for (const file of present) {
264
+ const deprecated = file.kind === 'legacy' ? ' (deprecated)' : '';
265
+ console.log(` ${file.layer} ${file.path}: ${file.status}${deprecated}`);
266
+ }
267
+ for (const note of report.notes || []) console.log(` note: ${note.text}`);
268
+ for (const error of report.errors || []) {
269
+ console.log(` error: ${error.key || 'settings'} (${error.layer}: ${error.source}): ${error.reason}`);
270
+ }
271
+ }
272
+
273
+ module.exports = {
274
+ maskUrl,
275
+ safeReport,
276
+ settingsReport,
277
+ buildSettingsReport,
278
+ printSettingsReport,
279
+ handleConfigCommand,
280
+ };