@khoavu882/doflow 0.0.0-stage → 2.0.0-beta.4

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 (409) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -2
  3. package/bin/doflow.js +44 -0
  4. package/core/.antigravity-plugin/hooks.json +39 -0
  5. package/core/.antigravity-plugin/mcp_config.json +18 -0
  6. package/core/.antigravity-plugin/plugin.json +31 -0
  7. package/core/.claude-plugin/marketplace.json +20 -0
  8. package/core/.claude-plugin/plugin.json +21 -0
  9. package/core/.codex-plugin/marketplace.json +20 -0
  10. package/core/.codex-plugin/plugin.json +35 -0
  11. package/core/.plugin/plugin.json +19 -0
  12. package/core/harnesses/antigravity/hooks/pre-implementation-gate.sh +9 -0
  13. package/core/harnesses/antigravity/hooks/stop-check.sh +10 -0
  14. package/core/harnesses/claude/hooks/lib.sh +304 -0
  15. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +20 -0
  16. package/core/harnesses/claude/hooks/permission-audit.sh +47 -0
  17. package/core/harnesses/claude/hooks/post-compact.sh +74 -0
  18. package/core/harnesses/claude/hooks/post-edit-lint.sh +36 -0
  19. package/core/harnesses/claude/hooks/post-tool-failure.sh +47 -0
  20. package/core/harnesses/claude/hooks/pre-bash-guard.sh +20 -0
  21. package/core/harnesses/claude/hooks/pre-compact.sh +43 -0
  22. package/core/harnesses/claude/hooks/pre-implement-gate.sh +21 -0
  23. package/core/harnesses/claude/hooks/session-end.sh +89 -0
  24. package/core/harnesses/claude/hooks/session-start.sh +5 -0
  25. package/core/harnesses/claude/hooks/skill-config-audit.sh +39 -0
  26. package/core/harnesses/claude/hooks/stop-check.sh +5 -0
  27. package/core/harnesses/claude/hooks/subagent-audit.sh +7 -0
  28. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -0
  29. package/core/harnesses/claude/settings/keybindings.json +20 -0
  30. package/core/harnesses/claude/settings/settings.json +201 -0
  31. package/core/harnesses/claude/statusline/doflow-statusline.sh +49 -0
  32. package/core/harnesses/codex/agents/core-implementer.toml +7 -0
  33. package/core/harnesses/codex/agents/quality-guardian.toml +8 -0
  34. package/core/harnesses/codex/agents/research-writer.toml +8 -0
  35. package/core/harnesses/codex/agents/spec-analyst.toml +8 -0
  36. package/core/harnesses/codex/agents/system-architect.toml +9 -0
  37. package/core/harnesses/codex/config/config.toml +10 -0
  38. package/core/harnesses/codex/hooks/hooks.json +122 -0
  39. package/core/harnesses/codex/hooks/lib.sh +335 -0
  40. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +18 -0
  41. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +52 -0
  42. package/core/harnesses/codex/hooks/post-edit-lint.sh +6 -0
  43. package/core/harnesses/codex/hooks/pre-bash-guard.sh +18 -0
  44. package/core/harnesses/codex/hooks/pre-compact.impl.sh +46 -0
  45. package/core/harnesses/codex/hooks/pre-compact.sh +9 -0
  46. package/core/harnesses/codex/hooks/pre-implement-gate.sh +19 -0
  47. package/core/harnesses/codex/hooks/session-end.impl.sh +92 -0
  48. package/core/harnesses/codex/hooks/session-end.sh +5 -0
  49. package/core/harnesses/codex/hooks/session-start.sh +5 -0
  50. package/core/harnesses/codex/hooks/stop-check.sh +8 -0
  51. package/core/harnesses/codex/hooks/subagent-audit.sh +8 -0
  52. package/core/harnesses/codex/hooks/user-prompt-submit.sh +6 -0
  53. package/core/harnesses/gemini/hooks/hooks.json +77 -0
  54. package/core/harnesses/gemini/hooks/lib.sh +302 -0
  55. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +21 -0
  56. package/core/harnesses/gemini/hooks/post-edit-lint.sh +39 -0
  57. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +21 -0
  58. package/core/harnesses/gemini/hooks/pre-compact.sh +38 -0
  59. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +22 -0
  60. package/core/harnesses/gemini/hooks/session-end.impl.sh +89 -0
  61. package/core/harnesses/gemini/hooks/session-end.sh +5 -0
  62. package/core/harnesses/gemini/hooks/session-start.sh +5 -0
  63. package/core/harnesses/gemini/settings/adapter-defaults.json +4 -0
  64. package/core/harnesses/kiro/agents/doflow.json +58 -0
  65. package/core/harnesses/kiro/hooks/doflow.json +53 -0
  66. package/core/harnesses/kiro/hooks/lib.sh +324 -0
  67. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +9 -0
  68. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +9 -0
  69. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +9 -0
  70. package/core/harnesses/kiro/hooks/session-start.sh +9 -0
  71. package/core/harnesses/kiro/hooks/stop-check.sh +43 -0
  72. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +39 -0
  73. package/core/harnesses/shared/hooks/adapters/antigravity.js +111 -0
  74. package/core/harnesses/shared/hooks/adapters/arg-fields.js +19 -0
  75. package/core/harnesses/shared/hooks/adapters/gemini.js +114 -0
  76. package/core/harnesses/shared/hooks/hooks.json +47 -0
  77. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +91 -0
  78. package/core/harnesses/shared/hooks/policies/capture-failure.sh +97 -0
  79. package/core/harnesses/shared/hooks/policies/deny-json.sh +29 -0
  80. package/core/harnesses/shared/hooks/policies/lib.sh +501 -0
  81. package/core/harnesses/shared/hooks/policies/mcp-policy.conf +21 -0
  82. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +91 -0
  83. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +328 -0
  84. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +496 -0
  85. package/core/harnesses/shared/hooks/policies/prompt-nudge.jq +131 -0
  86. package/core/harnesses/shared/hooks/policies/session-context.sh +141 -0
  87. package/core/harnesses/shared/hooks/policies/stop-check.sh +264 -0
  88. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +46 -0
  89. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +235 -0
  90. package/core/harnesses/shared/hooks/stream-hook-runner.js +590 -0
  91. package/core/harnesses/shared/locator/doflow-run +87 -0
  92. package/core/registry/advice.json +101 -0
  93. package/core/registry/assets.json +792 -0
  94. package/core/registry/capabilities.json +120 -0
  95. package/core/registry/contracts.json +189 -0
  96. package/core/registry/external-tools.json +191 -0
  97. package/core/registry/harnesses.json +2062 -0
  98. package/core/registry/lifecycle.json +231 -0
  99. package/core/registry/mcp.json +18 -0
  100. package/core/registry/models.json +79 -0
  101. package/core/registry/readiness-templates.json +118 -0
  102. package/core/registry/routes.json +47 -0
  103. package/core/registry/storage.json +1061 -0
  104. package/core/registry/verification.json +258 -0
  105. package/core/registry/workflows.json +790 -0
  106. package/core/shared/agent-specs/core-implementer.md +73 -0
  107. package/core/shared/agent-specs/quality-guardian.md +28 -0
  108. package/core/shared/agent-specs/research-writer.md +31 -0
  109. package/core/shared/agent-specs/spec-analyst.md +33 -0
  110. package/core/shared/agent-specs/system-architect.md +29 -0
  111. package/core/shared/guidance/DOFLOW_CORE.md +8 -0
  112. package/core/shared/guidance/FLAGS.md +25 -0
  113. package/core/shared/guidance/PRINCIPLES.md +61 -0
  114. package/core/shared/guidance/VERSION +1 -0
  115. package/core/shared/guidance/mcp/MCP_Context7.md +18 -0
  116. package/core/shared/guidance/mcp/MCP_Sequential.md +17 -0
  117. package/core/shared/guidance/modes/MODE_Brainstorming.md +46 -0
  118. package/core/shared/guidance/modes/MODE_DeepResearch.md +69 -0
  119. package/core/shared/guidance/modes/MODE_Introspection.md +33 -0
  120. package/core/shared/guidance/modes/MODE_Orchestration.md +57 -0
  121. package/core/shared/guidance/modes/MODE_Task_Management.md +74 -0
  122. package/core/shared/guidance/pointers/claude-gemini.md +2 -0
  123. package/core/shared/guidance/pointers/codex.md +6 -0
  124. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +373 -0
  125. package/core/shared/guidance/references/ARTIFACT_VOICE.md +104 -0
  126. package/core/shared/guidance/references/CONSTITUTION_BASE.md +47 -0
  127. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +161 -0
  128. package/core/shared/guidance/references/DOFLOW_CHAIN.md +87 -0
  129. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +93 -0
  130. package/core/shared/guidance/references/MODEL_SELECTION.md +74 -0
  131. package/core/shared/guidance/references/ORCHESTRATION.md +36 -0
  132. package/core/shared/guidance/references/RESEARCH_CONFIG.md +21 -0
  133. package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +81 -0
  134. package/core/shared/guidance/rules/RULE_01_SAFETY.md +25 -0
  135. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +23 -0
  136. package/core/shared/guidance/rules/RULE_03_QUALITY.md +14 -0
  137. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +46 -0
  138. package/core/shared/scripts/doflow/assets/mermaid-theme.json +46 -0
  139. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +167 -0
  140. package/core/shared/scripts/doflow/bash/do-git-state.sh +546 -0
  141. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +208 -0
  142. package/core/shared/scripts/doflow/bash/do-paths.sh +347 -0
  143. package/core/shared/scripts/doflow/bash/do-prereqs.sh +66 -0
  144. package/core/shared/scripts/doflow/bash/do-review-package.sh +127 -0
  145. package/core/shared/scripts/doflow/bash/do-task-brief.sh +698 -0
  146. package/core/shared/scripts/doflow/bash/render-audit.sh +224 -0
  147. package/core/shared/scripts/doflow/bash/render-diagrams.sh +177 -0
  148. package/core/shared/scripts/doflow/bash/render-puml.sh +223 -0
  149. package/core/shared/scripts/doflow/bash/sync-context.sh +66 -0
  150. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +526 -0
  151. package/core/shared/scripts/doflow/bin/doflow-run +592 -0
  152. package/core/shared/skills/do/SKILL.md +128 -0
  153. package/core/shared/skills/do/references/estimation.md +13 -0
  154. package/core/shared/skills/do/references/pm_routing.md +25 -0
  155. package/core/shared/skills/do/references/tool_matrix.md +52 -0
  156. package/core/shared/skills/do-brainstorm/SKILL.md +252 -0
  157. package/core/shared/skills/do-code-review/README.md +49 -0
  158. package/core/shared/skills/do-code-review/SKILL.md +333 -0
  159. package/core/shared/skills/do-code-review/assets/sample_c_clean.c +71 -0
  160. package/core/shared/skills/do-code-review/assets/sample_c_smells.c +66 -0
  161. package/core/shared/skills/do-code-review/assets/sample_cpp_shift.cpp +19 -0
  162. package/core/shared/skills/do-code-review/assets/sample_csharp_clean.cs +100 -0
  163. package/core/shared/skills/do-code-review/assets/sample_csharp_smells.cs +78 -0
  164. package/core/shared/skills/do-code-review/assets/sample_java_clean.java +55 -0
  165. package/core/shared/skills/do-code-review/assets/sample_java_smells.java +56 -0
  166. package/core/shared/skills/do-code-review/assets/sample_json_clean.json +7 -0
  167. package/core/shared/skills/do-code-review/assets/sample_markdown_clean.md +30 -0
  168. package/core/shared/skills/do-code-review/assets/sample_markdown_smells.md +29 -0
  169. package/core/shared/skills/do-code-review/assets/sample_shell_heredoc.sh +38 -0
  170. package/core/shared/skills/do-code-review/assets/sample_shell_smells.sh +15 -0
  171. package/core/shared/skills/do-code-review/assets/sample_typescript_strings.ts +36 -0
  172. package/core/shared/skills/do-code-review/assets/sample_yaml_smells.yaml +6 -0
  173. package/core/shared/skills/do-code-review/content-types/config.md +83 -0
  174. package/core/shared/skills/do-code-review/content-types/markdown.md +132 -0
  175. package/core/shared/skills/do-code-review/expected_outputs/sample_c_clean_quality.json +79 -0
  176. package/core/shared/skills/do-code-review/expected_outputs/sample_c_smells_quality.json +169 -0
  177. package/core/shared/skills/do-code-review/expected_outputs/sample_cpp_shift_quality.json +49 -0
  178. package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_clean_quality.json +149 -0
  179. package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_smells_quality.json +157 -0
  180. package/core/shared/skills/do-code-review/expected_outputs/sample_java_clean_quality.json +67 -0
  181. package/core/shared/skills/do-code-review/expected_outputs/sample_java_smells_quality.json +97 -0
  182. package/core/shared/skills/do-code-review/expected_outputs/sample_json_clean_quality.json +33 -0
  183. package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_clean_quality.json +1 -0
  184. package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_smells_quality.json +16 -0
  185. package/core/shared/skills/do-code-review/expected_outputs/sample_shell_heredoc_quality.json +54 -0
  186. package/core/shared/skills/do-code-review/expected_outputs/sample_shell_smells_quality.json +91 -0
  187. package/core/shared/skills/do-code-review/expected_outputs/sample_typescript_strings_quality.json +60 -0
  188. package/core/shared/skills/do-code-review/expected_outputs/sample_yaml_smells_quality.json +46 -0
  189. package/core/shared/skills/do-code-review/languages/c.md +103 -0
  190. package/core/shared/skills/do-code-review/languages/cpp.md +103 -0
  191. package/core/shared/skills/do-code-review/languages/csharp.md +97 -0
  192. package/core/shared/skills/do-code-review/languages/dart.md +108 -0
  193. package/core/shared/skills/do-code-review/languages/go.md +92 -0
  194. package/core/shared/skills/do-code-review/languages/java.md +103 -0
  195. package/core/shared/skills/do-code-review/languages/kotlin.md +88 -0
  196. package/core/shared/skills/do-code-review/languages/php.md +109 -0
  197. package/core/shared/skills/do-code-review/languages/python.md +94 -0
  198. package/core/shared/skills/do-code-review/languages/ruby.md +105 -0
  199. package/core/shared/skills/do-code-review/languages/rust.md +99 -0
  200. package/core/shared/skills/do-code-review/languages/shell.md +90 -0
  201. package/core/shared/skills/do-code-review/languages/swift.md +88 -0
  202. package/core/shared/skills/do-code-review/languages/typescript.md +97 -0
  203. package/core/shared/skills/do-code-review/review-policy.json +47 -0
  204. package/core/shared/skills/do-code-review/rules/universal.md +49 -0
  205. package/core/shared/skills/do-code-review/scripts/code_quality_checker.py +1696 -0
  206. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +532 -0
  207. package/core/shared/skills/do-code-review/scripts/pr_analyzer.py +620 -0
  208. package/core/shared/skills/do-code-review/scripts/review_report_generator.py +505 -0
  209. package/core/shared/skills/do-constitution/SKILL.md +72 -0
  210. package/core/shared/skills/do-design/SKILL.md +212 -0
  211. package/core/shared/skills/do-diagnose/SKILL.md +156 -0
  212. package/core/shared/skills/do-diagnose/references/code_audit.md +15 -0
  213. package/core/shared/skills/do-diagnose/references/refactoring.md +12 -0
  214. package/core/shared/skills/do-diagnose/references/root_cause.md +17 -0
  215. package/core/shared/skills/do-document/SKILL.md +81 -0
  216. package/core/shared/skills/do-document/references/api-reference.md +42 -0
  217. package/core/shared/skills/do-document/references/deep_research.md +18 -0
  218. package/core/shared/skills/do-document/references/embedded-research.md +11 -0
  219. package/core/shared/skills/do-document/references/implementation-flow.md +105 -0
  220. package/core/shared/skills/do-document/references/index_generator.md +9 -0
  221. package/core/shared/skills/do-document/references/user-guide.md +22 -0
  222. package/core/shared/skills/do-execute-plan/SKILL.md +261 -0
  223. package/core/shared/skills/do-execute-plan/references/handoff_record.md +57 -0
  224. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +92 -0
  225. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +155 -0
  226. package/core/shared/skills/do-execute-plan/references/run_position.md +63 -0
  227. package/core/shared/skills/do-execute-plan/references/scaffold.md +441 -0
  228. package/core/shared/skills/do-execute-plan/references/task_attempts.md +41 -0
  229. package/core/shared/skills/do-flow/SKILL.md +186 -0
  230. package/core/shared/skills/do-flow/references/task_classes.md +134 -0
  231. package/core/shared/skills/do-git/SKILL.md +115 -0
  232. package/core/shared/skills/do-git/references/hotfix-ritual.md +44 -0
  233. package/core/shared/skills/do-git/references/lifecycle-policy.md +54 -0
  234. package/core/shared/skills/do-git/references/release-ritual.md +64 -0
  235. package/core/shared/skills/do-implement/SKILL.md +222 -0
  236. package/core/shared/skills/do-plan/SKILL.md +219 -0
  237. package/core/shared/skills/do-test/SKILL.md +113 -0
  238. package/core/shared/templates/doflow/constitution-template.md +28 -0
  239. package/core/shared/templates/doflow/data-model-template.md +169 -0
  240. package/core/shared/templates/doflow/design-template.md +193 -0
  241. package/core/shared/templates/doflow/external-contract-template.md +35 -0
  242. package/core/shared/templates/doflow/intent-template.md +45 -0
  243. package/core/shared/templates/doflow/plan-template.md +151 -0
  244. package/core/shared/templates/doflow/question-log-template.md +51 -0
  245. package/core/shared/templates/doflow/requirement-template.md +126 -0
  246. package/core/shared/templates/doflow/specs-template.md +55 -0
  247. package/core/shared/templates/doflow/state-template.md +59 -0
  248. package/core/shared/workflows/do-flow-chain.md +17 -0
  249. package/package.json +48 -4
  250. package/src/adapters/antigravity/index.js +631 -0
  251. package/src/adapters/claude/index.js +548 -0
  252. package/src/adapters/claude/mcp.js +94 -0
  253. package/src/adapters/codex/agents.js +208 -0
  254. package/src/adapters/codex/config.js +272 -0
  255. package/src/adapters/codex/hooks.js +203 -0
  256. package/src/adapters/codex/index.js +707 -0
  257. package/src/adapters/codex/mcp.js +271 -0
  258. package/src/adapters/codex/native-projection.js +34 -0
  259. package/src/adapters/copilot/index.js +413 -0
  260. package/src/adapters/copy-tree.js +620 -0
  261. package/src/adapters/gemini/hooks.js +210 -0
  262. package/src/adapters/gemini/index.js +390 -0
  263. package/src/adapters/hook-commands.js +60 -0
  264. package/src/adapters/index.js +106 -0
  265. package/src/adapters/kiro/index.js +310 -0
  266. package/src/adapters/mcp-entries.js +331 -0
  267. package/src/adapters/opencode/index.js +466 -0
  268. package/src/adapters/pi/index.js +279 -0
  269. package/src/adapters/pi/json-members.js +185 -0
  270. package/src/adapters/pi/mcp.js +373 -0
  271. package/src/adapters/resolve-source.js +19 -0
  272. package/src/cli/commands/install.js +218 -0
  273. package/src/cli/commands/list-backups.js +21 -0
  274. package/src/cli/commands/reconcile.js +120 -0
  275. package/src/cli/commands/remove.js +82 -0
  276. package/src/cli/commands/rollback.js +172 -0
  277. package/src/cli/commands/self-update.js +33 -0
  278. package/src/cli/commands/status.js +138 -0
  279. package/src/cli/commands/tools.js +92 -0
  280. package/src/cli/commands/update.js +162 -0
  281. package/src/cli/index.js +568 -0
  282. package/src/cli/runtime-commands.js +281 -0
  283. package/src/cli/shared.js +218 -0
  284. package/src/helper/bash-helper.js +34 -0
  285. package/src/helper/git.js +24 -0
  286. package/src/helper/guardrails.js +49 -0
  287. package/src/helper/harness-paths.js +140 -0
  288. package/src/helper/marker-merge.js +116 -0
  289. package/src/helper/prompt.js +61 -0
  290. package/src/helper/repo-root.js +29 -0
  291. package/src/helper/settings-merge.js +151 -0
  292. package/src/helper/settings-scope.js +33 -0
  293. package/src/helper/toml.js +244 -0
  294. package/src/install/backup-set.js +149 -0
  295. package/src/install/backup.js +724 -0
  296. package/src/install/context.js +58 -0
  297. package/src/install/manifest.js +145 -0
  298. package/src/install/mcp.js +254 -0
  299. package/src/install/paths.js +84 -0
  300. package/src/install/targets.js +90 -0
  301. package/src/install/tool-lifecycle.js +151 -0
  302. package/src/lifecycle/bash-availability.js +25 -0
  303. package/src/lifecycle/index.js +812 -0
  304. package/src/lifecycle/mcp-index.js +28 -0
  305. package/src/lifecycle/policies.js +73 -0
  306. package/src/lifecycle/view.js +175 -0
  307. package/src/registry/index.js +611 -0
  308. package/src/release/build-package.js +91 -0
  309. package/src/release/changelog-section.js +70 -0
  310. package/src/release/sync-plugin-versions.js +66 -0
  311. package/src/runtime/advice/cli.js +283 -0
  312. package/src/runtime/advice/client.js +324 -0
  313. package/src/runtime/advice/config.js +189 -0
  314. package/src/runtime/advice/doctor.js +169 -0
  315. package/src/runtime/advice/endpoint-guard.js +134 -0
  316. package/src/runtime/advice/kinds.js +154 -0
  317. package/src/runtime/advice/providers/ollama-systemone.js +209 -0
  318. package/src/runtime/advice/record.js +166 -0
  319. package/src/runtime/advice/registry.js +170 -0
  320. package/src/runtime/advice/strength.js +66 -0
  321. package/src/runtime/brief-tools.js +342 -0
  322. package/src/runtime/c4-project.js +371 -0
  323. package/src/runtime/capability-router.js +484 -0
  324. package/src/runtime/checkouts.js +170 -0
  325. package/src/runtime/claims.js +468 -0
  326. package/src/runtime/cli-result.js +55 -0
  327. package/src/runtime/cli.js +678 -0
  328. package/src/runtime/command-detect.js +516 -0
  329. package/src/runtime/context-pack.js +492 -0
  330. package/src/runtime/decision-register.js +493 -0
  331. package/src/runtime/evidence-ledger.js +338 -0
  332. package/src/runtime/failure/capture.js +272 -0
  333. package/src/runtime/failure/classifier.js +68 -0
  334. package/src/runtime/failure/cli.js +178 -0
  335. package/src/runtime/failure/home.js +64 -0
  336. package/src/runtime/failure/store.js +226 -0
  337. package/src/runtime/feature-resolve.js +79 -0
  338. package/src/runtime/freshness.js +177 -0
  339. package/src/runtime/health.js +863 -0
  340. package/src/runtime/history-compactor.js +237 -0
  341. package/src/runtime/implementation-gate.js +321 -0
  342. package/src/runtime/inventory/coverage.js +218 -0
  343. package/src/runtime/inventory/identity.js +65 -0
  344. package/src/runtime/inventory/index.js +705 -0
  345. package/src/runtime/inventory/precedence.js +134 -0
  346. package/src/runtime/inventory/read-scopes.js +131 -0
  347. package/src/runtime/inventory/siblings.js +192 -0
  348. package/src/runtime/knowledge/chunker.js +124 -0
  349. package/src/runtime/knowledge/hybrid.js +149 -0
  350. package/src/runtime/knowledge/index-store.js +145 -0
  351. package/src/runtime/knowledge/retrieval.js +194 -0
  352. package/src/runtime/leak-scan.js +185 -0
  353. package/src/runtime/lifecycle/cli.js +276 -0
  354. package/src/runtime/lifecycle/event-store.js +340 -0
  355. package/src/runtime/lifecycle/fold.js +353 -0
  356. package/src/runtime/lifecycle/followup.js +432 -0
  357. package/src/runtime/lifecycle/goal.js +249 -0
  358. package/src/runtime/lifecycle/intent-writer.js +86 -0
  359. package/src/runtime/lifecycle/overview.js +292 -0
  360. package/src/runtime/lifecycle/release.js +241 -0
  361. package/src/runtime/lifecycle/report-store.js +221 -0
  362. package/src/runtime/lifecycle/retention.js +212 -0
  363. package/src/runtime/lifecycle/root.js +71 -0
  364. package/src/runtime/lifecycle/status.js +353 -0
  365. package/src/runtime/lifecycle/store-upkeep.js +393 -0
  366. package/src/runtime/locator-resolve.js +158 -0
  367. package/src/runtime/mask.js +210 -0
  368. package/src/runtime/mcp-consultations.js +412 -0
  369. package/src/runtime/mcp-reach.js +157 -0
  370. package/src/runtime/model-router.js +153 -0
  371. package/src/runtime/outcome.js +467 -0
  372. package/src/runtime/reach.js +148 -0
  373. package/src/runtime/readiness-record.js +150 -0
  374. package/src/runtime/readiness.js +435 -0
  375. package/src/runtime/recovery.js +282 -0
  376. package/src/runtime/research-request.js +231 -0
  377. package/src/runtime/retrieval-plan.js +578 -0
  378. package/src/runtime/scaffold/artifacts.js +217 -0
  379. package/src/runtime/scaffold/fingerprint.js +56 -0
  380. package/src/runtime/scaffold/generate.js +911 -0
  381. package/src/runtime/scaffold/languages.js +245 -0
  382. package/src/runtime/storage/cli.js +64 -0
  383. package/src/runtime/storage/engine.js +249 -0
  384. package/src/runtime/storage/registry.js +130 -0
  385. package/src/runtime/storage/roots.js +63 -0
  386. package/src/runtime/storage/rules.js +282 -0
  387. package/src/runtime/storage/upkeep.js +243 -0
  388. package/src/runtime/storage/work-status.js +326 -0
  389. package/src/runtime/task-attempts.js +415 -0
  390. package/src/runtime/task-classifier.js +550 -0
  391. package/src/runtime/task-scope.js +130 -0
  392. package/src/runtime/task-state.js +176 -0
  393. package/src/runtime/trace/indicators.js +235 -0
  394. package/src/runtime/trace/ledger.js +626 -0
  395. package/src/runtime/trace/render.js +76 -0
  396. package/src/runtime/trace/views.js +459 -0
  397. package/src/runtime/verification/contract-runner.js +341 -0
  398. package/src/runtime/verification/engine.js +1461 -0
  399. package/src/runtime/verification/registry.js +130 -0
  400. package/src/runtime/verification/scope-bound.js +418 -0
  401. package/src/runtime/verification/test-selection.js +172 -0
  402. package/src/runtime/workflow-engine.js +588 -0
  403. package/src/runtime/workflow-orchestrator.js +953 -0
  404. package/src/runtime/worktree.js +340 -0
  405. package/src/state/index.js +193 -0
  406. package/src/state/lockfile.js +136 -0
  407. package/src/state/run-lock.js +408 -0
  408. package/types/node-commonjs.d.ts +68 -0
  409. package/types/runtime.d.ts +91 -0
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+ // context.js — resolved-scope/properties view, shown by `install --dry-run` and `status`.
3
+ // Purpose: once install targets can be $HOME (global) OR a project path, it stops being obvious
4
+ // at a glance which one a given invocation resolves to — this makes that explicit for debugging.
5
+ //
6
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
7
+ // native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
8
+ // caller's `require('../install/context.js')` keeps working while the tree is still `.js`.
9
+ const path = require('node:path');
10
+ const { sourceCommit: gitSourceCommit } = require('../helper/git.js');
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+ /**
23
+ * `sourceCommit` lets a caller (bin/doflow.js) pass an already-resolved commit instead of this
24
+ * module spawning its own `git rev-parse`; omit it to resolve here (e.g. tests calling this module
25
+ * directly). Returns a plain properties object (used by both the text and --json renderers).
26
+ */
27
+ function resolveContext({ repoRoot, global, projectRoot, targets, dirs, sourceCommit, managedResources } ) {
28
+ const context = {
29
+ scope: global ? 'global' : 'project',
30
+ scopeRoot: global ? path.dirname(dirs.claude) : path.resolve(projectRoot),
31
+ targets,
32
+ dirs: Object.fromEntries(targets.map((t) => [t, dirs[t]])),
33
+ repoRoot,
34
+ sourceCommit: sourceCommit ?? gitSourceCommit(repoRoot),
35
+ nodeVersion: process.version,
36
+ };
37
+ // The lifecycle engine may provide its ownership ledger for status/verify output. Do not add
38
+ // an empty field for existing callers: their JSON/text output remains backwards compatible.
39
+ if (managedResources !== undefined) context.managedResources = managedResources;
40
+ return context;
41
+ }
42
+
43
+ /** Human-readable "Resolved context" block, printed to stderr (keeps stdout clean like sync.sh's log()). */
44
+ function printContext(ctx ) {
45
+ const lines = [
46
+ '[CONTEXT] Resolved install context',
47
+ ` scope : ${ctx.scope} (root: ${ctx.scopeRoot})`,
48
+ ` targets : ${ctx.targets.join(', ')}`,
49
+ ...ctx.targets.map((t ) => ` ${t.padEnd(7)}-> ${ctx.dirs[t]}`),
50
+ ` repo root : ${ctx.repoRoot}`,
51
+ ` source commit : ${ctx.sourceCommit}`,
52
+ ` node : ${ctx.nodeVersion}`,
53
+ ];
54
+ if (ctx.managedResources !== undefined) lines.push(` managed resources: ${ctx.managedResources.length}`);
55
+ console.error(lines.join('\n'));
56
+ }
57
+
58
+ module.exports = { resolveContext, printContext };
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+ // manifest.js — port of sync.sh's write_manifest/read_manifest.
3
+ // PARITY: schema {script_version,last_operation,last_run,source_path,source_commit,last_backup_id,
4
+ // tools:{tool:{installed,last_updated}}}; atomic write via tmp file + rename (never partial-write
5
+ // the manifest, matching sync.sh's `mktemp` + `mv`).
6
+ //
7
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
8
+ // native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
9
+ // caller's `require('../install/manifest.js')` keeps working while the tree is still `.js`.
10
+ const fs = require('node:fs');
11
+ const path = require('node:path');
12
+ const { sourceCommit: gitSourceCommit } = require('../helper/git.js');
13
+
14
+ // Explicit migration bridge: pre-registry installations stored this record beneath .claude.
15
+ // Lifecycle commands use the scope-neutral .doflow location; only the neutral-state importer
16
+ // calls the legacy reader below.
17
+ const LEGACY_MANIFEST_FILE_NAME = '.install-manifest.json';
18
+ const MANIFEST_FILE_NAME = LEGACY_MANIFEST_FILE_NAME;
19
+
20
+ function manifestPath(claudeDir ) {
21
+ return path.join(claudeDir, MANIFEST_FILE_NAME);
22
+ }
23
+
24
+ function canonicalManifestPath(scopeRoot ) {
25
+ return path.join(path.resolve(scopeRoot), '.doflow', MANIFEST_FILE_NAME);
26
+ }
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+ /**
43
+ * `sourceCommit` lets a caller (bin/doflow.js) pass an already-resolved commit instead of this
44
+ * module spawning its own `git rev-parse`; omit it to resolve here (e.g. tests calling this module
45
+ * directly). An `mcp_servers` list a 1.18.0 install recorded is carried over as it is and never
46
+ * written anew: doflow.lock records each harness's MCP selection now, and the old list is read only
47
+ * as a fallback for a harness the lock lacks. `managedResources` is the optional ownership ledger
48
+ * for fine-grained Codex resources. Each record uses {target,scope,kind,identity,sourceVersion,
49
+ * fingerprint,selection,recoveryPoint}. Omit it to preserve a ledger written by a newer command.
50
+ */
51
+ function writeManifest({ scopeRoot, claudeDir, scriptVersion, operation, repoRoot, backupId = '', tools, date, dryRun = false, sourceCommit, managedResources } ) {
52
+ if (!scopeRoot && !claudeDir) throw new Error('scopeRoot is required for lifecycle metadata');
53
+ const file = scopeRoot ? canonicalManifestPath(scopeRoot) : manifestPath(claudeDir );
54
+ if (dryRun) return file;
55
+
56
+ // Preserve per-tool last_updated for tools NOT part of this operation (matches sync.sh's
57
+ // incremental jq merge, which only touches the tools passed to write_manifest).
58
+ let existingTools = {};
59
+ let existingMcpServers ;
60
+ let existingManagedResources ;
61
+ const existingFile = file;
62
+ if (existingFile && fs.existsSync(existingFile)) {
63
+ try {
64
+ const existing = JSON.parse(fs.readFileSync(existingFile, 'utf8'));
65
+ existingTools = existing.tools || {};
66
+ existingMcpServers = existing.mcp_servers;
67
+ // `managedResources` is accepted as a short-lived compatibility alias for manifests
68
+ // written by pre-release lifecycle work. New writes use the established snake_case
69
+ // top-level convention, while resource field names stay camelCase as specified by the
70
+ // ownership contract.
71
+ existingManagedResources = existing.managed_resources ?? existing.managedResources;
72
+ } catch { /* start fresh */ }
73
+ }
74
+ const ts = date.toISOString().replace(/\.\d+Z$/, 'Z');
75
+ const toolsOut = { ...existingTools };
76
+ for (const t of tools) toolsOut[t] = { installed: true, last_updated: ts };
77
+
78
+ const manifest = {
79
+ script_version: scriptVersion,
80
+ last_operation: operation,
81
+ last_run: ts,
82
+ source_path: repoRoot,
83
+ source_commit: sourceCommit ?? gitSourceCommit(repoRoot),
84
+ last_backup_id: backupId,
85
+ tools: toolsOut,
86
+ mcp_servers: existingMcpServers,
87
+ };
88
+ const resources = managedResources ?? existingManagedResources;
89
+ if (resources !== undefined) manifest.managed_resources = resources;
90
+
91
+ fs.mkdirSync(path.dirname(file), { recursive: true });
92
+ // Security: the temp file must live next to `file` itself, not a shared os.tmpdir() — a
93
+ // world-writable /tmp plus a PID+timestamp-derived (i.e. guessable) name is a symlink-race
94
+ // waiting to happen (plant a symlink at the predicted path, the write clobbers its target).
95
+ // 'wx' (O_CREAT|O_EXCL) refuses to open through an existing symlink or file at all, and being
96
+ // on the same filesystem as `file` makes the final rename a guaranteed atomic same-fs op
97
+ // (os.tmpdir() offered no such guarantee — e.g. containers where /tmp is a separate tmpfs mount).
98
+ const tmp = path.join(path.dirname(file), `.install-manifest-${process.pid}-${date.getTime()}.json.tmp`);
99
+ fs.writeFileSync(tmp, `${JSON.stringify(manifest, null, 2)}\n`, { flag: 'wx' });
100
+ fs.renameSync(tmp, file); // atomic on the same filesystem, matches sync.sh's mktemp + mv
101
+ return file;
102
+ }
103
+
104
+ /** null if no manifest yet. */
105
+ function readManifestFile(file ) {
106
+ if (!fs.existsSync(file)) return null;
107
+ try {
108
+ const m = JSON.parse(fs.readFileSync(file, 'utf8'));
109
+ return {
110
+ operation: m.last_operation ?? 'none',
111
+ lastRun: m.last_run ?? 'none',
112
+ sourceCommit: m.source_commit ?? 'none',
113
+ backupId: m.last_backup_id ?? 'none',
114
+ tools: m.tools ?? {},
115
+ scriptVersion: m.script_version ?? 'none',
116
+ mcpServers: m.mcp_servers ?? null,
117
+ // Older manifests have no ownership ledger. Returning an empty list gives lifecycle
118
+ // callers a migration-safe baseline without changing their legacy install state.
119
+ managedResources: Array.isArray(m.managed_resources)
120
+ ? m.managed_resources
121
+ : (Array.isArray(m.managedResources) ? m.managedResources : []),
122
+ };
123
+ } catch {
124
+ return {
125
+ operation: 'error', lastRun: 'error', sourceCommit: 'error', backupId: 'error',
126
+ tools: {}, scriptVersion: 'error', mcpServers: null, managedResources: [],
127
+ };
128
+ }
129
+ }
130
+
131
+ /** Read only the canonical scope-neutral manifest used by lifecycle commands. */
132
+ function readInstallManifest({ scopeRoot } ) {
133
+ if (!scopeRoot) throw new Error('scopeRoot is required');
134
+ return readManifestFile(canonicalManifestPath(scopeRoot));
135
+ }
136
+
137
+ /** Read only the legacy path; neutral-state migration uses this deliberately. */
138
+ function readManifest(claudeDir ) {
139
+ return readManifestFile(manifestPath(claudeDir));
140
+ }
141
+
142
+ module.exports = {
143
+ LEGACY_MANIFEST_FILE_NAME, MANIFEST_FILE_NAME, manifestPath, canonicalManifestPath,
144
+ writeManifest, readManifest, readInstallManifest,
145
+ };
@@ -0,0 +1,254 @@
1
+ 'use strict';
2
+ // mcp.js — MCP server selection: which of the registry's servers each harness gets, from --mcp,
3
+ // the interactive checkbox, doflow.lock, the ledger or a 1.18.0 manifest. Writing a harness's MCP
4
+ // file is that harness's adapter's job (src/adapters/<id>/), never this module's.
5
+ //
6
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
7
+ // native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
8
+ // caller's `require('../install/mcp.js')` keeps working while the tree is still `.js`. The
9
+ // `../registry` and `../state/lockfile` requires stay bare while those modules are still `.js`.
10
+ const { readSyncBlocking } = require('../helper/prompt.js');
11
+ const { selectMcpServers, nativeMcpCatalog, mcpCapable } = require('../registry/index.js');
12
+ const { pinnedSelections } = require('../state/lockfile.js');
13
+
14
+ const ESC = String.fromCharCode(27);
15
+ const CTRL_C = String.fromCharCode(3);
16
+
17
+ /** `registry` is a loaded registry (src/registry#loadRegistry); returns server names in registry
18
+ * declaration order. */
19
+ function readAllServers(registry ) {
20
+ return nativeMcpCatalog(selectMcpServers(registry)).allServers;
21
+ }
22
+
23
+ /** Parse `--mcp`: `all` and `none` are keywords that cannot be mixed with names or each other, an
24
+ * empty list and an unknown name are errors, names are deduplicated. `null` when the flag is absent. */
25
+ function parseMcpFlag(requested , catalogIds ) {
26
+ if (!requested) return null;
27
+ const keywords = requested.filter((s) => s === 'all' || s === 'none');
28
+ if (keywords.length) {
29
+ if (keywords.length !== requested.length) {
30
+ throw new Error(`--mcp keyword '${keywords[0]}' cannot be combined with server names`);
31
+ }
32
+ if (new Set(requested).size > 1) {
33
+ throw new Error("Choose either '--mcp all' or '--mcp none', not both");
34
+ }
35
+ return keywords[0] === 'all' ? [...catalogIds] : [];
36
+ }
37
+ if (requested.length === 0) {
38
+ throw new Error("--mcp requires at least one server; use '--mcp none' for an explicit empty selection");
39
+ }
40
+ const invalid = requested.filter((s) => !catalogIds.includes(s));
41
+ if (invalid.length) {
42
+ throw new Error(`Unknown MCP server(s): ${invalid.join(', ')} (valid: ${catalogIds.join(', ')})`);
43
+ }
44
+ return [...new Set(requested)];
45
+ }
46
+
47
+ const holdsRows = (ledger , harness ) => (ledger?.resources ?? []).some((row ) => row.harness === harness);
48
+
49
+ /** Ids of `lists` that the catalog still declares, as one union in registry order. */
50
+ const catalogUnion = (catalogIds , lists ) => catalogIds.filter((id) => lists.some((ids) => ids.includes(id)));
51
+
52
+ /** 1.18.0 wrote lock rows only for claude and codex, and only non-empty ones. Any other row was
53
+ * written by a later run, which already settled which of that harness's entries are DoFlow's. */
54
+ const LEGACY_LOCK_HARNESSES = ['claude', 'codex'];
55
+
56
+ function settledByLaterRun(rows , harness ) {
57
+ return harness in rows && (!LEGACY_LOCK_HARNESSES.includes(harness) || rows[harness].length === 0);
58
+ }
59
+
60
+ /** The servers a 1.18.0 install recorded writing for this harness, from state no later run writes:
61
+ * the manifest's MCP list, Antigravity's rows that carry no fingerprint, and Copilot's whole-map
62
+ * registration row, which covers the manifest's list or, with none, the catalog 1.18.0 wrote. */
63
+ function legacyRecordedIds({ catalogIds, ledger, harness, manifestServers } ) {
64
+ const rows = (ledger?.resources ?? []).filter((row ) => row.harness === harness);
65
+ const ids = [...(manifestServers ?? [])];
66
+ for (const row of rows) if (row.kind === 'mcp-server' && (row.fingerprint ?? null) === null) ids.push(row.identity);
67
+ if (!manifestServers && rows.some((row ) => row.ownershipIdentity === `${harness}:mcp:registration`)) ids.push(...catalogIds);
68
+ return catalogUnion(catalogIds, [ids]);
69
+ }
70
+
71
+ /** Per MCP-capable harness, the servers DoFlow may adopt when their entries already equal its own:
72
+ * only what a 1.18.0 install recorded for a harness the ledger holds, and only until a later run
73
+ * has recorded the harness's own selection. An entry a user wrote is never adoptable merely because
74
+ * DoFlow installed something else for that harness. Retired ids are dropped. */
75
+ function adoptableMcpIds({ registry, lock, ledger, harnesses, manifestServers = null } ) {
76
+ const catalogIds = readAllServers(registry);
77
+ const rows = pinnedSelections(lock);
78
+ return Object.fromEntries(harnesses.filter((harness) => mcpCapable(registry, harness)).map((harness) => {
79
+ if (!holdsRows(ledger, harness) || settledByLaterRun(rows, harness)) return [harness, []];
80
+ return [harness, legacyRecordedIds({ catalogIds, ledger, harness, manifestServers })];
81
+ }));
82
+ }
83
+
84
+ /** Servers another harness of this scope still has recorded: the union of the lock rows of the lock's
85
+ * harnesses that are not targeted, registry order. */
86
+ function retainedMcpIds(catalogIds , rows , targets ) {
87
+ return catalogUnion(catalogIds, Object.entries(rows).filter(([harness]) => !targets.includes(harness)).map(([, ids]) => ids));
88
+ }
89
+
90
+ /**
91
+ * Decide each targeted MCP-capable harness's servers. Per harness, the first step that applies:
92
+ * 1. --mcp <list>|all|none -> the parsed ids (flag)
93
+ * 2. install on a real TTY -> one checkbox for every harness; a null answer falls through (prompt)
94
+ * 3. a lock row -> that row (recorded)
95
+ * 4. ledger rows -> 'keep': the servers the harness owns now (kept)
96
+ * 5. a 1.18.0 manifest list -> that list (manifest)
97
+ * 6. otherwise -> none (default)
98
+ * Lock and manifest ids the registry retired are dropped and reported once through `onStale`; an
99
+ * explicit --mcp naming one stays an error. Pure apart from `promptFn` and `onStale`.
100
+ */
101
+ function resolveMcpSelections({
102
+ cmd, requested, targets, registry, lock, ledger, manifestServers = null, interactive = false, promptFn, onStale,
103
+ } ) {
104
+ const catalogIds = readAllServers(registry);
105
+ const flagged = parseMcpFlag(requested, catalogIds);
106
+ const rows = pinnedSelections(lock);
107
+ const capable = targets.filter((harness ) => mcpCapable(registry, harness));
108
+ const retired = new Set ();
109
+ const known = (ids ) => {
110
+ for (const id of ids) if (!catalogIds.includes(id)) retired.add(id);
111
+ return catalogUnion(catalogIds, [ids]);
112
+ };
113
+
114
+ let prompted = null;
115
+ if (!flagged && cmd === 'install' && interactive && capable.length) {
116
+ const recorded = capable.filter((harness ) => harness in rows).map((harness ) => rows[harness]);
117
+ const seed = recorded.length ? catalogUnion(catalogIds, recorded.map(known))
118
+ : manifestServers ? known(manifestServers) : [...catalogIds];
119
+ prompted = promptFn(catalogIds, seed);
120
+ }
121
+
122
+ const pick = (harness ) => {
123
+ if (flagged) return [flagged, 'flag'];
124
+ if (prompted !== null) return [prompted, 'prompt'];
125
+ if (harness in rows) return [known(rows[harness]), 'recorded'];
126
+ if (holdsRows(ledger, harness)) return ['keep', 'kept'];
127
+ if (manifestServers) return [known(manifestServers), 'manifest'];
128
+ return [[], 'default'];
129
+ };
130
+ const selections = {};
131
+ const sources = {};
132
+ for (const harness of capable) [selections[harness], sources[harness]] = pick(harness);
133
+ for (const [harness, ids] of Object.entries(rows)) if (!targets.includes(harness)) known(ids );
134
+ if (retired.size && onStale) onStale([...retired].sort());
135
+
136
+ return {
137
+ selections,
138
+ sources,
139
+ adoptable: adoptableMcpIds({ registry, lock, ledger, harnesses: capable, manifestServers }),
140
+ retainedMcpIds: retainedMcpIds(catalogIds, rows, targets),
141
+ };
142
+ }
143
+
144
+ /** The selections a scope has recorded, for readers that never prompt (reconcile, status, inventory):
145
+ * each targeted MCP-capable harness's lock row, or 'keep' when the lock has none. Retired ids are
146
+ * dropped silently. */
147
+ function recordedMcpSelections({ registry, lock, ledger, targets, manifestServers = null } ) {
148
+ const catalogIds = readAllServers(registry);
149
+ const rows = pinnedSelections(lock);
150
+ const capable = targets.filter((harness ) => mcpCapable(registry, harness));
151
+ return {
152
+ selections: Object.fromEntries(capable.map((harness ) => [harness,
153
+ harness in rows ? catalogUnion(catalogIds, [rows[harness]]) : 'keep'])),
154
+ adoptable: adoptableMcpIds({ registry, lock, ledger, harnesses: capable, manifestServers }),
155
+ retainedMcpIds: retainedMcpIds(catalogIds, rows, targets),
156
+ };
157
+ }
158
+
159
+ const KEY = {
160
+ UP: `${ESC}[A`,
161
+ DOWN: `${ESC}[B`,
162
+ SPACE: ' ',
163
+ ENTER_CR: '\r',
164
+ ENTER_LF: '\n',
165
+ CTRL_C,
166
+ ALL: 'a',
167
+ };
168
+
169
+ /**
170
+ * Block for one raw-mode keypress, via prompt.js's readSyncBlocking (retries the EAGAIN-while-TTY
171
+ * case, bounded by a deadline for a genuinely unusable fd). Returns the decoded chunk, or null if
172
+ * the fd is unusable.
173
+ */
174
+ function readKeypress(buf ) {
175
+ try {
176
+ const n = readSyncBlocking(0, buf);
177
+ return buf.toString('utf8', 0, n);
178
+ } catch {
179
+ return null;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Synchronous raw-mode checkbox prompt (arrow keys / j-k to move, space to toggle, 'a' to
185
+ * toggle-all, enter to confirm). Matches src/prompt.js's synchronous-read style — this CLI has no
186
+ * async control flow to hang off, so a real TTY read loop is built directly on fs.readSync(0, ...),
187
+ * the same primitive confirm()/promptLine() already use.
188
+ * Returns selected server names, or null if no usable TTY (caller falls back).
189
+ */
190
+ function promptMcpCheckbox(servers , initialSelected , message = 'Select MCP servers to install:') {
191
+ if (!process.stdin.isTTY || !process.stdout.isTTY) return null;
192
+ if (servers.length === 0) return [];
193
+
194
+ const selected = new Set(initialSelected);
195
+ let cursor = 0;
196
+ const help = ' (up/down or j/k move, space toggle, a toggle-all, enter confirm)';
197
+
198
+ const render = (first ) => {
199
+ if (!first) process.stdout.write(`${ESC}[${servers.length + 2}A`);
200
+ console.log(message);
201
+ console.log(help);
202
+ for (let i = 0; i < servers.length; i++) {
203
+ const mark = selected.has(servers[i]) ? '[x]' : '[ ]';
204
+ const pointer = i === cursor ? '>' : ' ';
205
+ console.log(`${pointer} ${mark} ${servers[i]}`);
206
+ }
207
+ };
208
+
209
+ let wasRaw = false;
210
+ let aborted = false;
211
+ try {
212
+ wasRaw = Boolean(process.stdin.isRaw);
213
+ process.stdin.setRawMode(true);
214
+ render(true);
215
+ const buf = Buffer.alloc(16);
216
+ for (;;) {
217
+ const chunk = readKeypress(buf);
218
+ if (chunk === null) {
219
+ aborted = true;
220
+ break;
221
+ }
222
+ if (chunk === KEY.ENTER_CR || chunk === KEY.ENTER_LF) break;
223
+ if (chunk === KEY.CTRL_C) {
224
+ console.log('\n[INFO] Aborted.');
225
+ process.exit(130);
226
+ }
227
+ if (chunk === KEY.UP || chunk === 'k') cursor = (cursor - 1 + servers.length) % servers.length;
228
+ else if (chunk === KEY.DOWN || chunk === 'j') cursor = (cursor + 1) % servers.length;
229
+ else if (chunk === KEY.SPACE) {
230
+ const s = servers[cursor];
231
+ if (selected.has(s)) selected.delete(s);
232
+ else selected.add(s);
233
+ } else if (chunk === KEY.ALL) {
234
+ if (selected.size === servers.length) selected.clear();
235
+ else for (const s of servers) selected.add(s);
236
+ }
237
+ render(false);
238
+ }
239
+ } finally {
240
+ process.stdin.setRawMode(wasRaw);
241
+ }
242
+ console.log('');
243
+ if (aborted) return null; // fd went unusable mid-prompt — caller falls back to manifest/all
244
+ return servers.filter((s) => selected.has(s));
245
+ }
246
+
247
+ module.exports = {
248
+ readAllServers,
249
+ parseMcpFlag,
250
+ resolveMcpSelections,
251
+ recordedMcpSelections,
252
+ adoptableMcpIds,
253
+ promptMcpCheckbox,
254
+ };
@@ -0,0 +1,84 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
4
+ // native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
5
+ // caller's `require('../install/paths.js')` keeps working while the rest of the tree is still `.js`.
6
+ const path = require('node:path');
7
+
8
+ const DOFLOW_DIR_NAME = '.doflow';
9
+ const BACKUP_DIR_NAME = 'backups';
10
+ const INSTALL_MANIFEST_FILE_NAME = '.install-manifest.json';
11
+
12
+ // Explicit migration bridge, mirroring manifest.js's: before 7de6d5f ("centralize lifecycle
13
+ // metadata under .doflow") both the install manifest and the backup root were anchored to the
14
+ // Claude harness directory, so restore points landed in `<scopeRoot>/.claude/backups` — $HOME
15
+ // globally, the project root otherwise. That commit moved the write location to `.doflow/` and
16
+ // bridged the manifest, leaving every pre-move restore point unreachable. The two names below
17
+ // exist so the READ side of rollback/list-backups can still see what the move left behind.
18
+ // Nothing may ever write, prune or delete here: those directories are a user's only recovery
19
+ // material and DoFlow no longer owns their lifecycle.
20
+ const LEGACY_BACKUP_ANCHOR_DIR_NAME = '.claude';
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+ /** Read-only legacy backup location for a scope. Never a write or prune target — see the note above. */
35
+ function legacyBackupReadRoot({ scopeRoot } ) {
36
+ if (typeof scopeRoot !== 'string' || !scopeRoot) throw new Error('scopeRoot is required');
37
+ return path.join(path.resolve(scopeRoot), LEGACY_BACKUP_ANCHOR_DIR_NAME, BACKUP_DIR_NAME);
38
+ }
39
+
40
+ /** True for `<anything>/.claude/backups` — used to refuse writes/prunes against the legacy root. */
41
+ function isLegacyBackupReadRoot(candidate ) {
42
+ if (typeof candidate !== 'string' || !candidate) return false;
43
+ const resolved = path.resolve(candidate);
44
+ return path.basename(resolved) === BACKUP_DIR_NAME
45
+ && path.basename(path.dirname(resolved)) === LEGACY_BACKUP_ANCHOR_DIR_NAME;
46
+ }
47
+
48
+ /** Inverse of `doflowPaths().backupRoot`: recover the scope root from a canonical backup root so a
49
+ * read path handed only that root can still derive the legacy one. Returns null for any path that
50
+ * is not shaped `<scopeRoot>/.doflow/backups` (a test fixture, a hand-passed directory) — the
51
+ * bridge then stays inert rather than inventing a sibling `.claude` next to an arbitrary path. */
52
+ function scopeRootFromCanonicalBackupRoot(backupRoot ) {
53
+ if (typeof backupRoot !== 'string' || !backupRoot) return null;
54
+ const resolved = path.resolve(backupRoot);
55
+ if (path.basename(resolved) !== BACKUP_DIR_NAME) return null;
56
+ const doflowRoot = path.dirname(resolved);
57
+ if (path.basename(doflowRoot) !== DOFLOW_DIR_NAME) return null;
58
+ return path.dirname(doflowRoot);
59
+ }
60
+
61
+ /** Resolve DoFlow-owned lifecycle metadata without anchoring it to a harness directory. */
62
+ function doflowPaths({ scopeRoot } ) {
63
+ if (typeof scopeRoot !== 'string' || !scopeRoot) throw new Error('scopeRoot is required');
64
+ const root = path.resolve(scopeRoot);
65
+ const doflowRoot = path.join(root, DOFLOW_DIR_NAME);
66
+ return {
67
+ scopeRoot: root,
68
+ doflowRoot,
69
+ backupRoot: path.join(doflowRoot, BACKUP_DIR_NAME),
70
+ legacyBackupReadRoot: legacyBackupReadRoot({ scopeRoot: root }),
71
+ manifestPath: path.join(doflowRoot, INSTALL_MANIFEST_FILE_NAME),
72
+ };
73
+ }
74
+
75
+ module.exports = {
76
+ DOFLOW_DIR_NAME,
77
+ BACKUP_DIR_NAME,
78
+ INSTALL_MANIFEST_FILE_NAME,
79
+ LEGACY_BACKUP_ANCHOR_DIR_NAME,
80
+ doflowPaths,
81
+ legacyBackupReadRoot,
82
+ isLegacyBackupReadRoot,
83
+ scopeRootFromCanonicalBackupRoot,
84
+ };
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+ // targets.js — target validation + install-dir resolution.
3
+ // PARITY: sync.sh is global-only — $HOME/.{claude,codex,gemini}, hardcoded, NOT CLAUDE_CONFIG_DIR.
4
+ // doflow extends this with a second, DoFlow-only scope: project-local install (this repo's own
5
+ // .claude/ is a real precedent — Claude Code, Codex, and Copilot all support project-scoped config
6
+ // alongside global, see agent-docs/research/context-setup-claude-codex-copilot.md).
7
+ //
8
+ // Scope is explicit, never inferred: -g/--global -> $HOME-based (sync.sh parity); default (no -g) ->
9
+ // rooted at an explicit project path, defaulting to cwd ('.') when no path is given. The parity
10
+ // harness must pass -g for doflow, since sync.sh has no project mode to compare against.
11
+ //
12
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
13
+ // native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
14
+ // caller's `require('../install/targets.js')` keeps working while the rest of the tree is still `.js`.
15
+ const os = require('node:os');
16
+ const path = require('node:path');
17
+
18
+ const VALID = ['claude', 'codex', 'gemini', 'copilot', 'kiro', 'opencode', 'pi', 'antigravity'];
19
+
20
+ /** The target used when `--target` is omitted. Deliberately one harness, not all of VALID: an
21
+ * install with no flag should touch the one tool the user almost certainly has, not write
22
+ * configuration into every harness DoFlow knows about — several of which may not be installed at
23
+ * all. That mattered less when VALID was three entries and matters more as it grows. Ask for the
24
+ * rest explicitly with `--target claude,codex,gemini`. */
25
+ const DEFAULT_TARGETS = ['claude'];
26
+
27
+ const TARGET_ALIASES = {
28
+ agy: 'antigravity',
29
+ };
30
+
31
+ /** Default to DEFAULT_TARGETS; validate any explicitly requested. */
32
+ function resolveTargets(requested ) {
33
+ const rawTargets = requested && requested.length ? requested : [...DEFAULT_TARGETS];
34
+ const targets = rawTargets.map((t) => TARGET_ALIASES[t] || t);
35
+ for (const t of targets) {
36
+ if (!VALID.includes(t)) {
37
+ throw new Error(`Unknown target: '${t}' (valid: ${VALID.join(', ')})`);
38
+ }
39
+ }
40
+ return [...new Set(targets)];
41
+ }
42
+
43
+
44
+
45
+
46
+
47
+
48
+ /**
49
+ * Map tool -> install root.
50
+ * global=true -> $HOME/.{claude,codex,gemini,copilot,kiro,pi}, $HOME/.config/opencode
51
+ * (sync.sh parity for claude/codex/gemini; the four new harnesses follow
52
+ * each one's own documented global root — see below)
53
+ * global=false (dflt) -> <resolved projectRoot>/.{claude,codex,kiro,opencode,pi}, but .agents for
54
+ * gemini and .github for copilot — both deliberately asymmetric with their own global-scope
55
+ * directory, for reasons native to each harness (see per-key notes below).
56
+ *
57
+ * gemini -> project-scoped Gemini config follows the Antigravity customization convention, not
58
+ * `.gemini/`, so it is asymmetric with its own global directory and with the other
59
+ * targets.
60
+ * copilot -> Copilot has no single native root the way Claude's `.claude` is one: instructions,
61
+ * skills, agents, and MCP each live under a different path (see design.md §4's
62
+ * native-surfaces table). `.github` is the project root DoFlow treats as Copilot's
63
+ * "home" here because that is where `copilot-instructions.md` and `.github/agents`
64
+ * live; `~/.copilot` is the equivalent global root.
65
+ * kiro -> `.kiro` / `~/.kiro` is the one native root that actually holds everything Kiro
66
+ * reads (steering, agents, hooks, settings/mcp.json), so — unlike copilot — this is
67
+ * the clean, symmetric case.
68
+ * opencode -> Global config is `~/.config/opencode`, NOT `~/.opencode/` — a path DoFlow's own
69
+ * docs asserted incorrectly for several releases (see src/adapters/opencode/index.js).
70
+ * Project scope is `.opencode`, asymmetric with the global path the same way gemini's
71
+ * is.
72
+ * pi -> Global config is `~/.pi/agent` — note the `agent/` segment; `~/.pi/settings.json`
73
+ * is not a location Pi reads. Project scope is `.pi` (see
74
+ * src/adapters/pi/index.js's own `nativePaths()`, which this mirrors exactly).
75
+ */
76
+ function toolDirs(opts = {}) {
77
+ const { global = false, projectRoot = '.' } = opts;
78
+ const root = global ? os.homedir() : path.resolve(projectRoot);
79
+ return {
80
+ claude: path.join(root, '.claude'),
81
+ codex: path.join(root, '.codex'),
82
+ gemini: global ? path.join(root, '.gemini') : path.join(root, '.agents'),
83
+ copilot: global ? path.join(root, '.copilot') : path.join(root, '.github'),
84
+ kiro: path.join(root, '.kiro'),
85
+ opencode: global ? path.join(root, '.config', 'opencode') : path.join(root, '.opencode'),
86
+ pi: global ? path.join(root, '.pi', 'agent') : path.join(root, '.pi'),
87
+ };
88
+ }
89
+
90
+ module.exports = { VALID, DEFAULT_TARGETS, resolveTargets, toolDirs };