@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,546 @@
1
+ #!/usr/bin/env bash
2
+ # do-git-state.sh — deterministic lifecycle state helper for /do-git.
3
+ #
4
+ # Emits JSON describing the repository's position in the development cycle.
5
+ # This is a deterministic layer component: no prompts, no side effects, just
6
+ # reading git refs and producing structured data.
7
+
8
+ set -uo pipefail
9
+
10
+ if ! command -v jq >/dev/null 2>&1; then
11
+ printf '{"error":"jq-not-found","branch_class":null}\n'
12
+ exit 0
13
+ fi
14
+
15
+ script_dir="$(cd "$(dirname "$0")" && pwd)"
16
+ resolved="$(bash "$script_dir/do-paths.sh" --json 2>/dev/null)"
17
+ repo_root="$(printf '%s' "$resolved" | jq -r '.repo_root // empty')"
18
+ # The resolver refuses a branch whose derived slug is unsafe and then prints no repo_root. This
19
+ # helper does not need a feature, only the repository, so it asks git rather than reporting a
20
+ # repository that exists as "not-a-git-repo".
21
+ [ -z "$repo_root" ] && repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
22
+
23
+ trunk_names="main develop"
24
+ feature_prefixes="feat feature"
25
+ fix_prefixes="fix bugfix"
26
+ integration_branch="develop"
27
+ production_branch="master"
28
+
29
+ mode=""
30
+ class_name=""
31
+ slug=""
32
+
33
+ for arg in "$@"; do
34
+ case "$arg" in
35
+ --state) mode="state" ;;
36
+ --next-version) mode="next-version" ;;
37
+ --propagation-targets) mode="propagation-targets" ;;
38
+ --fingerprint) mode="fingerprint" ;;
39
+ --lifecycle) mode="lifecycle" ;;
40
+ --branch-name) mode="branch-name" ;;
41
+ --class=*) class_name="${arg#--class=}" ;;
42
+ --slug=*) slug="${arg#--slug=}" ;;
43
+ *) ;;
44
+ esac
45
+ done
46
+
47
+ [ -z "$mode" ] && mode="state"
48
+
49
+ if [ -z "$repo_root" ]; then
50
+ printf '{"error":"not-a-git-repo"}\n'
51
+ exit 0
52
+ fi
53
+
54
+ cd "$repo_root"
55
+
56
+ current_branch=""
57
+ if git rev-parse --abbrev-ref HEAD >/dev/null 2>&1; then
58
+ current_branch="$(git rev-parse --abbrev-ref HEAD)"
59
+ else
60
+ current_branch=""
61
+ fi
62
+
63
+ # ── Helper: classify a branch name ────────────────────────────────────────────
64
+ get_class() {
65
+ local b="$1"
66
+ case "$b" in
67
+ "") echo "trunk" ;;
68
+ master|main|develop|HEAD) echo "trunk" ;;
69
+ release/*) echo "release" ;;
70
+ hotfix/*) echo "hotfix" ;;
71
+ feat/*|feature/*) echo "feature" ;;
72
+ fix/*|bugfix/*) echo "fix" ;;
73
+ */*) echo "other" ;;
74
+ *) echo "other" ;;
75
+ esac
76
+ }
77
+
78
+ get_slug() {
79
+ local b="$1"
80
+ case "$b" in
81
+ "") echo "" ;;
82
+ master|main|develop|HEAD) echo "" ;;
83
+ release/*|hotfix/*) echo "" ;;
84
+ */*) echo "${b#*/}" ;;
85
+ *) echo "$b" ;;
86
+ esac
87
+ }
88
+
89
+ commits_between() {
90
+ local from="$1"
91
+ local to="$2"
92
+ if git rev-parse "$from" >/dev/null 2>&1 && git rev-parse "$to" >/dev/null 2>&1; then
93
+ local ahead="$(git rev-list --count "${to}..${from}" 2>/dev/null || echo "0")"
94
+ local behind="$(git rev-list --count "${from}..${to}" 2>/dev/null || echo "0")"
95
+ echo "$ahead $behind"
96
+ else
97
+ echo "0 0"
98
+ fi
99
+ }
100
+
101
+ is_dirty() {
102
+ local status="$(git status --short 2>/dev/null | wc -l)"
103
+ [ "$status" -gt 0 ]
104
+ }
105
+
106
+ # Which ref the integration branch resolves to decides whether a distance measured against it
107
+ # means anything. The local branch was used unconditionally, so a develop that had not been pulled
108
+ # made every distance wrong by however stale it was — observed live at 22 reported against 32.
109
+ #
110
+ # The remote-tracking ref is preferred where it exists; the local branch is the fallback for a
111
+ # repository with no remote or one never fetched. Preferring it does not make the answer *fresh* —
112
+ # origin/develop is only as current as the last fetch — so the third field reports how far the
113
+ # local branch trails, which is non-zero exactly when a `git fetch` would change the answer.
114
+ #
115
+ # Echoes: "<ref> <kind> <local-behind-remote>"
116
+ resolve_integration_ref() {
117
+ local branch="$1"
118
+ local ref="$branch" kind="local" behind=0
119
+ if git rev-parse --verify --quiet "refs/remotes/origin/${branch}" >/dev/null 2>&1; then
120
+ ref="origin/${branch}"
121
+ kind="remote-tracking"
122
+ if git rev-parse --verify --quiet "refs/heads/${branch}" >/dev/null 2>&1; then
123
+ behind="$(git rev-list --count "${branch}..origin/${branch}" 2>/dev/null || echo 0)"
124
+ fi
125
+ fi
126
+ printf '%s %s %s\n' "$ref" "$kind" "$behind"
127
+ }
128
+
129
+ # Commits either side of a comparison, or "0 0" when either ref is missing.
130
+ # Echoes: "<ahead> <behind>"
131
+ distance_from() {
132
+ local branch="$1" against="$2"
133
+ if git rev-parse "refs/heads/${branch}" >/dev/null 2>&1 && \
134
+ git rev-parse --verify --quiet "$against" >/dev/null 2>&1; then
135
+ local counts; counts="$(commits_between "$branch" "$against")"
136
+ printf '%s %s\n' "$(echo "$counts" | awk '{print $1}')" "$(echo "$counts" | awk '{print $2}')"
137
+ else
138
+ printf '0 0\n'
139
+ fi
140
+ }
141
+
142
+ # The single lifecycle word for a branch's position. Ordered so the most actionable state wins:
143
+ # an uncommitted change matters more than any distance, and being behind matters more than ahead.
144
+ lifecycle_position() {
145
+ local ahead="$1" behind="$2" dirty="$3"
146
+ if [ "$dirty" = "true" ]; then printf 'dirty-worktree\n'
147
+ elif [ "$behind" -gt 0 ]; then printf 'behind-integration\n'
148
+ elif [ "$ahead" -gt 0 ]; then printf 'ahead-of-integration\n'
149
+ else printf 'in-sync\n'
150
+ fi
151
+ }
152
+
153
+ do_state() {
154
+ local class="$(get_class "$current_branch")"
155
+ local slugs="$(get_slug "$current_branch")"
156
+ local dirty="false"
157
+ is_dirty && dirty="true"
158
+
159
+ local integration_ref integration_ref_kind integration_local_behind
160
+ read -r integration_ref integration_ref_kind integration_local_behind \
161
+ <<< "$(resolve_integration_ref "$integration_branch")"
162
+
163
+ local ahead behind position
164
+ read -r ahead behind <<< "$(distance_from "$current_branch" "$integration_ref")"
165
+ position="$(lifecycle_position "$ahead" "$behind" "$dirty")"
166
+
167
+ jq -n \
168
+ --arg branch "${current_branch:-null}" \
169
+ --arg class_name "$class" \
170
+ --arg feature_slug "$slugs" \
171
+ --argjson dirty "$dirty" \
172
+ --arg ahead "$ahead" \
173
+ --arg behind "$behind" \
174
+ --arg position "$position" \
175
+ --arg integration_ref "$integration_ref" \
176
+ --arg integration_ref_kind "$integration_ref_kind" \
177
+ --arg integration_local_behind "$integration_local_behind" \
178
+ '{
179
+ branch: (if $branch=="" then null else $branch end),
180
+ class: $class_name,
181
+ feature_slug: (if $feature_slug=="" then null else $feature_slug end),
182
+ dirty: $dirty,
183
+ ahead_of_integration: ($ahead | tonumber),
184
+ behind_integration: ($behind | tonumber),
185
+ integration_ref: $integration_ref,
186
+ integration_ref_kind: $integration_ref_kind,
187
+ integration_local_behind_remote: ($integration_local_behind | tonumber),
188
+ position: $position
189
+ }'
190
+ }
191
+
192
+ # The production branch's refs, for finding release tags that were never merged back: the local
193
+ # branch and its remote-tracking ref, both when both exist, because either may be the one that
194
+ # holds the newest release (a local `main` that was not pulled is stale, a remote-tracking one that
195
+ # was not fetched is too). `main` is the default; `master` is the older default the lifecycle
196
+ # policy still names, used only when no `main` ref exists. Echoes one ref per line, possibly none.
197
+ production_refs() {
198
+ local b ref found
199
+ for b in main master; do
200
+ found=""
201
+ for ref in "refs/heads/${b}" "refs/remotes/origin/${b}"; do
202
+ if git rev-parse --verify --quiet "$ref" >/dev/null 2>&1; then printf '%s\n' "${ref#refs/}"; found=1; fi
203
+ done
204
+ [ -n "$found" ] && return
205
+ done
206
+ }
207
+
208
+ # Splits a semver string into its numeric core and its pre-release label.
209
+ # Echoes: "<major> <minor> <patch> <prerelease-or-empty>"
210
+ parse_semver() {
211
+ local version="$1"
212
+ local core="${version%%-*}"; core="${core%%+*}"
213
+ local pre=""
214
+ case "$version" in
215
+ *-*) pre="${version#*-}"; pre="${pre%%+*}" ;;
216
+ esac
217
+ local major minor patch
218
+ IFS='.' read -r major minor patch <<< "$core" 2>/dev/null || true
219
+ printf '%s %s %s %s\n' "${major:-0}" "${minor:-0}" "${patch:-0}" "$pre"
220
+ }
221
+
222
+ # Reads the commit subjects since a base tag and names the bump they imply.
223
+ # Echoes: "<MAJOR|MINOR|PATCH|INITIAL> <commit-count>"
224
+ classify_bump() {
225
+ local base_tag="$1"
226
+ if [ -z "$base_tag" ]; then
227
+ printf 'INITIAL 0\n'
228
+ return
229
+ fi
230
+ local subjects count kind="PATCH"
231
+ subjects="$(git log --format=%s "${base_tag}..HEAD" 2>/dev/null || true)"
232
+ # `git rev-list --count`, not `printf | wc -l`: the latter sees no trailing newline and so
233
+ # undercounts every range by one.
234
+ count="$(git rev-list --count "${base_tag}..HEAD" 2>/dev/null || echo 0)"
235
+ if printf '%s' "$subjects" | grep -qE 'BREAKING CHANGE|\!:'; then
236
+ kind="MAJOR"
237
+ elif printf '%s' "$subjects" | grep -qE '^feat|^feature'; then
238
+ kind="MINOR"
239
+ fi
240
+ printf '%s %s\n' "$kind" "$count"
241
+ }
242
+
243
+ # The next free number on a pre-release line. Computing label.N+1 arithmetically once proposed a
244
+ # tag that already existed — a well-formed value derived without consulting the thing it describes.
245
+ # `git describe` finds the nearest *reachable* tag, so a pre-release cut on an unmerged branch is
246
+ # invisible to the base-tag lookup and collides here instead. Taken numbers are stepped over rather
247
+ # than stopped on, because the caller wants a usable candidate, and the count of them is reported
248
+ # because the gap is a fact about the tag history. Bounded so a pathological tag set cannot spin.
249
+ # Echoes: "<candidate> <skipped-count>"
250
+ next_free_prerelease() {
251
+ local core="$1" pre="$2"
252
+ local label="${pre%.*}" num="${pre##*.}"
253
+ if ! printf '%s' "$num" | grep -qE '^[0-9]+$'; then
254
+ printf '%s-%s.1 0\n' "$core" "$pre"
255
+ return
256
+ fi
257
+ local candidate=$((num + 1)) skipped=0 guard=0
258
+ while [ "$guard" -lt 100 ]; do
259
+ if ! git rev-parse --verify --quiet "refs/tags/v${core}-${label}.${candidate}" >/dev/null 2>&1; then
260
+ break
261
+ fi
262
+ candidate=$((candidate + 1)); skipped=$((skipped + 1)); guard=$((guard + 1))
263
+ done
264
+ printf '%s-%s.%s %s\n' "$core" "$label" "$candidate" "$skipped"
265
+ }
266
+
267
+ # The newest release tag reachable from HEAD or from the production branch, by version order;
268
+ # empty when there is none. do_next_version bases its proposal on it and the lifecycle output
269
+ # reports it, so the two never disagree. The reasoning behind "newest by version" and "reachable
270
+ # from production too" is in do_next_version's comments below.
271
+ find_base_tag() {
272
+ local base_tag="" eligible="" ref
273
+ eligible="$(git tag --merged HEAD --list 'v*' 2>/dev/null || true)"
274
+ while IFS= read -r ref; do
275
+ [ -z "$ref" ] && continue
276
+ eligible="${eligible}"$'\n'"$(git tag --merged "$ref" --list 'v*' 2>/dev/null || true)"
277
+ done <<< "$(production_refs)"
278
+ eligible="$(printf '%s\n' "$eligible" | sort -u | sed '/^$/d')"
279
+ if [ -n "$eligible" ]; then
280
+ base_tag="$(git tag --list 'v*' --sort=-v:refname 2>/dev/null | grep -Fx -f <(printf '%s\n' "$eligible") | head -1 || true)"
281
+ fi
282
+ printf '%s\n' "$base_tag"
283
+ }
284
+
285
+ do_next_version() {
286
+ # Both start set: `set -u` makes a bare `local base_tag` an unbound variable on bash 4+ whenever
287
+ # no eligible tag exists (a repository with no release tags yet).
288
+ local base_tag="" current_version="0.0.0"
289
+ # `git describe --tags --abbrev=0` answers "nearest reachable tag by commit distance", and every
290
+ # version decision below needs "newest reachable tag by version". Those coincide only while
291
+ # history is linear — and a release ritual merges twice, so the newest tag routinely sits further
292
+ # from HEAD than an older one. On this repository after v1.0.0 shipped, beta.8 was reachable at 33
293
+ # commits and beta.7 at 31, so describe returned beta.7 and every later computation was based on a
294
+ # superseded release.
295
+ #
296
+ # Sorting by version instead. The `v*` filter keeps a non-version tag from winning the sort, and
297
+ # --merged keeps the answer to tags that are actually released work.
298
+ #
299
+ # "Merged" means into HEAD *or into the production branch*. A release ritual tags the merge commit
300
+ # on production, and the integration branch only gets that commit back through a second merge
301
+ # that may not have happened yet, so from the integration branch the newest release tag is
302
+ # unreachable and the proposal was based on an older one (v1.8.3 proposed while v1.12.0 was out).
303
+ # The tags are sorted by git as before; the union only decides which of them are eligible.
304
+ base_tag="$(find_base_tag)"
305
+ [ -n "$base_tag" ] && current_version="${base_tag#v}"
306
+
307
+ # The manifest is what a release publishes. A base tag that disagrees with it means the tag or
308
+ # the manifest is behind, which the caller should see before proposing a version from either.
309
+ local manifest_version="" warning=""
310
+ if [ -n "$base_tag" ] && [ -f package.json ]; then
311
+ manifest_version="$(jq -r '.version // empty' package.json 2>/dev/null || true)"
312
+ if [ -n "$manifest_version" ] && [ "$manifest_version" != "$current_version" ]; then
313
+ warning="manifest version ${manifest_version} differs from base tag ${base_tag}"
314
+ fi
315
+ fi
316
+
317
+ local major minor patch prerelease
318
+ read -r major minor patch prerelease <<< "$(parse_semver "$current_version")"
319
+ local version_core="${major}.${minor}.${patch}"
320
+
321
+ local bump_kind commit_count
322
+ read -r bump_kind commit_count <<< "$(classify_bump "$base_tag")"
323
+
324
+ # Semver orders 1.0.0-beta.7 < 1.0.0 < 1.0.1, so a pre-release already sits *before* its own
325
+ # version number: promoting it IS the bump, not an increment on top of it. A bump promotes when
326
+ # the field it would raise is already zero, and otherwise increments with the suffix falling
327
+ # away. These are node-semver's inc() rules reproduced rather than invented.
328
+ local next_major="$major" next_minor="$minor" next_patch="$patch"
329
+ if [ "$bump_kind" = "INITIAL" ]; then
330
+ next_major=1; next_minor=0; next_patch=0
331
+ elif [ -n "$prerelease" ]; then
332
+ case "$bump_kind" in
333
+ MAJOR) [ "$minor" -eq 0 ] && [ "$patch" -eq 0 ] || { next_major=$((major + 1)); next_minor=0; next_patch=0; } ;;
334
+ MINOR) [ "$patch" -eq 0 ] || { next_minor=$((minor + 1)); next_patch=0; } ;;
335
+ esac
336
+ else
337
+ case "$bump_kind" in
338
+ MAJOR) next_major=$((major + 1)); next_minor=0; next_patch=0 ;;
339
+ MINOR) next_minor=$((minor + 1)); next_patch=0 ;;
340
+ *) next_patch=$((patch + 1)) ;;
341
+ esac
342
+ fi
343
+ local next_version="${next_major}.${next_minor}.${next_patch}"
344
+
345
+ # The other honest option for a pre-release base: continue the line rather than promote it.
346
+ # Reported alongside so the release ritual can offer the choice instead of assuming one.
347
+ local next_prerelease="" next_prerelease_skipped=0
348
+ if [ -n "$prerelease" ]; then
349
+ read -r next_prerelease next_prerelease_skipped <<< "$(next_free_prerelease "$version_core" "$prerelease")"
350
+ fi
351
+
352
+ jq -n \
353
+ --arg base_tag "${base_tag:-null}" \
354
+ --arg current_version "$current_version" \
355
+ --arg next_version "$next_version" \
356
+ --arg next_prerelease "$next_prerelease" \
357
+ --arg next_prerelease_skipped "$next_prerelease_skipped" \
358
+ --arg prerelease "$prerelease" \
359
+ --arg bump_kind "$bump_kind" \
360
+ --argjson commit_count "$commit_count" \
361
+ --arg warning "$warning" \
362
+ '{
363
+ base_tag: (if $base_tag=="" then null else $base_tag end),
364
+ current_version: $current_version,
365
+ is_prerelease: ($prerelease != ""),
366
+ next_version: $next_version,
367
+ next_prerelease: (if $next_prerelease=="" then null else $next_prerelease end),
368
+ next_prerelease_skipped: ($next_prerelease_skipped | tonumber),
369
+ bump_kind: $bump_kind,
370
+ commits_count: ($commit_count | tonumber)
371
+ } + (if $warning != "" then {warning: $warning} else {} end)'
372
+ }
373
+
374
+ do_propagation_targets() {
375
+ local targets_json="[]"
376
+
377
+ # Integration branch is always a target
378
+ targets_json="$(echo "$targets_json" | jq --arg k "integration" --arg n "$integration_branch" --argjson hf "true" \
379
+ '. += [{kind: $k, name: $n, has_fix: $hf}]')"
380
+
381
+ # Get all active release branches as targets
382
+ while IFS= read -r rel; do
383
+ [ -z "$rel" ] && continue
384
+ targets_json="$(echo "$targets_json" | jq --arg k "release" --arg n "$rel" --argjson hf "false" \
385
+ '. += [{kind: $k, name: $n, has_fix: $hf}]')"
386
+ done < <(git for-each-ref --format='%(refname:short)' refs/heads/release/* 2>/dev/null | sed 's|refs/heads/release/||')
387
+
388
+ local current_class="$(get_class "$current_branch")"
389
+
390
+ jq -n \
391
+ --arg current "${current_branch:-null}" \
392
+ --arg class "$current_class" \
393
+ '{current: (if $current=="" then null else $current end), class: $class, targets: []}' \
394
+ | jq --argjson t "$targets_json" '.targets = $t'
395
+ }
396
+
397
+ do_fingerprint() {
398
+ local head_sha=""
399
+ head_sha="$(git rev-parse HEAD 2>/dev/null | head -c 40)" || true
400
+
401
+ [ -z "$head_sha" ] && { printf '{"error":"cannot-read-head-sha"}\n'; exit 1; }
402
+
403
+ local ref_list=""
404
+ while IFS= read -r r; do
405
+ ref_list="${ref_list}${r}"$'\n'
406
+ done < <(git for-each-ref --format='%(refname)' refs/heads refs/tags 2>/dev/null | sort)
407
+
408
+ local fp=""
409
+ fp="$(printf '%s\n%s' "$head_sha" "$ref_list" | shasum -a 256 | cut -d' ' -f1)" || true
410
+
411
+ [ -z "$fp" ] && fp="fingerprint-unavailable"
412
+
413
+ jq -n \
414
+ --arg sha "$head_sha" \
415
+ --arg branch "${current_branch:-null}" \
416
+ --arg fingerprint "$fp" \
417
+ '{
418
+ sha: $sha,
419
+ branch: (if $branch=="" then null else $branch end),
420
+ fingerprint: $fingerprint
421
+ }'
422
+ }
423
+
424
+ do_branch_name() {
425
+ local class="${class_name:-}"
426
+ local slugs="${slug:-}"
427
+
428
+ [ -z "$class" ] && { printf '{"error":"missing-class"}\n'; exit 2; }
429
+ [ -z "$slugs" ] && { printf '{"error":"missing-slug"}\n'; exit 2; }
430
+
431
+ # A slug becomes a branch name and then a directory under agent-docs/doflow/, so it must not be
432
+ # able to name a path or a flag (IC-009). Same shape do-paths.sh enforces for its own slugs.
433
+ # ASCII ranges whatever the locale: under a UTF-8 collation [A-Za-z] can admit "é".
434
+ if ! (export LC_ALL=C; [[ "$slugs" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] && [[ "$slugs" != *..* ]]); then
435
+ jq -n --arg slug "$slugs" '{error: "invalid-slug", message: ("slug \"" + $slug + "\" is not a valid feature slug: use letters, digits, dot, underscore or dash, start with a letter or digit, and no \"..\""), hint: "use letters, digits, dot, underscore or dash, start with a letter or digit, and no \"..\""}'
436
+ exit 2
437
+ fi
438
+
439
+ # Two vocabularies meet here and only one of them is a branch prefix.
440
+ #
441
+ # The arms below are *branch* classes — the same set get_class emits — so --class=fix round-trips
442
+ # and --class=bug did not: it fell through to a catch-all that prepended whatever it was handed,
443
+ # producing bug/<slug>, which get_class then classified as "other". Every DoFlow skill holds a
444
+ # *task* class (bug, refactor, dependency-change, trivial-edit, feature), so the caller most
445
+ # likely to use this verb was the one guaranteed to get an unusable name.
446
+ #
447
+ # Task classes are mapped rather than passed through. In branch terms everything that is not a
448
+ # feature is a fix: the lifecycle policy declares exactly two working prefixes, feat and fix, and
449
+ # inventing refactor/ or trivial-edit/ would widen a vocabulary get_class does not recognise —
450
+ # trading one unclassifiable name for four.
451
+ local branch_class="$class"
452
+ case "$class" in
453
+ bug|refactor|dependency-change|trivial-edit) branch_class="fix" ;;
454
+ esac
455
+
456
+ local branch_name=""
457
+ case "$branch_class" in
458
+ feature) branch_name="feat/${slugs}" ;;
459
+ fix) branch_name="fix/${slugs}" ;;
460
+ release) branch_name="release/${slugs}" ;;
461
+ hotfix) branch_name="hotfix/${slugs}" ;;
462
+ trunk|other)
463
+ printf '{"error":"cannot-create-branch-for-trunk-or-other-class"}\n'
464
+ exit 2
465
+ ;;
466
+ # An unrecognised class is refused rather than prepended. The catch-all that used to sit here
467
+ # is what let a task class through and produced a name no verb could classify.
468
+ *)
469
+ printf '{"error":"unknown-class","class":"%s","valid":"feature, fix, release, hotfix, bug, refactor, dependency-change, trivial-edit"}\n' "$class"
470
+ exit 2
471
+ ;;
472
+ esac
473
+
474
+ jq -n --arg name "$branch_name" '{name: $name}'
475
+ }
476
+
477
+ # The git facts the lifecycle verbs need, in one place so Node never derives them a second way
478
+ # (feature 046, DEC-044, DEC-035). Read-only.
479
+ # integration_ref develop, else main, else master, else origin/HEAD: the first that resolves, a
480
+ # local branch before its origin/ tracking ref; null when none does. Unlike
481
+ # resolve_integration_ref this never prefers the remote, because the lifecycle
482
+ # views must agree with the release preview about one pinned ref.
483
+ # feature_prefixes the branch prefixes a feature branch is named with
484
+ # integration_local_behind_remote how many commits origin/<integration> is ahead of the local
485
+ # integration branch (0 when it has no origin counterpart or is itself origin/...);
486
+ # the same field --state reports, so a merge that only exists on the remote is
487
+ # visible as the staleness it is. No fetch is run: the answer is as fresh as the
488
+ # last one.
489
+ # release_tags v* tags that match the release-tag pattern (vX.Y.Z with an optional pre-release)
490
+ # base_tag the same base tag --next-version proposes from, or null
491
+ lifecycle_integration_ref() {
492
+ local name
493
+ for name in develop main master; do
494
+ if git rev-parse --verify --quiet "refs/heads/${name}" >/dev/null 2>&1; then printf '%s\n' "$name"; return; fi
495
+ if git rev-parse --verify --quiet "refs/remotes/origin/${name}" >/dev/null 2>&1; then printf 'origin/%s\n' "$name"; return; fi
496
+ done
497
+ if git rev-parse --verify --quiet "refs/remotes/origin/HEAD" >/dev/null 2>&1; then printf 'origin/HEAD\n'; return; fi
498
+ printf '\n'
499
+ }
500
+
501
+ do_lifecycle() {
502
+ local integration="" tags="" base_tag=""
503
+ integration="$(lifecycle_integration_ref)"
504
+ tags="$(git tag --list 'v*' 2>/dev/null | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$' || true)"
505
+ base_tag="$(find_base_tag)"
506
+
507
+ local behind=0
508
+ case "$integration" in
509
+ ""|origin/*) ;;
510
+ *)
511
+ if git rev-parse --verify --quiet "refs/remotes/origin/${integration}" >/dev/null 2>&1; then
512
+ behind="$(git rev-list --count "${integration}..origin/${integration}" 2>/dev/null || echo 0)"
513
+ fi
514
+ ;;
515
+ esac
516
+
517
+ local prefixes_json="" tags_json=""
518
+ prefixes_json="$(printf '%s\n' $feature_prefixes | jq -R . | jq -s .)"
519
+ tags_json="$(printf '%s' "$tags" | jq -R -s 'split("\n") | map(select(length > 0))')"
520
+
521
+ jq -n \
522
+ --arg integration "$integration" \
523
+ --arg base_tag "$base_tag" \
524
+ --arg behind "$behind" \
525
+ --argjson prefixes "$prefixes_json" \
526
+ --argjson tags "$tags_json" \
527
+ '{
528
+ integration_ref: (if $integration=="" then null else $integration end),
529
+ integration_local_behind_remote: ($behind | tonumber),
530
+ feature_prefixes: $prefixes,
531
+ release_tags: $tags,
532
+ base_tag: (if $base_tag=="" then null else $base_tag end)
533
+ }'
534
+ }
535
+
536
+ case "$mode" in
537
+ state) do_state ;;
538
+ next-version) do_next_version ;;
539
+ propagation-targets) do_propagation_targets ;;
540
+ fingerprint) do_fingerprint ;;
541
+ lifecycle) do_lifecycle ;;
542
+ branch-name) do_branch_name ;;
543
+ *) printf '{"error":"unknown-mode","mode":"%s"}\n' "$mode"; exit 2 ;;
544
+ esac
545
+
546
+ exit 0