@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
@@ -1,429 +0,0 @@
1
- 'use strict';
2
-
3
- // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
-
5
- // Gemini CLI adapter. Native settings are JSON and Gemini's extension installation is a host
6
- // workflow, so this adapter deliberately plans only safe instruction projection while exposing
7
- // settings/MCP/extensions as first-class native-surface results for the lifecycle UI.
8
- const fs = require('node:fs');
9
- const path = require('node:path');
10
- const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, readJson, sourceDirFor, resolveTransform } = require('../copy-tree.js');
11
- const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
12
- const { planGeminiHooks, deployGeminiHooks, planRemoveGeminiHooks, deployRemoveGeminiHooks, scriptFingerprint } = require('./hooks.js');
13
-
14
- const MARKER_START = '<!-- doflow:start -->';
15
- const MARKER_END = '<!-- doflow:end -->';
16
- /**
17
- * Residue (not a path fact): the one non-path native fact this harness still reads from its own
18
- * fixture file — how extensions are installed ("native-host-workflow"), reported verbatim in plan
19
- * surfaces. The file-name facts that used to live beside it (instructionFile, settingsFile) are now
20
- * declared in core/registry/harnesses.json "paths" and resolve below like every other harness.
21
- */
22
- const DEFAULTS_FILE = path.resolve(__dirname, '../../../core/harnesses/gemini/settings/adapter-defaults.json');
23
- const HARNESS = 'gemini';
24
-
25
- function defaults({ fsImpl = fs } = {}) { return JSON.parse(fsImpl.readFileSync(DEFAULTS_FILE, 'utf8')); }
26
-
27
- /** Pure string helper (no path facts): replace only the span between DoFlow's markers in an
28
- * existing GEMINI.md body. An existing file with no DoFlow section is refused rather than appended
29
- * to — GEMINI.md is shared with the user and other tools. */
30
- function managedInstruction(existing , rendered ) {
31
- if (existing === null) return { ok: true, operation: 'create', content: rendered };
32
- const start = existing.indexOf(MARKER_START);
33
- const end = existing.indexOf(MARKER_END);
34
- if (start === -1 && end === -1) return { ok: false, conflict: 'GEMINI.md exists without a DoFlow managed section' };
35
- if (start === -1 || end === -1 || end < start) return { ok: false, conflict: 'GEMINI.md has malformed DoFlow managed-section markers' };
36
- const content = `${existing.slice(0, start)}${rendered}${existing.slice(end + MARKER_END.length).replace(/^\n?/, '')}`;
37
- return { ok: true, operation: content === existing ? 'none' : 'merge', content };
38
- }
39
-
40
- /** Server names in one settings read, or null when the file is absent or unreadable. */
41
- function settingsServerNames(read ) {
42
- if (!read.exists || read.error || !read.value || typeof read.value !== 'object') return null;
43
- const servers = read.value.mcpServers;
44
- return Object.keys(servers && typeof servers === 'object' ? servers : {});
45
- }
46
-
47
- /**
48
- * The server names DoFlow can see for Gemini at a scope (`mcpConfigured`): the scope's settings, and
49
- * at project scope the user's `~/.gemini/settings.json` too, since Gemini merges both. `null` while
50
- * neither can be read. Gemini also merges system settings and extension servers, which DoFlow does
51
- * not read, so the view is never complete (`mcpComplete: false`) and absence is never proof.
52
- */
53
- function configuredGeminiMcp({ scope, settings, homeDir, fsImpl } ) {
54
- const own = settingsServerNames(settings);
55
- const user = scope === 'project' && homeDir ? settingsServerNames(readJson(path.join(homeDir, '.gemini', 'settings.json'), { fsImpl })) : null;
56
- const mcpConfigured = own || user ? [...new Set([...(own || []), ...(user || [])])].sort() : null;
57
- return { mcpConfigured, mcpComplete: false };
58
- }
59
-
60
- /**
61
- * Native path facts live in core/registry/harnesses.json under this harness's "paths" section and
62
- * resolve through the shared harness-paths resolver: project-scope config follows the Antigravity
63
- * customization convention (.agents/) while user-scope config lives under ~/.gemini/config/.
64
- * createGeminiAdapter({ declaredPaths }) is the injection point buildAdapterRegistry() uses;
65
- * module-level exports delegate to a default-configured instance so direct callers keep their
66
- * historical shape.
67
- */
68
- function createGeminiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } = {}) {
69
- function nativePaths({ scope, scopeRoot, homeDir, fsImpl = fs } ) {
70
- const resolved = resolveHarnessPaths(declaredPaths, { scope, scopeRoot, homeDir });
71
- // Non-path native fact carried alongside the resolved surfaces (see DEFAULTS_FILE residue note).
72
- return { ...resolved, extensionMode: defaults({ fsImpl }).extensionInstall };
73
- }
74
-
75
- function discover({ scope, scopeRoot, context = {}, fsImpl = fs } ) {
76
- const paths = nativePaths({ scope, scopeRoot, homeDir: context.homeDir, fsImpl });
77
- const instruction = fsImpl.existsSync(paths.instruction) ? fsImpl.readFileSync(paths.instruction, 'utf8') : null;
78
- // Gemini takes no servers from DoFlow's MCP catalog.
79
- const settings = readJson(paths.settings, { fsImpl });
80
- const { mcpConfigured, mcpComplete } = configuredGeminiMcp({ scope, settings, homeDir: context.homeDir, fsImpl });
81
- return { paths, instruction, settings, mcpOwned: [], mcpConfigured, mcpComplete };
82
- }
83
-
84
- function render({ content = '' } = {}) {
85
- const body = String(content).trimEnd();
86
- return `${MARKER_START}\n${body}\n${MARKER_END}\n`;
87
- }
88
-
89
- /**
90
- * Rewrite the pointer's guidance import so it resolves from wherever the instruction file
91
- * actually lands. The shared pointer source carries one hardcoded `../`, which is correct only
92
- * when the instruction file sits one level below the install root — true for Gemini's global
93
- * scope (`.gemini/GEMINI.md`) and false for its project scope, where Gemini reads `GEMINI.md`
94
- * from the workspace root itself. A static prefix cannot satisfy both, because the depth varies
95
- * by scope rather than by harness, so it is computed here instead. Left unrewritten, the import
96
- * pointed above the install root and the whole guidance chain silently failed to load.
97
- *
98
- * Logic residue: the relative prefix depends on the DISTANCE between two declared surfaces
99
- * (instruction vs. the guidance tree), not on any single declared path — expressible only by
100
- * joining and diffing, which is computation, not declaration.
101
- */
102
- function geminiDestDir(paths , asset ) {
103
- if (asset.nativeDir && asset.nativeDir.startsWith('../')) {
104
- const dest = path.join(paths.root, asset.nativeDir.slice(3));
105
- const rel = path.relative(paths.root, dest);
106
- if (rel === '..' || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) {
107
- throw new Error(`gemini nativeDir escapes the scope root: ${asset.nativeDir}`);
108
- }
109
- return dest;
110
- }
111
- return copyTreeDestDir(paths.configDir, asset);
112
- }
113
-
114
- function rewriteGuidanceImport(content , assets , paths ) {
115
- const guidance = assets.find((item ) => item.id === 'guidance.context-layer');
116
- if (!guidance?.nativeDir || !paths?.instruction || !paths?.configDir) return content;
117
- const guidanceDir = geminiDestDir(paths, guidance);
118
- const rel = path.relative(path.dirname(paths.instruction), guidanceDir).split(path.sep).join('/');
119
- if (!rel) return content;
120
- // `./` is added explicitly for a same-directory descent: the bare form starts with the dot of
121
- // `.doflow`, which reads as an extension rather than a path segment.
122
- const prefix = rel.startsWith('../') ? rel : `./${rel}`;
123
- return content.replace(/@\S*?(DOFLOW_CORE\.md)/g, `@${prefix}/$1`);
124
- }
125
-
126
- function instructionContent({ assets, context, paths, fsImpl = fs } ) {
127
- if (context.instructionContent !== undefined) return String(context.instructionContent);
128
- const asset = assets.find((item ) => item.id === 'guidance.core');
129
- if (!asset || !context.repoRoot) return null;
130
- const source = path.resolve(context.repoRoot, asset.source);
131
- if (!fsImpl.existsSync(source)) return null;
132
- return rewriteGuidanceImport(fsImpl.readFileSync(source, 'utf8'), assets, paths);
133
- }
134
-
135
- function policyStatuses(policies = []) {
136
- return policies.map((policy ) => ({ id: policy.id, status: 'unavailable', fallback: 'guidance', reason: 'Gemini policy automation is not rendered by this adapter' }));
137
- }
138
-
139
- // ---- copy-tree assets (rules, skills, agents, modes, references) ----
140
-
141
- function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs } ) {
142
- const paths = nativePaths({ scope, scopeRoot, homeDir: context.homeDir, fsImpl });
143
- const changes = [];
144
- const conflicts = [];
145
- const treeResults = [];
146
- const notices = [];
147
- for (const asset of copyTreeAssets(assets)) {
148
- const destDir = geminiDestDir(paths, asset);
149
- const sourceDir = sourceDirFor(asset, context, fsImpl, 'Gemini');
150
- const previousResources = ledgerFileResources(ledger?.resources, HARNESS, asset.id);
151
- const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, transform: asset.transform, keepModified: !removing,
152
- // Was `force: context?.force`, ungated. Gemini was one of only two adapters forwarding force
153
- // at all, so it looked like the reference implementation — but it handed force to the remove
154
- // path too, where copy-tree deliberately stays strict: force heals drift on apply, and a
155
- // hand-edited file is never deleted, forced or not (codex/index.ts states the rule in full).
156
- // Found by the guard written for the six adapters that forwarded nothing.
157
- force: !removing && context?.force === true });
158
- treeResults.push(result);
159
- conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
160
- // A hand-edited file at a path DoFlow no longer writes (the agents folder layout) is the
161
- // user's: it stays, only its ownership row is released.
162
- notices.push(...result.kept.map((item ) => `kept hand-edited ${path.relative(paths.root, item.target)}; DoFlow no longer manages it`));
163
- for (const change of result.changes) {
164
- changes.push({
165
- assetId: asset.id, target: change.target, source: change.source, operation: change.operation,
166
- ownershipIdentity: `doflow:gemini:copy-tree:${asset.id}:${change.relPath}`,
167
- kind: 'copy-tree-file', identity: change.relPath,
168
- afterFingerprint: change.fingerprint, fingerprint: change.fingerprint, sourceVersion: 'registry-v1',
169
- transformName: asset.transform || null,
170
- ...(change.kept ? { retained: true, retainedFor: [] } : {}),
171
- projection: { renderer: 'copy-tree' },
172
- });
173
- }
174
- }
175
- return { changes, conflicts, notices: [...siblingReplacedNotices(treeResults), ...notices] };
176
- }
177
-
178
- function applyCopyTreeAssets(changes , { fsImpl = fs } = {}) {
179
- let applied = 0;
180
- const grouped = new Map();
181
- for (const change of changes) {
182
- if (change.projection?.renderer !== 'copy-tree' || change.operation === 'remove') continue;
183
- const key = change.transformName || null;
184
- if (!grouped.has(key)) grouped.set(key, []);
185
- grouped.get(key).push({ relPath: change.identity, target: change.target, source: change.source, operation: change.operation, fingerprint: change.fingerprint });
186
- }
187
- for (const [transformName, treeChanges] of grouped) {
188
- applied += applyTree({ changes: treeChanges, fsImpl, transform: resolveTransform(transformName) }).applied;
189
- }
190
- return applied;
191
- }
192
-
193
- function removeCopyTreeAssets(changes , { fsImpl = fs } = {}) {
194
- const treeChanges = changes.filter((change ) => change.projection?.renderer === 'copy-tree' && change.operation === 'remove' && !change.retained)
195
- .map((change ) => ({ relPath: change.identity, target: change.target, operation: 'remove', fingerprint: change.fingerprint, pruneGuard: change.pruneGuard }));
196
- return removeTree({ changes: treeChanges, fsImpl }).removed;
197
- }
198
-
199
- function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs } ) {
200
- const paths = nativePaths({ scope, scopeRoot, homeDir: context.homeDir, fsImpl });
201
- const statuses = [];
202
- const resources = [];
203
- const conflicts = [];
204
- for (const asset of copyTreeAssets(assets)) {
205
- const destDir = geminiDestDir(paths, asset);
206
- const sourceDir = sourceDirFor(asset, context, fsImpl, 'Gemini');
207
- const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.transform, plannedWrites: context?.plannedWrites });
208
- conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
209
- for (const resource of result.resources) {
210
- resources.push({
211
- assetId: asset.id, target: resource.target, ownershipIdentity: `doflow:gemini:copy-tree:${asset.id}:${resource.relPath}`,
212
- kind: 'copy-tree-file', identity: resource.relPath,
213
- fingerprint: resource.fingerprint, sourceVersion: 'registry-v1',
214
- projection: { renderer: 'copy-tree' },
215
- });
216
- }
217
- statuses.push({ assetId: asset.id, capability: asset.capability, status: result.ok ? 'managed' : 'conflict', target: destDir });
218
- }
219
- return { statuses, resources, conflicts };
220
- }
221
-
222
- // ---- shared adapter contract ----
223
-
224
- // Hooks are a pseudo-component, not a registry-declared asset (like Codex's own hooks
225
- // integration) — the lifecycle layer's normalizeChange() requires every change's assetId to
226
- // match a real registered asset, so this piggybacks on guidance.core's id the same way Codex's
227
- // assetIdFor(options, 'hooks') does, rather than inventing an unregistered 'hooks' id.
228
- function hooksAssetId(assets ) {
229
- return assets.find((asset ) => asset.id === 'guidance.core')?.id ?? assets[0]?.id;
230
- }
231
-
232
- /** This scope's ledger rows for the hook scripts deployed beside settings.json. */
233
- function scriptRows(ledger , scope ) {
234
- return (ledger?.resources || []).filter((row ) => row?.harness === 'gemini' && row.kind === 'hooks-script' && (!row.scope || row.scope === scope));
235
- }
236
-
237
- /** Plan the hooks pseudo-component's single change (or none), mirroring the {changes, conflicts}
238
- * shape planCopyTreeAssets already returns so plan() can fold it in the same way. The hook
239
- * scripts directory beside settings.json is declared (paths.hooksDirectory); the merge/remove
240
- * planning itself lives in ./hooks.ts. A removal also releases the rows of the hook scripts. */
241
- function planHooksChange({ removing, found, context, assets, ledger, scope, fsImpl } ) {
242
- const component = planHooksComponentChange({ removing, found, context, assets, ledger, scope, fsImpl });
243
- if (!removing) return component;
244
- const releases = scriptRows(ledger, scope).map((row ) => ({ assetId: row.assetId, target: row.target, operation: 'remove',
245
- ownershipIdentity: row.ownershipIdentity, kind: row.kind, identity: row.identity, projection: row.projection,
246
- retained: true, retainedFor: [] }));
247
- return { ...component, changes: [...component.changes, ...releases] };
248
- }
249
-
250
- function planHooksComponentChange({ removing, found, context, assets, ledger, scope, fsImpl } ) {
251
- if (found.settings.error) return { changes: [], conflicts: [] };
252
- const hooksPlan = removing
253
- ? planRemoveGeminiHooks({ settingsFile: found.paths.settings, fsImpl })
254
- : (context.geminiHooksSourceFile
255
- ? planGeminiHooks({ sourceFile: context.geminiHooksSourceFile, sourceHooksDir: context.geminiHooksSourceDir,
256
- settingsFile: found.paths.settings, destinationHooksDir: found.paths.hooksDirectory, trusted: context.hooksTrusted,
257
- scriptRows: scriptRows(ledger, scope), force: context.force === true, fsImpl })
258
- : null);
259
- if (hooksPlan && !hooksPlan.ok) return { changes: [], conflicts: hooksPlan.errors || [] };
260
- if (hooksPlan?.status !== 'change') return { changes: [], conflicts: [] };
261
- return { changes: [{ assetId: hooksAssetId(assets), target: hooksPlan.settingsFile, operation: removing ? 'remove' : (hooksPlan.changes[0]?.type ?? 'update'),
262
- ownershipIdentity: 'gemini:hooks', nativeComponent: 'hooks', nativePlan: hooksPlan, projection: { renderer: 'gemini-hooks' },
263
- companionTargets: removing ? [] : (hooksPlan.companionTargets ?? []) }], conflicts: [] };
264
- }
265
-
266
- function plan({ scope, scopeRoot, assets = [], mcp = [], discovery, context = {}, ledger, fsImpl = fs } ) {
267
- const found = discovery || discover({ scope, scopeRoot, context, fsImpl });
268
- const changes = []; const conflicts = [];
269
- const removing = context.operation === 'remove';
270
- if (removing) {
271
- // Removal only ever targets a section this adapter itself owns — a foreign or absent
272
- // GEMINI.md yields no change, never a forced deletion of user content.
273
- const owns = found.instruction !== null && found.instruction.includes(MARKER_START) && found.instruction.includes(MARKER_END);
274
- if (assets.some((asset ) => asset.id === 'guidance.core') && owns) {
275
- changes.push({ assetId: 'guidance.core', target: found.paths.instruction, operation: 'remove', ownershipIdentity: 'gemini:instructions', projection: { renderer: 'gemini-instructions' } });
276
- }
277
- } else {
278
- const content = instructionContent({ assets, context, paths: found.paths, fsImpl });
279
- if (content !== null) {
280
- const next = managedInstruction(found.instruction, render({ content }));
281
- if (!next.ok) conflicts.push(next.conflict);
282
- else if (next.operation !== 'none') changes.push({ assetId: 'guidance.core', target: found.paths.instruction, operation: next.operation, ownershipIdentity: 'gemini:instructions', afterFingerprint: fingerprint(next.content), content: next.content, projection: { renderer: 'gemini-instructions' } });
283
- }
284
- }
285
- const copyTree = planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl });
286
- changes.push(...copyTree.changes); conflicts.push(...copyTree.conflicts);
287
- if (found.settings.error) conflicts.push(found.settings.error);
288
- const hooks = planHooksChange({ removing, found, context, assets, ledger, scope, fsImpl });
289
- changes.push(...hooks.changes); conflicts.push(...hooks.conflicts);
290
- return {
291
- changes, conflicts, prerequisites: [], notices: copyTree.notices,
292
- surfaces: {
293
- instructions: { status: 'supported', target: found.paths.instruction },
294
- settings: { status: found.settings.error ? 'invalid' : 'supported', target: found.paths.settings },
295
- mcp: { status: found.settings.error ? 'blocked' : 'supported', target: found.paths.settings, selected: mcp.map((item ) => item.id) },
296
- extensions: { status: 'different', mode: found.paths.extensionMode, action: 'use Gemini CLI extension management' },
297
- policies: policyStatuses(context.policies),
298
- },
299
- };
300
- }
301
-
302
- function atomicWrite(file , content , { fsImpl = fs } = {}) {
303
- fsImpl.mkdirSync(path.dirname(file), { recursive: true });
304
- const temp = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${Date.now()}.tmp`);
305
- try {
306
- fsImpl.writeFileSync(temp, content, { encoding: 'utf8', flag: 'wx' });
307
- fsImpl.renameSync(temp, file);
308
- } finally {
309
- if (fsImpl.existsSync(temp)) fsImpl.rmSync(temp, { force: true });
310
- }
311
- }
312
- function apply({ changes = [], fsImpl = fs } ) {
313
- for (const change of changes) if (change.content !== undefined) atomicWrite(change.target, change.content, { fsImpl });
314
- const hooksChange = changes.find((change ) => change.nativeComponent === 'hooks');
315
- if (hooksChange?.nativePlan) deployGeminiHooks(hooksChange.nativePlan, { fsImpl });
316
- applyCopyTreeAssets(changes, { fsImpl });
317
- // An update that relocates an asset (the agents folder layout -> flat files) carries the old
318
- // rows as removals inside the apply batch; the lifecycle calls remove() only for `doflow remove`.
319
- removeCopyTreeAssets(changes, { fsImpl });
320
- }
321
-
322
- /** Strip only the DoFlow-managed span, exactly like the Claude adapter's removeManagedSection —
323
- * a file with user content before/after the managed section must keep that content. Only delete
324
- * the file outright once nothing but the managed section (and its separator) remains. */
325
- function removeManagedSection(file , { fsImpl = fs } = {}) {
326
- if (!fsImpl.existsSync(file)) return false;
327
- const existing = fsImpl.readFileSync(file, 'utf8');
328
- const start = existing.indexOf(MARKER_START);
329
- if (start === -1) return false;
330
- const end = existing.indexOf(MARKER_END, start + MARKER_START.length);
331
- if (end === -1 || existing.indexOf(MARKER_START, end + MARKER_END.length) !== -1) {
332
- throw new Error(`Refusing to remove malformed DoFlow markers in ${file}`);
333
- }
334
- let after = end + MARKER_END.length;
335
- if (existing[after] === '\n') after += 1;
336
- let next = existing.slice(0, start) + existing.slice(after);
337
- if (next === '\n') next = '';
338
- if (next === '') fsImpl.rmSync(file, { force: true });
339
- else fsImpl.writeFileSync(file, next);
340
- return true;
341
- }
342
- function remove({ changes = [], fsImpl = fs } ) {
343
- for (const change of changes) if (change.target && change.projection?.renderer === 'gemini-instructions') removeManagedSection(change.target, { fsImpl });
344
- const hooksChange = changes.find((change ) => change.nativeComponent === 'hooks');
345
- if (hooksChange?.nativePlan) deployRemoveGeminiHooks(hooksChange.nativePlan, { fsImpl });
346
- removeCopyTreeAssets(changes, { fsImpl });
347
- }
348
-
349
- /** Whether the hooks pseudo-component is on disk as the source declares it, with the resource that
350
- * records it when so. Null status when the run carries no hooks source or settings.json is unreadable. */
351
- function verifyHooksComponent({ found, assets, context, fsImpl } ) {
352
- if (!context.geminiHooksSourceFile || found.settings.error) return { hooksStatus: null, resources: [], conflicts: [] };
353
- const hooksPlan = planGeminiHooks({ sourceFile: context.geminiHooksSourceFile, sourceHooksDir: context.geminiHooksSourceDir,
354
- settingsFile: found.paths.settings, destinationHooksDir: found.paths.hooksDirectory, trusted: context.hooksTrusted, fsImpl });
355
- const hooksStatus = hooksPlan.ok && hooksPlan.status === 'unchanged' ? 'managed' : (hooksPlan.ok ? 'missing' : 'conflict');
356
- const resources = hooksStatus === 'managed' ? [{ assetId: hooksAssetId(assets), target: hooksPlan.settingsFile, ownershipIdentity: 'gemini:hooks',
357
- fingerprint: fingerprint(hooksPlan.merged.hooks), sourceVersion: context.sourceVersion ?? 'unknown', projection: { renderer: 'gemini-hooks' } },
358
- ...hooksPlan.scriptNames.map((identity ) => {
359
- const target = path.join(hooksPlan.destinationHooksDir, identity);
360
- return { assetId: hooksAssetId(assets), target, ownershipIdentity: `gemini:hooks-script:${identity}`, kind: 'hooks-script', identity,
361
- fingerprint: scriptFingerprint(fs.readFileSync(target)), sourceVersion: context.sourceVersion ?? 'unknown', projection: { renderer: 'gemini-hooks' } };
362
- })] : [];
363
- return { hooksStatus, resources, conflicts: hooksPlan.ok ? [] : (hooksPlan.errors || []) };
364
- }
365
-
366
- /** The hook front doors (IC-037): the hooks pseudo-component alone. */
367
- function planHookDoors({ scope, scopeRoot, assets = [], discovery, context = {}, ledger, fsImpl = fs } ) {
368
- const found = discovery || discover({ scope, scopeRoot, context, fsImpl });
369
- const hooks = planHooksChange({ removing: false, found, context, assets, ledger, scope, fsImpl });
370
- return { changes: hooks.changes, conflicts: [...(found.settings.error ? [found.settings.error] : []), ...hooks.conflicts], prerequisites: [], notices: [] };
371
- }
372
-
373
- function verifyHookDoors({ scope, scopeRoot, assets = [], context = {}, fsImpl = fs } ) {
374
- const found = discover({ scope, scopeRoot, context, fsImpl });
375
- const hooks = verifyHooksComponent({ found, assets, context, fsImpl });
376
- const conflicts = [...(found.settings.error ? [found.settings.error] : []), ...hooks.conflicts];
377
- return { ok: conflicts.length === 0 && hooks.hooksStatus !== 'conflict', resources: hooks.resources, statuses: { hooks: hooks.hooksStatus }, conflicts };
378
- }
379
-
380
- function verify({ scope, scopeRoot, assets = [], context = {}, fsImpl = fs } ) {
381
- const found = discover({ scope, scopeRoot, context, fsImpl });
382
- const resources = [];
383
- if (assets.some((asset ) => asset.id === 'guidance.core') && found.instruction?.includes(MARKER_START) && found.instruction.includes(MARKER_END)) {
384
- resources.push({ assetId: 'guidance.core', target: found.paths.instruction, ownershipIdentity: 'gemini:instructions', fingerprint: fingerprint(found.instruction), sourceVersion: context.sourceVersion ?? 'unknown', projection: { renderer: 'gemini-instructions' } });
385
- }
386
- const copyTree = verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl });
387
- resources.push(...copyTree.resources);
388
- const hooks = verifyHooksComponent({ found, assets, context, fsImpl });
389
- resources.push(...hooks.resources);
390
- const hooksStatus = hooks.hooksStatus;
391
- const conflicts = [...(found.settings.error ? [found.settings.error] : []), ...copyTree.conflicts, ...hooks.conflicts];
392
- return { ok: !found.settings.error && copyTree.conflicts.length === 0 && hooksStatus !== 'conflict', resources,
393
- statuses: { settings: found.settings.error ? 'invalid' : 'supported', extensions: 'different', policies: policyStatuses(context.policies), copyTree: copyTree.statuses, hooks: hooksStatus },
394
- conflicts };
395
- }
396
-
397
- return { nativePaths, discover, render, plan, apply, remove, verify, planHookDoors, verifyHookDoors };
398
- }
399
-
400
- const singleton = createGeminiAdapter();
401
-
402
- /** Gemini's hooks trust, observed for a lifecycle target. Trust is not a static registry
403
- * prerequisite the way Codex's `trusted-project`/`hook-review` are: Gemini fingerprints hook
404
- * name/command and warns before running one that changed, so trust is computed live against the
405
- * current hooks.json source and the harness's current settings.json. This is the adapter's own
406
- * API for that observation (review A3 — lifecycle used to import planGeminiHooks and re-derive
407
- * the plan itself, which put Gemini's hook semantics on the lifecycle side of the boundary).
408
- * Read-only and side-effect free; returns the trust value or null when unobservable.
409
- * @param {Object} target a lifecycle status target ({adapterInput, discovery})
410
- * @returns {*} the plan's trust, or null
411
- */
412
- function hookTrustFor(target ) {
413
- const context = target.adapterInput?.context || {};
414
- const settingsFile = target.discovery?.paths?.settings;
415
- if (!context.geminiHooksSourceFile || !settingsFile || target.discovery?.settings?.error) return null;
416
- const plan = planGeminiHooks({
417
- sourceFile: context.geminiHooksSourceFile, sourceHooksDir: context.geminiHooksSourceDir,
418
- settingsFile, trusted: context.hooksTrusted,
419
- });
420
- return plan.ok ? (plan.trust ?? null) : null;
421
- }
422
-
423
- module.exports = {
424
- MARKER_START, MARKER_END,
425
- nativePaths: singleton.nativePaths, discover: singleton.discover, render: singleton.render,
426
- managedInstruction, plan: singleton.plan, apply: singleton.apply, remove: singleton.remove,
427
- verify: singleton.verify, planHookDoors: singleton.planHookDoors, verifyHookDoors: singleton.verifyHookDoors, planGeminiHooks, hookTrustFor,
428
- createGeminiAdapter,
429
- };