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

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 (407) 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 +1057 -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 +46 -4
  250. package/src/adapters/antigravity/index.ts +631 -0
  251. package/src/adapters/claude/index.ts +548 -0
  252. package/src/adapters/claude/mcp.ts +94 -0
  253. package/src/adapters/codex/agents.ts +208 -0
  254. package/src/adapters/codex/config.ts +272 -0
  255. package/src/adapters/codex/hooks.ts +203 -0
  256. package/src/adapters/codex/index.ts +707 -0
  257. package/src/adapters/codex/mcp.ts +271 -0
  258. package/src/adapters/codex/native-projection.ts +34 -0
  259. package/src/adapters/copilot/index.ts +413 -0
  260. package/src/adapters/copy-tree.ts +620 -0
  261. package/src/adapters/gemini/hooks.ts +210 -0
  262. package/src/adapters/gemini/index.ts +390 -0
  263. package/src/adapters/hook-commands.ts +60 -0
  264. package/src/adapters/index.ts +106 -0
  265. package/src/adapters/kiro/index.ts +310 -0
  266. package/src/adapters/mcp-entries.ts +331 -0
  267. package/src/adapters/opencode/index.ts +466 -0
  268. package/src/adapters/pi/index.ts +279 -0
  269. package/src/adapters/pi/json-members.ts +185 -0
  270. package/src/adapters/pi/mcp.ts +373 -0
  271. package/src/adapters/resolve-source.ts +19 -0
  272. package/src/cli/commands/install.ts +218 -0
  273. package/src/cli/commands/list-backups.ts +21 -0
  274. package/src/cli/commands/reconcile.ts +120 -0
  275. package/src/cli/commands/remove.ts +82 -0
  276. package/src/cli/commands/rollback.ts +172 -0
  277. package/src/cli/commands/self-update.ts +33 -0
  278. package/src/cli/commands/status.ts +138 -0
  279. package/src/cli/commands/tools.ts +92 -0
  280. package/src/cli/commands/update.ts +162 -0
  281. package/src/cli/index.ts +568 -0
  282. package/src/cli/runtime-commands.ts +281 -0
  283. package/src/cli/shared.ts +218 -0
  284. package/src/helper/bash-helper.ts +34 -0
  285. package/src/helper/git.ts +24 -0
  286. package/src/helper/guardrails.ts +49 -0
  287. package/src/helper/harness-paths.ts +140 -0
  288. package/src/helper/marker-merge.ts +116 -0
  289. package/src/helper/prompt.ts +61 -0
  290. package/src/helper/repo-root.ts +29 -0
  291. package/src/helper/settings-merge.ts +151 -0
  292. package/src/helper/settings-scope.ts +33 -0
  293. package/src/helper/toml.ts +244 -0
  294. package/src/install/backup-set.ts +149 -0
  295. package/src/install/backup.ts +724 -0
  296. package/src/install/context.ts +58 -0
  297. package/src/install/manifest.ts +145 -0
  298. package/src/install/mcp.ts +254 -0
  299. package/src/install/paths.ts +84 -0
  300. package/src/install/targets.ts +90 -0
  301. package/src/install/tool-lifecycle.ts +151 -0
  302. package/src/lifecycle/bash-availability.ts +25 -0
  303. package/src/lifecycle/index.ts +812 -0
  304. package/src/lifecycle/mcp-index.ts +28 -0
  305. package/src/lifecycle/policies.ts +73 -0
  306. package/src/lifecycle/view.ts +175 -0
  307. package/src/registry/index.ts +611 -0
  308. package/src/release/sync-plugin-versions.ts +66 -0
  309. package/src/runtime/advice/cli.ts +283 -0
  310. package/src/runtime/advice/client.ts +324 -0
  311. package/src/runtime/advice/config.ts +189 -0
  312. package/src/runtime/advice/doctor.ts +169 -0
  313. package/src/runtime/advice/endpoint-guard.ts +134 -0
  314. package/src/runtime/advice/kinds.ts +154 -0
  315. package/src/runtime/advice/providers/ollama-systemone.ts +209 -0
  316. package/src/runtime/advice/record.ts +166 -0
  317. package/src/runtime/advice/registry.ts +170 -0
  318. package/src/runtime/advice/strength.ts +66 -0
  319. package/src/runtime/brief-tools.ts +342 -0
  320. package/src/runtime/c4-project.ts +371 -0
  321. package/src/runtime/capability-router.ts +484 -0
  322. package/src/runtime/checkouts.ts +170 -0
  323. package/src/runtime/claims.ts +468 -0
  324. package/src/runtime/cli-result.ts +55 -0
  325. package/src/runtime/cli.ts +678 -0
  326. package/src/runtime/command-detect.ts +516 -0
  327. package/src/runtime/context-pack.ts +492 -0
  328. package/src/runtime/decision-register.ts +493 -0
  329. package/src/runtime/evidence-ledger.ts +338 -0
  330. package/src/runtime/failure/capture.ts +272 -0
  331. package/src/runtime/failure/classifier.ts +68 -0
  332. package/src/runtime/failure/cli.ts +178 -0
  333. package/src/runtime/failure/home.ts +64 -0
  334. package/src/runtime/failure/store.ts +226 -0
  335. package/src/runtime/feature-resolve.ts +79 -0
  336. package/src/runtime/freshness.ts +177 -0
  337. package/src/runtime/health.ts +863 -0
  338. package/src/runtime/history-compactor.ts +237 -0
  339. package/src/runtime/implementation-gate.ts +321 -0
  340. package/src/runtime/inventory/coverage.ts +218 -0
  341. package/src/runtime/inventory/identity.ts +65 -0
  342. package/src/runtime/inventory/index.ts +705 -0
  343. package/src/runtime/inventory/precedence.ts +134 -0
  344. package/src/runtime/inventory/read-scopes.ts +131 -0
  345. package/src/runtime/inventory/siblings.ts +192 -0
  346. package/src/runtime/knowledge/chunker.ts +124 -0
  347. package/src/runtime/knowledge/hybrid.ts +149 -0
  348. package/src/runtime/knowledge/index-store.ts +145 -0
  349. package/src/runtime/knowledge/retrieval.ts +194 -0
  350. package/src/runtime/leak-scan.ts +185 -0
  351. package/src/runtime/lifecycle/cli.ts +276 -0
  352. package/src/runtime/lifecycle/event-store.ts +340 -0
  353. package/src/runtime/lifecycle/fold.ts +353 -0
  354. package/src/runtime/lifecycle/followup.ts +432 -0
  355. package/src/runtime/lifecycle/goal.ts +249 -0
  356. package/src/runtime/lifecycle/intent-writer.ts +86 -0
  357. package/src/runtime/lifecycle/overview.ts +292 -0
  358. package/src/runtime/lifecycle/release.ts +241 -0
  359. package/src/runtime/lifecycle/report-store.ts +221 -0
  360. package/src/runtime/lifecycle/retention.ts +212 -0
  361. package/src/runtime/lifecycle/root.ts +71 -0
  362. package/src/runtime/lifecycle/status.ts +353 -0
  363. package/src/runtime/lifecycle/store-upkeep.ts +393 -0
  364. package/src/runtime/locator-resolve.ts +158 -0
  365. package/src/runtime/mask.ts +210 -0
  366. package/src/runtime/mcp-consultations.ts +412 -0
  367. package/src/runtime/mcp-reach.ts +157 -0
  368. package/src/runtime/model-router.ts +153 -0
  369. package/src/runtime/outcome.ts +467 -0
  370. package/src/runtime/reach.ts +148 -0
  371. package/src/runtime/readiness-record.ts +150 -0
  372. package/src/runtime/readiness.ts +435 -0
  373. package/src/runtime/recovery.ts +282 -0
  374. package/src/runtime/research-request.ts +231 -0
  375. package/src/runtime/retrieval-plan.ts +578 -0
  376. package/src/runtime/scaffold/artifacts.ts +217 -0
  377. package/src/runtime/scaffold/fingerprint.ts +56 -0
  378. package/src/runtime/scaffold/generate.ts +911 -0
  379. package/src/runtime/scaffold/languages.ts +245 -0
  380. package/src/runtime/storage/cli.ts +64 -0
  381. package/src/runtime/storage/engine.ts +249 -0
  382. package/src/runtime/storage/registry.ts +130 -0
  383. package/src/runtime/storage/roots.ts +63 -0
  384. package/src/runtime/storage/rules.ts +282 -0
  385. package/src/runtime/storage/upkeep.ts +243 -0
  386. package/src/runtime/storage/work-status.ts +326 -0
  387. package/src/runtime/task-attempts.ts +415 -0
  388. package/src/runtime/task-classifier.ts +550 -0
  389. package/src/runtime/task-scope.ts +130 -0
  390. package/src/runtime/task-state.ts +176 -0
  391. package/src/runtime/trace/indicators.ts +235 -0
  392. package/src/runtime/trace/ledger.ts +626 -0
  393. package/src/runtime/trace/render.ts +76 -0
  394. package/src/runtime/trace/views.ts +459 -0
  395. package/src/runtime/verification/contract-runner.ts +341 -0
  396. package/src/runtime/verification/engine.ts +1461 -0
  397. package/src/runtime/verification/registry.ts +130 -0
  398. package/src/runtime/verification/scope-bound.ts +418 -0
  399. package/src/runtime/verification/test-selection.ts +172 -0
  400. package/src/runtime/workflow-engine.ts +588 -0
  401. package/src/runtime/workflow-orchestrator.ts +953 -0
  402. package/src/runtime/worktree.ts +340 -0
  403. package/src/state/index.ts +193 -0
  404. package/src/state/lockfile.ts +136 -0
  405. package/src/state/run-lock.ts +408 -0
  406. package/types/node-commonjs.d.ts +68 -0
  407. package/types/runtime.d.ts +91 -0
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env bash
2
+ # do-parallel-check.sh — verify one phase's [P] siblings really write disjoint files.
3
+ # Form dispatch groups by (phase, owner) and detect union overlaps.
4
+ #
5
+ # `[P]` asserts at plan time that a task's `files:` set does not overlap its phase
6
+ # siblings'. Nothing verified that assertion, so parallel-by-default rested on an
7
+ # unchecked claim: two agents dispatched concurrently onto one file is a lost edit,
8
+ # and the loss is silent. This script turns the claim into a check.
9
+ #
10
+ # Grouping by (phase, owner) lets one agent handle all tasks of the same owner in a phase,
11
+ # reducing dispatch overhead. The script forms groups from `owner:` fields, computes union
12
+ # file sets per group, and reports overlaps across groups (`group_overlaps[]`).
13
+ #
14
+ # Advisory by design: it reports overlaps and grouping decisions; the calling skill decides
15
+ # to serialize or group dispatch. It never edits the plan and never dispatches anything.
16
+ #
17
+ # A task whose `files:` value names no real path (a verification-only task) simply
18
+ # contributes nothing to compare, rather than being treated as conflicting with
19
+ # everything.
20
+ #
21
+ # Usage:
22
+ # do-parallel-check.sh --phase=<X> [--slug=<slug>] [--json]
23
+
24
+ set -uo pipefail
25
+
26
+ phase=""
27
+ slug_override=""
28
+ for arg in "$@"; do
29
+ case "$arg" in
30
+ --phase=*) phase="${arg#--phase=}" ;;
31
+ --slug=*) slug_override="${arg#--slug=}" ;;
32
+ --json) ;;
33
+ *) ;;
34
+ esac
35
+ done
36
+
37
+ if ! command -v jq >/dev/null 2>&1; then
38
+ printf '{"error":"jq-not-found","parallel_safe":null}\n'
39
+ exit 0
40
+ fi
41
+
42
+ if [ -z "$phase" ]; then
43
+ jq -n '{error:"missing-phase", hint:"pass --phase=<X>, e.g. --phase=A"}'
44
+ exit 2
45
+ fi
46
+
47
+ script_dir="$(cd "$(dirname "$0")" && pwd)"
48
+ resolved="$(bash "$script_dir/do-paths.sh" --json ${slug_override:+--slug="$slug_override"} 2>/dev/null)"
49
+ resolved_rc=$?
50
+ # An unsafe slug is refused with the resolver's own error object (IC-009), not read as an empty feature.
51
+ if [ "$resolved_rc" -eq 2 ] && [ "$(printf '%s' "$resolved" | jq -r '.error // empty' 2>/dev/null)" = "invalid-slug" ]; then
52
+ printf '%s\n' "$resolved"; exit 2
53
+ fi
54
+ repo_root="$(printf '%s' "$resolved" | jq -r '.repo_root // empty')"
55
+ plan_rel="$(printf '%s' "$resolved" | jq -r '.plan // empty')"
56
+ has_plan="$(printf '%s' "$resolved" | jq -r '.has_plan // false')"
57
+
58
+ if [ "$has_plan" != "true" ] || [ -z "$plan_rel" ]; then
59
+ jq -n '{error:"no-plan", hint:"this feature has no plan.md to check"}'
60
+ exit 2
61
+ fi
62
+ plan_abs="$repo_root/$plan_rel"
63
+
64
+ # Pipeline: awk outputs tab-separated values (one per line), jq reads raw lines as strings,
65
+ # aggregates them into an array, then processes each row.
66
+ awk -v want="$phase" '
67
+ /^### Phase / {
68
+ inphase = ($3 == want)
69
+ next
70
+ }
71
+ /^### / { inphase = 0; next }
72
+ !inphase { next }
73
+ /^- \[[ x]\] / {
74
+ line = $0
75
+ if (match(line, /^- \[[ x]\] [^ ]+/) == 0) next
76
+ id = substr(line, RSTART + 6, RLENGTH - 6)
77
+ par = (line ~ /^- \[[ x]\] [^ ]+ \[P\]/) ? "P" : "S"
78
+ # Extract only the portion after em-dash for owner:/files: parsing
79
+ dash_pos = index(line, "—")
80
+ if (dash_pos == 0) {
81
+ print id "\t" par "\t\t"
82
+ next
83
+ }
84
+ field_section = substr(line, dash_pos + length("—"))
85
+ owner = ""
86
+ if (match(field_section, /owner:[^;]*/)) {
87
+ owner = substr(field_section, RSTART + 7, RLENGTH - 7)
88
+ gsub(/^[ \t]+|[ \t]+$/, "", owner)
89
+ }
90
+ if (match(field_section, /files:[^;]*/)) {
91
+ files = substr(field_section, RSTART + 6, RLENGTH - 6)
92
+ n = split(files, parts, ",")
93
+ emitted = 0
94
+ for (i = 1; i <= n; i++) {
95
+ f = parts[i]
96
+ gsub(/^[ \t]+|[ \t]+$/, "", f)
97
+ if (f == "" || f ~ /^none/) continue
98
+ print id "\t" par "\t" owner "\t" f
99
+ emitted = 1
100
+ }
101
+ if (!emitted) print id "\t" par "\t" owner "\t"
102
+ } else {
103
+ print id "\t" par "\t" owner "\t"
104
+ }
105
+ }
106
+ ' "$plan_abs" | jq -R . | jq -s --arg phase "$phase" '
107
+ # Convert array of tab-separated strings to array of objects
108
+ map(split("\t")) |
109
+ map({id: .[0], par: .[1], owner: (.[2] // ""), file: (.[3] // "")}) |
110
+ . as $rows |
111
+
112
+ # Separate by parallel/sequential
113
+ ([.[] | select(.par == "P") | .id] | unique) as $par |
114
+ ([.[] | select(.par == "S") | .id] | unique) as $seq |
115
+
116
+ # Existing overlaps: per-task file conflicts among [P] tasks
117
+ (
118
+ [.[] | select(.par == "P") | select(.file != "")] |
119
+ group_by(.file) |
120
+ map({path: .[0].file, tasks: ([.[] | .id] | unique)}) |
121
+ map(select(.tasks | length > 1))
122
+ ) as $overlaps |
123
+
124
+ # Unowned tasks: those with empty owner
125
+ ([.[] | select(.owner == "") | .id] | unique) as $unowned_ids |
126
+
127
+ # Form groups preserving first task appearance order
128
+ (
129
+ reduce $rows[] as $row (
130
+ [];
131
+ if $row.id == "" then .
132
+ elif $row.owner == "" then
133
+ # Check if unowned task already added
134
+ if any(.[]; .id == ($phase + ":" + $row.id)) then
135
+ map(if .id == ($phase + ":" + $row.id) then
136
+ .files = (.files + (if $row.file == "" then [] else [$row.file] end) | unique)
137
+ else . end)
138
+ else
139
+ . + [{
140
+ id: ($phase + ":" + $row.id),
141
+ owner: null,
142
+ tasks: [$row.id],
143
+ files: (if $row.file == "" then [] else [$row.file] end)
144
+ }]
145
+ end
146
+ else
147
+ ($row.owner) as $own |
148
+ ($phase + ":" + $own) as $gid |
149
+ if any(.[]; .id == $gid) then
150
+ map(if .id == $gid then
151
+ .tasks = (if (.tasks | index($row.id)) then .tasks else (.tasks + [$row.id]) end) |
152
+ .files = (.files + (if $row.file == "" then [] else [$row.file] end) | unique)
153
+ else . end)
154
+ else
155
+ . + [{
156
+ id: $gid,
157
+ owner: $own,
158
+ tasks: [$row.id],
159
+ files: (if $row.file == "" then [] else [$row.file] end)
160
+ }]
161
+ end
162
+ end
163
+ )
164
+ ) as $final_groups |
165
+
166
+ # Create a mapping from task_id to group_id for lookup
167
+ ([$final_groups[] | .tasks[] as $t | {key: $t, value: .id}] | from_entries) as $task_to_group |
168
+
169
+ # Group overlaps: paths appearing in tasks assigned to more than one group
170
+ (
171
+ $rows |
172
+ [.[] | select(.par != "") | select(.file != "")] |
173
+ group_by(.file) |
174
+ map({
175
+ path: .[0].file,
176
+ groups: ([.[] | .id as $tid | $task_to_group[$tid]] | unique)
177
+ }) |
178
+ map(select((.groups | length) > 1))
179
+ ) as $final_group_overlaps |
180
+
181
+ # Serialize list = union of group ids from overlaps
182
+ ([$final_group_overlaps[].groups[]] | unique) as $group_serialize |
183
+
184
+ {
185
+ phase: $phase,
186
+ parallel_tasks: $par,
187
+ sequential_tasks: $seq,
188
+ overlaps: $overlaps,
189
+ # `null` when the caller named a phase and nothing parsed, true only when BOTH the task-level
190
+ # and the group-level collision sets are empty.
191
+ #
192
+ # This field was `(($overlaps|length) == 0)`, which was wrong twice. A plan whose tasks are not
193
+ # under a `### Phase <X>` heading parses to zero tasks, so zero overlaps, so "safe" — the flag
194
+ # said a collision-free phase where it had read no phase at all. And `$overlaps` counts only
195
+ # `[P]`-to-`[P]` collisions, so a plan could report `parallel_safe: true` in the same object as
196
+ # a non-empty `group_overlaps` naming the very file two groups both write. `do-execute-plan`
197
+ # tells the reader to branch on this field and never on the exit code, so either shape
198
+ # authorises concurrent writes to one file.
199
+ parallel_safe: (if (($par | length) == 0 and ($seq | length) == 0) then null
200
+ else (($overlaps | length) == 0 and ($final_group_overlaps | length) == 0) end),
201
+ serialize: ([$overlaps[] | .tasks[]] | unique),
202
+ groups: $final_groups,
203
+ group_overlaps: $final_group_overlaps,
204
+ group_serialize: $group_serialize,
205
+ unowned_tasks: $unowned_ids
206
+ }
207
+ '
208
+ exit 0
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env bash
2
+ # do-paths.sh — deterministic path/feature resolver for the doflow chain.
3
+ #
4
+ # Emits a single JSON object describing the ACTIVE feature so doflow skills
5
+ # never compute paths, feature numbers, or existence themselves (the
6
+ # deterministic/generative split — design §4). In a git repo, the active
7
+ # feature is derived from the branch (branch-coupled, FR-6): `feat/<NNN-slug>`
8
+ # → feature dir `agent-docs/doflow/<NNN-slug>/`. On a trunk branch
9
+ # (master/main/develop) the slug is null and the caller must prompt for one.
10
+ # Outside a git repo (e.g. doflow installed at a multi-service container root,
11
+ # above the actual git sub-repos) there is no branch to key off at all, so
12
+ # resolution falls back to scanning `agent-docs/doflow/` directly — see the
13
+ # "feature slug" section below and `candidate_slugs` in the emitted JSON.
14
+ #
15
+ # Self-contained by design: it does NOT source hooks/lib.sh, because this
16
+ # script and lib.sh sit at different relative depths depending on install
17
+ # location (global vs project-scoped). It needs only `git` + `jq`.
18
+ #
19
+ # Fail-open: on any error it still emits best-effort JSON and exits 0, so a
20
+ # skill is never hard-blocked by path resolution. The one non-zero exit is the
21
+ # explicit `--require feature` gate (exit 2 when no active feature).
22
+ #
23
+ # Usage:
24
+ # do-paths.sh [--json] # default — full resolver JSON
25
+ # do-paths.sh --paths-only # skip the next_number scan (cheap)
26
+ # do-paths.sh --require feature # exit 2 + error JSON if no active feature
27
+ # do-paths.sh --slug=<slug> # force resolution to this slug, skipping branch/dir detection
28
+ # # (used by a skill after it has already disambiguated via
29
+ # # AskUserQuestion — see "non-git root" note below)
30
+
31
+ set -uo pipefail # deliberately NOT -e: we handle errors and still emit JSON
32
+
33
+ mode="json"
34
+ require=""
35
+ slug_override=""
36
+ prev=""
37
+ for arg in "$@"; do
38
+ case "$arg" in
39
+ --json) mode="json" ;;
40
+ --paths-only) mode="paths-only" ;;
41
+ --require) require="pending" ;; # value follows in next arg
42
+ --require=*) require="${arg#--require=}" ;;
43
+ --slug=*) slug_override="${arg#--slug=}" ;;
44
+ feature) [ "$prev" = "--require" ] && require="feature" ;;
45
+ *) ;;
46
+ esac
47
+ prev="$arg"
48
+ done
49
+ [ "$require" = "pending" ] && require="feature"
50
+
51
+ # jq is mandatory to emit JSON; fail-open with a diagnostic object if absent.
52
+ if ! command -v jq >/dev/null 2>&1; then
53
+ printf '{"error":"jq-not-found","feature_slug":null}\n'
54
+ exit 0
55
+ fi
56
+
57
+ # ── repo root & branch (degrade gracefully outside a git repo) ────────────────
58
+ is_git_repo=false
59
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null)"
60
+ if [ -n "$repo_root" ]; then
61
+ is_git_repo=true
62
+ else
63
+ repo_root="$(pwd)"
64
+ fi
65
+ branch=""
66
+ [ "$is_git_repo" = true ] && branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")"
67
+
68
+ specs_rel="agent-docs/doflow"
69
+ specs_dir="$repo_root/$specs_rel"
70
+
71
+ # ── numeric-prefixed feature dirs under agent-docs/doflow/ (shared by the non-git candidate-slug
72
+ # fallback below and the next_number scan further down — one scan, not two; applies the same
73
+ # numeric-prefix filter next_number always used, so a stray non-numeric dir — notes/, .archive/,
74
+ # a manual-cleanup leftover — can never masquerade as a feature candidate) ────────────────────
75
+ numbered_dirs=()
76
+ if { [ "$is_git_repo" = false ] || [ "$mode" != "paths-only" ]; } && [ -d "$specs_dir" ]; then
77
+ for d in "$specs_dir"/*/; do
78
+ [ -d "$d" ] || continue
79
+ n="$(basename "$d")"
80
+ case "${n%%-*}" in ''|*[!0-9]*) continue ;; esac
81
+ numbered_dirs+=("$n")
82
+ done
83
+ fi
84
+
85
+ # ── feature slug: branch-derived in a git repo; directory-scan otherwise ──────
86
+ # A non-git root (doflow installed above the actual git repos — e.g. a multi-service
87
+ # container workspace) has no branch to key off at all, so branch parsing doesn't apply here —
88
+ # fall back to scanning agent-docs/doflow/ directly (numbered_dirs above). Exactly one candidate
89
+ # dir is unambiguous and gets auto-selected (no prompt needed — nothing to decide). Zero
90
+ # candidates is genuinely "no active feature." Two or more is a real decision this deterministic
91
+ # script cannot make on its own; it surfaces every candidate via `candidate_slugs` instead of
92
+ # guessing, so the calling skill (do-execute-plan / do-flow, which has AskUserQuestion access)
93
+ # can ask the user, then re-invoke with `--slug=<chosen>` to force resolution.
94
+ feature_slug=""
95
+ candidate_slugs_json="[]"
96
+ # Initialized here, not only inside the git arm below: a non-git root never runs the branch
97
+ # `case`, and the emit block reads this unconditionally under `set -u`. Leaving it unset made
98
+ # the resolver die with "branch_class: unbound variable" at exactly the container-root layout
99
+ # the directory-scan fallback exists to support.
100
+ branch_class=""
101
+ if [ "$is_git_repo" = true ]; then
102
+ case "$branch" in
103
+ "") feature_slug=""; branch_class="trunk" ;;
104
+ master|main|develop|HEAD) feature_slug=""; branch_class="trunk" ;;
105
+ release/*) feature_slug=""; branch_class="release" ;;
106
+ hotfix/*) feature_slug=""; branch_class="hotfix" ;;
107
+ feat/*|feature/*) feature_slug="${branch#*/}"; branch_class="feature" ;;
108
+ fix/*|bugfix/*) feature_slug="${branch#*/}"; branch_class="fix" ;;
109
+ */*) feature_slug="${branch#*/}"; branch_class="other" ;;
110
+ *) feature_slug="$branch"; branch_class="other" ;;
111
+ esac
112
+ # Flatten any separator the strip above left behind. Two reasons this is not cosmetic:
113
+ # `feature_dir` is `agent-docs/doflow/<slug>`, and a slug with a `/` nests it a level deeper than
114
+ # every other feature — deep enough that the non-git directory scan above can never find it,
115
+ # since that scan globs one level and then drops names without a numeric prefix. And
116
+ # `assertSafeTaskId` in src/runtime/evidence-ledger.js rejects path separators outright, so
117
+ # `doflow evidence` and `doflow claim` throw for the whole session.
118
+ #
119
+ # The arm above used to read `*/)`, which matches only a name ending in `/` — something git
120
+ # refuses to create — so it never once fired, and every prefix outside the six named here fell
121
+ # to `*)` and kept it. `feat/`, `feature/`, `fix/` and `bugfix/` were unaffected because they
122
+ # have their own arms; `task/`, `chore/`, `refactor/` and the rest were not.
123
+ feature_slug="$(printf '%s' "$feature_slug" | tr '/' '-')"
124
+ else
125
+ case "${#numbered_dirs[@]}" in
126
+ 0) : ;;
127
+ 1) feature_slug="${numbered_dirs[0]}" ;;
128
+ *) candidate_slugs_json="$(printf '%s\n' "${numbered_dirs[@]}" | jq -R . | jq -s .)" ;;
129
+ esac
130
+ fi
131
+
132
+ # An explicit --slug always wins (post-disambiguation re-resolution, or a deliberate override).
133
+ if [ -n "$slug_override" ]; then
134
+ feature_slug="$slug_override"
135
+ candidate_slugs_json="[]"
136
+ fi
137
+
138
+ # The slug becomes a directory name under agent-docs/doflow/ and a key in per-task state, so it must
139
+ # not be able to name a path or a flag (IC-009): letters, digits, dot, underscore and dash, a letter
140
+ # or digit first, and no "..". Refused before any path is built, whether it came from --slug or was
141
+ # derived from the branch. Exit 2 like the other refusals; the message is also in `hint`, which is
142
+ # the field every caller of the other error objects already reads.
143
+ if [ -n "$feature_slug" ]; then
144
+ slug_ok=true
145
+ # The ranges are ASCII whatever the caller's locale: under a UTF-8 collation [A-Za-z] can admit
146
+ # letters such as "é", which the rule exists to refuse.
147
+ (export LC_ALL=C; [[ "$feature_slug" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] && [[ "$feature_slug" != *..* ]]) || slug_ok=false
148
+ if [ "$slug_ok" = false ]; then
149
+ rule='use letters, digits, dot, underscore or dash, start with a letter or digit, and no ".."'
150
+ # A slug the caller did not type is traced to where it came from, so a branch name is not blamed
151
+ # on a flag nobody passed.
152
+ if [ -z "$slug_override" ] && [ "$is_git_repo" = true ] && [ -n "$branch" ]; then
153
+ subject="branch '$branch' gives slug '$feature_slug', which is not a valid feature slug"
154
+ else
155
+ subject="slug \"$feature_slug\" is not a valid feature slug"
156
+ fi
157
+ jq -n --arg subject "$subject" --arg rule "$rule" \
158
+ '{error:"invalid-slug", message:($subject + ": " + $rule), hint:$rule}'
159
+ exit 2
160
+ fi
161
+ fi
162
+
163
+ feature_dir=""; requirement=""; design=""; specs=""; data_model=""; plan=""; state=""; audit=""
164
+ decisions=""; decisions_register=""; has_decisions=false
165
+ has_requirement=false; has_design=false; has_plan=false; has_specs=false; has_data_model=false
166
+ layout="legacy"
167
+ intention_next_round=""; design_next_round=""; plan_next_round=""
168
+ if [ -n "$feature_slug" ]; then
169
+ feature_dir="$specs_rel/$feature_slug"
170
+ abs="$repo_root/$feature_dir"
171
+ state="$feature_dir/state.md"; audit="$feature_dir/audit.md"
172
+ decisions="$feature_dir/decisions.md"; decisions_register="$feature_dir/decisions/register.json"
173
+ [ -f "$abs/decisions/register.json" ] && has_decisions=true
174
+ # ── layout: computed ONCE from a single file's presence, then used to pick
175
+ # every path field below — never detected per-field (design.md R4). This is
176
+ # what makes a self-contradictory mix of old/new paths for the same feature
177
+ # dir unreachable by construction.
178
+ [ -f "$abs/intention/requirement.md" ] && layout="structured"
179
+ if [ "$layout" = "structured" ]; then
180
+ requirement="$feature_dir/intention/requirement.md"
181
+ design="$feature_dir/design/design.md"
182
+ specs="$feature_dir/design/specs.md"
183
+ data_model="$feature_dir/design/data-model.md"
184
+ plan="$feature_dir/plan.md"
185
+ [ -f "$abs/intention/requirement.md" ] && has_requirement=true
186
+ [ -f "$abs/design/design.md" ] && has_design=true
187
+ [ -f "$abs/design/specs.md" ] && has_specs=true
188
+ [ -f "$abs/design/data-model.md" ] && has_data_model=true
189
+ [ -f "$abs/plan.md" ] && has_plan=true
190
+ else
191
+ requirement="$feature_dir/requirement.md"; design="$feature_dir/design.md"
192
+ plan="$feature_dir/plan.md"
193
+ [ -f "$abs/requirement.md" ] && has_requirement=true
194
+ [ -f "$abs/design.md" ] && has_design=true
195
+ [ -f "$abs/plan.md" ] && has_plan=true
196
+ fi
197
+
198
+ # ── per-stage question-log round scan (structured layout only has files to find,
199
+ # but the scan itself is layout-agnostic — an empty/absent subdir just floors at 1).
200
+ # Skipped in --paths-only mode like the next_number scan below; the three variables
201
+ # stay "" (emitted as null, never a numeric 1) so "not computed in this cheap mode"
202
+ # can never be mistaken for "computed, no rounds yet" ──
203
+ if [ "$mode" != "paths-only" ]; then
204
+ max=0
205
+ if [ -d "$abs/intention" ]; then
206
+ for f in "$abs/intention"/brainstorm-*-question.md; do
207
+ [ -f "$f" ] || continue
208
+ num="$(basename "$f")"; num="${num#brainstorm-}"; num="${num%-question.md}"
209
+ case "$num" in ''|*[!0-9]*) continue ;; esac
210
+ num=$((10#$num)); [ "$num" -gt "$max" ] && max="$num"
211
+ done
212
+ fi
213
+ intention_next_round=$((max + 1))
214
+
215
+ max=0
216
+ if [ -d "$abs/design" ]; then
217
+ for f in "$abs/design"/design-*-question.md; do
218
+ [ -f "$f" ] || continue
219
+ num="$(basename "$f")"; num="${num#design-}"; num="${num%-question.md}"
220
+ case "$num" in ''|*[!0-9]*) continue ;; esac
221
+ num=$((10#$num)); [ "$num" -gt "$max" ] && max="$num"
222
+ done
223
+ fi
224
+ design_next_round=$((max + 1))
225
+
226
+ max=0
227
+ if [ -d "$abs" ]; then
228
+ for f in "$abs"/plan-*-question.md; do
229
+ [ -f "$f" ] || continue
230
+ num="$(basename "$f")"; num="${num#plan-}"; num="${num%-question.md}"
231
+ case "$num" in ''|*[!0-9]*) continue ;; esac
232
+ num=$((10#$num)); [ "$num" -gt "$max" ] && max="$num"
233
+ done
234
+ fi
235
+ plan_next_round=$((max + 1))
236
+ fi
237
+ fi
238
+
239
+ # ── next number: max(existing spec dirs, numbered branches) + 1 ───────────────
240
+ # Base-10 forced (10#) so leading zeros never trigger octal parsing.
241
+ next_number="001"
242
+ if [ "$mode" != "paths-only" ]; then
243
+ max=0
244
+ if [ "${#numbered_dirs[@]}" -gt 0 ]; then
245
+ for n in "${numbered_dirs[@]}"; do
246
+ nn=$((10#${n%%-*})); [ "$nn" -gt "$max" ] && max="$nn"
247
+ done
248
+ fi
249
+ while IFS= read -r b; do
250
+ seg="${b##*/}"; num="${seg%%-*}"
251
+ case "$num" in ''|*[!0-9]*) continue ;; esac
252
+ num=$((10#$num)); [ "$num" -gt "$max" ] && max="$num"
253
+ done < <(git -C "$repo_root" branch --format='%(refname:short)' 2>/dev/null || true)
254
+ next_number="$(printf '%03d' "$((max + 1))")"
255
+ fi
256
+
257
+ # ── constitution paths (two-tier — design §6) ─────────────────────────────────
258
+ constitution_local="agent-docs/constitution.md" # tier-2, per-repo (may not exist)
259
+ script_dir="$(cd "$(dirname "$0")" && pwd)"
260
+ constitution_base=""
261
+ for c in \
262
+ "$script_dir/../../../guidance/references/CONSTITUTION_BASE.md" \
263
+ "$HOME/.doflow/guidance/references/CONSTITUTION_BASE.md"; do
264
+ if [ -f "$c" ]; then
265
+ constitution_base="$(cd "$(dirname "$c")" && pwd)/CONSTITUTION_BASE.md"
266
+ break
267
+ fi
268
+ done
269
+ # Existence of tier-2 is repo-scoped, not feature-scoped, so it is computed here rather than in the
270
+ # has_requirement/has_design/has_plan block above — a constitution is present or absent regardless
271
+ # of whether any feature is active. It also stays outside the --paths-only skip, like the two paths
272
+ # above, because callers using that cheap mode still need it.
273
+ has_constitution_local=false
274
+ [ -f "$repo_root/$constitution_local" ] && has_constitution_local=true
275
+
276
+ # ── --require feature gate (the one non-zero exit) ────────────────────────────
277
+ if [ "$require" = "feature" ] && [ -z "$feature_slug" ]; then
278
+ if [ "$candidate_slugs_json" != "[]" ]; then
279
+ jq -n --arg branch "$branch" --argjson candidate_slugs "$candidate_slugs_json" \
280
+ '{error:"ambiguous-feature",
281
+ branch:(if $branch=="" then null else $branch end),
282
+ candidate_slugs:$candidate_slugs,
283
+ hint:"multiple agent-docs/doflow/ feature dirs exist and no git branch disambiguates them — ask which one via AskUserQuestion, then re-run with --slug=<chosen>"}'
284
+ else
285
+ jq -n --arg branch "$branch" \
286
+ '{error:"no-active-feature",
287
+ branch:(if $branch=="" then null else $branch end),
288
+ hint:"checkout a feat/<NNN-slug> branch or run /do-brainstorm to start one"}'
289
+ fi
290
+ exit 2
291
+ fi
292
+
293
+ # ── emit resolver JSON ────────────────────────────────────────────────────────
294
+ jq -n \
295
+ --arg repo_root "$repo_root" \
296
+ --argjson is_git_repo "$is_git_repo" \
297
+ --arg branch "$branch" \
298
+ --arg feature_slug "$feature_slug" \
299
+ --arg feature_dir "$feature_dir" \
300
+ --argjson candidate_slugs "$candidate_slugs_json" \
301
+ --arg requirement "$requirement" --arg design "$design" --arg specs "$specs" --arg data_model "$data_model" --arg plan "$plan" \
302
+ --arg state "$state" --arg audit "$audit" \
303
+ --arg decisions "$decisions" --arg decisions_register "$decisions_register" --argjson has_decisions "$has_decisions" \
304
+ --argjson has_requirement "$has_requirement" --argjson has_design "$has_design" --argjson has_plan "$has_plan" \
305
+ --argjson has_specs "$has_specs" --argjson has_data_model "$has_data_model" \
306
+ --arg layout "$layout" \
307
+ --arg intention_next_round "$intention_next_round" \
308
+ --arg design_next_round "$design_next_round" \
309
+ --arg plan_next_round "$plan_next_round" \
310
+ --arg next_number "$next_number" \
311
+ --arg constitution_base "$constitution_base" \
312
+ --arg constitution_local "$constitution_local" \
313
+ --argjson has_constitution_local "$has_constitution_local" \
314
+ --arg branch_class "$branch_class" \
315
+ '{
316
+ repo_root: $repo_root,
317
+ is_git_repo: $is_git_repo,
318
+ branch: (if $branch=="" then null else $branch end),
319
+ feature_slug: (if $feature_slug=="" then null else $feature_slug end),
320
+ feature_dir: (if $feature_dir=="" then null else $feature_dir end),
321
+ candidate_slugs: $candidate_slugs,
322
+ layout: $layout,
323
+ requirement: (if $requirement=="" then null else $requirement end),
324
+ design: (if $design=="" then null else $design end),
325
+ specs: (if $specs=="" then null else $specs end),
326
+ data_model: (if $data_model=="" then null else $data_model end),
327
+ plan: (if $plan=="" then null else $plan end),
328
+ state: (if $state=="" then null else $state end),
329
+ audit: (if $audit=="" then null else $audit end),
330
+ decisions: (if $decisions=="" then null else $decisions end),
331
+ decisions_register: (if $decisions_register=="" then null else $decisions_register end),
332
+ has_decisions: $has_decisions,
333
+ has_requirement: $has_requirement,
334
+ has_design: $has_design,
335
+ has_specs: $has_specs,
336
+ has_data_model: $has_data_model,
337
+ has_plan: $has_plan,
338
+ intention_next_round: (if $intention_next_round=="" then null else ($intention_next_round|tonumber) end),
339
+ design_next_round: (if $design_next_round=="" then null else ($design_next_round|tonumber) end),
340
+ plan_next_round: (if $plan_next_round=="" then null else ($plan_next_round|tonumber) end),
341
+ next_number: $next_number,
342
+ constitution_base: (if $constitution_base=="" then null else $constitution_base end),
343
+ constitution_local: $constitution_local,
344
+ has_constitution_local: $has_constitution_local,
345
+ branch_class: (if $branch_class=="" then null else $branch_class end)
346
+ }'
347
+ exit 0
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env bash
2
+ # do-prereqs.sh — doflow implement-phase prerequisite gate (prompt-level PRIMARY).
3
+ #
4
+ # Called as step 1 of /do-execute-plan. With --require-plan it exits 2 (and prints an
5
+ # error JSON) when the active feature lacks requirement.md, design.md, or plan.md — the
6
+ # "no implementing before you've planned" rule. Self-contained; fail-open for everything
7
+ # EXCEPT the explicit gate failure (the one intentional non-zero exit).
8
+ #
9
+ # Usage: do-prereqs.sh --require-plan [--slug=<slug>]
10
+ # exit 0 = ok, exit 2 = missing requirement/design/plan (or ambiguous/no active feature)
11
+ # --slug=<slug> forwards to do-paths.sh — pass this after a caller has already disambiguated
12
+ # an "ambiguous-feature" result (non-git root, 2+ candidates) via AskUserQuestion.
13
+
14
+ set -uo pipefail
15
+
16
+ require_plan=false
17
+ slug_override=""
18
+ for a in "$@"; do
19
+ case "$a" in
20
+ --require-plan) require_plan=true ;;
21
+ --slug=*) slug_override="${a#--slug=}" ;;
22
+ esac
23
+ done
24
+
25
+ command -v jq >/dev/null 2>&1 || { echo '{"ok":true,"note":"jq-absent-skip-gate"}'; exit 0; }
26
+
27
+ script_dir="$(cd "$(dirname "$0")" && pwd)"
28
+ RESOLVER="$script_dir/do-paths.sh"
29
+ [ -f "$RESOLVER" ] || RESOLVER="${DOFLOW_CONFIG_DIR:+$DOFLOW_CONFIG_DIR/scripts/doflow/bash/do-paths.sh}"
30
+ [ -f "$RESOLVER" ] || { echo '{"ok":true,"note":"resolver-absent-skip-gate"}'; exit 0; }
31
+
32
+ RESOLVER_ARGS=(--json)
33
+ [ -n "$slug_override" ] && RESOLVER_ARGS+=("--slug=$slug_override")
34
+ rc=0; json=$("$RESOLVER" "${RESOLVER_ARGS[@]}" 2>/dev/null) || rc=$?
35
+ # A slug that could name a path is the caller's mistake, not an unrunnable resolver: the gate refuses
36
+ # it with the resolver's own error rather than skipping itself (IC-009).
37
+ if [ "$rc" -eq 2 ] && [ "$(printf '%s' "$json" | jq -r '.error // empty' 2>/dev/null)" = "invalid-slug" ]; then
38
+ printf '%s\n' "$json"; exit 2
39
+ fi
40
+ [ "$rc" -eq 0 ] || { echo '{"ok":true,"note":"resolver-error-skip-gate"}'; exit 0; }
41
+ slug=$(echo "$json" | jq -r '.feature_slug // empty')
42
+ candidate_slugs=$(echo "$json" | jq -c '.candidate_slugs // []')
43
+ has_requirement=$(echo "$json" | jq -r '.has_requirement')
44
+ has_design=$(echo "$json" | jq -r '.has_design')
45
+ has_plan=$(echo "$json" | jq -r '.has_plan')
46
+
47
+ if [ "$require_plan" = true ]; then
48
+ if [ -z "$slug" ]; then
49
+ if [ "$candidate_slugs" != "[]" ]; then
50
+ echo "$json" | jq '{ok:false, error:"ambiguous-feature", candidate_slugs:.candidate_slugs,
51
+ hint:"multiple agent-docs/doflow/ feature dirs exist and no git branch disambiguates them — ask which one via AskUserQuestion, then re-run do-prereqs.sh --require-plan --slug=<chosen>"}'
52
+ else
53
+ echo "$json" | jq '{ok:false, error:"no-active-feature", hint:"run /do-brainstorm to start a feature"}'
54
+ fi
55
+ exit 2
56
+ fi
57
+ if [ "$has_requirement" != "true" ] || [ "$has_design" != "true" ] || [ "$has_plan" != "true" ]; then
58
+ echo "$json" | jq '{ok:false, error:"missing-prereqs", has_requirement:.has_requirement,
59
+ has_design:.has_design, has_plan:.has_plan,
60
+ hint:"run /do-design and /do-plan before /do-execute-plan"}'
61
+ exit 2
62
+ fi
63
+ fi
64
+
65
+ echo "$json" | jq '{ok:true, feature_slug:.feature_slug, has_requirement:.has_requirement, has_design:.has_design, has_plan:.has_plan}'
66
+ exit 0