agentic-flow-kit 1.8.2 → 2.0.0

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 (936) hide show
  1. package/README.md +113 -148
  2. package/bin/flow-kit.mjs +5 -2
  3. package/locales/en/installer.json +25 -0
  4. package/locales/en/overrides/.agents/commands/deploy.md +52 -0
  5. package/locales/en/overrides/.agents/commands/maintain.md +76 -0
  6. package/locales/en/overrides/.agents/commands/new-task.md +89 -0
  7. package/locales/en/overrides/.agents/commands/pipeline-run.md +56 -0
  8. package/locales/en/overrides/.agents/commands/pr-review.md +30 -0
  9. package/locales/en/overrides/.agents/commands/review.md +93 -0
  10. package/locales/en/overrides/.agents/commands/stages.md +256 -0
  11. package/locales/en/overrides/.agents/notes/pr-review-cost-discipline.md +48 -0
  12. package/locales/en/overrides/.agents/notes/process-order-rule.md +21 -0
  13. package/locales/en/overrides/.agents/notes/runtime-env.local.example.md +21 -0
  14. package/locales/en/overrides/.agents/pipelines/_TEMPLATE.md +75 -0
  15. package/locales/en/overrides/.agents/pipelines/pipeline-closing.md +28 -0
  16. package/locales/en/overrides/.agents/roles/implementer.md +28 -0
  17. package/locales/en/overrides/.agents/roles/independent-reviewer.md +39 -0
  18. package/locales/en/overrides/.agents/roles/pr-adjudicator.md +27 -0
  19. package/locales/en/overrides/.agents/roles/pr-finder.md +51 -0
  20. package/locales/en/overrides/.agents/roles/pr-verifier.md +44 -0
  21. package/locales/en/overrides/.agents/roles/ui-verifier.md +22 -0
  22. package/locales/en/overrides/.agents/roles/wiki-compiler.md +47 -0
  23. package/locales/en/overrides/.agents/skills/arena/SKILL.md +50 -0
  24. package/locales/en/overrides/.agents/skills/autonomous-run/SKILL.md +45 -0
  25. package/locales/en/overrides/.agents/skills/blast-radius/SKILL.md +53 -0
  26. package/locales/en/overrides/.agents/skills/csharp-quality-gates/SKILL.md +109 -0
  27. package/locales/en/overrides/.agents/skills/dev-tour/SKILL.md +98 -0
  28. package/locales/en/overrides/.agents/skills/dream/SKILL.md +63 -0
  29. package/locales/en/overrides/.agents/skills/eval/SKILL.md +42 -0
  30. package/locales/en/overrides/.agents/skills/interrogate/SKILL.md +49 -0
  31. package/locales/en/overrides/.agents/skills/number-vetting/SKILL.md +35 -0
  32. package/locales/en/overrides/.agents/skills/page-refactor/SKILL.md +192 -0
  33. package/locales/en/overrides/.agents/skills/papercut-fix/SKILL.md +92 -0
  34. package/locales/en/overrides/.agents/skills/pause-safely/SKILL.md +31 -0
  35. package/locales/en/overrides/.agents/skills/perf/SKILL.md +42 -0
  36. package/locales/en/overrides/.agents/skills/review-method/SKILL.md +92 -0
  37. package/locales/en/overrides/.agents/skills/session-resume/SKILL.md +69 -0
  38. package/locales/en/overrides/.agents/skills/shipyard-workflow-closeout/SKILL.md +109 -0
  39. package/locales/en/overrides/.agents/skills/show-me-your-work/SKILL.md +57 -0
  40. package/locales/en/overrides/.agents/skills/tfs-pr/SKILL.md +95 -0
  41. package/locales/en/overrides/.agents/skills/verify-ui/SKILL.md +116 -0
  42. package/locales/en/overrides/.agents/skills/visual-parity/SKILL.md +35 -0
  43. package/locales/en/overrides/.agents/skills/why/SKILL.md +56 -0
  44. package/locales/en/overrides/.agents/skills/wiki/SKILL.md +53 -0
  45. package/locales/en/overrides/.claude/agents/implementer.md +9 -0
  46. package/locales/en/overrides/.claude/agents/independent-reviewer.md +10 -0
  47. package/locales/en/overrides/.claude/agents/pr-adjudicator.md +10 -0
  48. package/locales/en/overrides/.claude/agents/pr-finder.md +10 -0
  49. package/locales/en/overrides/.claude/agents/pr-verifier.md +10 -0
  50. package/locales/en/overrides/.claude/agents/ui-verifier.md +10 -0
  51. package/locales/en/overrides/.claude/agents/wiki-compiler.md +10 -0
  52. package/locales/en/overrides/.claude/commands/wf-deploy.md +8 -0
  53. package/locales/en/overrides/.claude/commands/wf-maintain.md +8 -0
  54. package/locales/en/overrides/.claude/commands/wf-new-task.md +8 -0
  55. package/locales/en/overrides/.claude/commands/wf-pipeline-run.md +8 -0
  56. package/locales/en/overrides/.claude/commands/wf-pr-review.md +8 -0
  57. package/locales/en/overrides/.claude/commands/wf-review.md +8 -0
  58. package/locales/en/overrides/.claude/commands/wf-stages.md +8 -0
  59. package/locales/en/overrides/.cline/agents/implementer.yml +13 -0
  60. package/locales/en/overrides/.cline/agents/independent-reviewer.yml +11 -0
  61. package/locales/en/overrides/.cline/agents/pr-adjudicator.yml +11 -0
  62. package/locales/en/overrides/.cline/agents/pr-finder.yml +11 -0
  63. package/locales/en/overrides/.cline/agents/pr-verifier.yml +11 -0
  64. package/locales/en/overrides/.cline/agents/ui-verifier.yml +8 -0
  65. package/locales/en/overrides/.cline/agents/wiki-compiler.yml +14 -0
  66. package/locales/en/overrides/.cline/workflows/wf-deploy.md +8 -0
  67. package/locales/en/overrides/.cline/workflows/wf-maintain.md +8 -0
  68. package/locales/en/overrides/.cline/workflows/wf-new-task.md +8 -0
  69. package/locales/en/overrides/.cline/workflows/wf-pipeline-run.md +8 -0
  70. package/locales/en/overrides/.cline/workflows/wf-pr-review.md +8 -0
  71. package/locales/en/overrides/.cline/workflows/wf-review.md +8 -0
  72. package/locales/en/overrides/.cline/workflows/wf-stages.md +8 -0
  73. package/locales/en/overrides/.codebuddy/CODEBUDDY.md +7 -0
  74. package/locales/en/overrides/.codebuddy/agents/implementer.md +9 -0
  75. package/locales/en/overrides/.codebuddy/agents/independent-reviewer.md +10 -0
  76. package/locales/en/overrides/.codebuddy/agents/pr-adjudicator.md +10 -0
  77. package/locales/en/overrides/.codebuddy/agents/pr-finder.md +10 -0
  78. package/locales/en/overrides/.codebuddy/agents/pr-verifier.md +10 -0
  79. package/locales/en/overrides/.codebuddy/agents/ui-verifier.md +11 -0
  80. package/locales/en/overrides/.codebuddy/agents/wiki-compiler.md +10 -0
  81. package/locales/en/overrides/.codebuddy/commands/wf-deploy.md +8 -0
  82. package/locales/en/overrides/.codebuddy/commands/wf-maintain.md +8 -0
  83. package/locales/en/overrides/.codebuddy/commands/wf-new-task.md +8 -0
  84. package/locales/en/overrides/.codebuddy/commands/wf-pipeline-run.md +8 -0
  85. package/locales/en/overrides/.codebuddy/commands/wf-pr-review.md +8 -0
  86. package/locales/en/overrides/.codebuddy/commands/wf-review.md +8 -0
  87. package/locales/en/overrides/.codebuddy/commands/wf-stages.md +8 -0
  88. package/locales/en/overrides/.codebuddy/rules/backend-scope.md +5 -0
  89. package/locales/en/overrides/.codebuddy/rules/frontend-scope.md +5 -0
  90. package/locales/en/overrides/.codebuddy/skills/tfs-pr/SKILL.md +6 -0
  91. package/locales/en/overrides/.commandcode/agents/implementer.md +10 -0
  92. package/locales/en/overrides/.commandcode/agents/independent-reviewer.md +9 -0
  93. package/locales/en/overrides/.commandcode/agents/pr-adjudicator.md +9 -0
  94. package/locales/en/overrides/.commandcode/agents/pr-finder.md +9 -0
  95. package/locales/en/overrides/.commandcode/agents/pr-verifier.md +9 -0
  96. package/locales/en/overrides/.commandcode/agents/ui-verifier.md +10 -0
  97. package/locales/en/overrides/.commandcode/agents/wiki-compiler.md +10 -0
  98. package/locales/en/overrides/.commandcode/commands/wf-deploy.md +7 -0
  99. package/locales/en/overrides/.commandcode/commands/wf-maintain.md +7 -0
  100. package/locales/en/overrides/.commandcode/commands/wf-new-task.md +7 -0
  101. package/locales/en/overrides/.commandcode/commands/wf-pipeline-run.md +7 -0
  102. package/locales/en/overrides/.commandcode/commands/wf-pr-review.md +7 -0
  103. package/locales/en/overrides/.commandcode/commands/wf-review.md +7 -0
  104. package/locales/en/overrides/.commandcode/commands/wf-stages.md +7 -0
  105. package/locales/en/overrides/.opencode/agents/implementer.md +51 -0
  106. package/locales/en/overrides/.opencode/agents/independent-reviewer.md +18 -0
  107. package/locales/en/overrides/.opencode/agents/pr-adjudicator.md +18 -0
  108. package/locales/en/overrides/.opencode/agents/pr-finder.md +18 -0
  109. package/locales/en/overrides/.opencode/agents/pr-verifier.md +18 -0
  110. package/locales/en/overrides/.opencode/agents/ui-verifier.md +45 -0
  111. package/locales/en/overrides/.opencode/agents/wiki-compiler.md +51 -0
  112. package/locales/en/overrides/.opencode/commands/wf-deploy.md +7 -0
  113. package/locales/en/overrides/.opencode/commands/wf-maintain.md +7 -0
  114. package/locales/en/overrides/.opencode/commands/wf-new-task.md +7 -0
  115. package/locales/en/overrides/.opencode/commands/wf-pipeline-run.md +7 -0
  116. package/locales/en/overrides/.opencode/commands/wf-pr-review.md +7 -0
  117. package/locales/en/overrides/.opencode/commands/wf-review.md +7 -0
  118. package/locales/en/overrides/.opencode/commands/wf-stages.md +7 -0
  119. package/locales/en/overrides/.pi/agents/implementer.md +11 -0
  120. package/locales/en/overrides/.pi/agents/independent-reviewer.md +12 -0
  121. package/locales/en/overrides/.pi/agents/pr-adjudicator.md +12 -0
  122. package/locales/en/overrides/.pi/agents/pr-finder.md +11 -0
  123. package/locales/en/overrides/.pi/agents/pr-verifier.md +11 -0
  124. package/locales/en/overrides/.pi/agents/ui-verifier.md +11 -0
  125. package/locales/en/overrides/.pi/agents/wiki-compiler.md +11 -0
  126. package/locales/en/overrides/.qoder/agents/implementer.md +9 -0
  127. package/locales/en/overrides/.qoder/agents/independent-reviewer.md +10 -0
  128. package/locales/en/overrides/.qoder/agents/pr-adjudicator.md +10 -0
  129. package/locales/en/overrides/.qoder/agents/pr-finder.md +10 -0
  130. package/locales/en/overrides/.qoder/agents/pr-verifier.md +10 -0
  131. package/locales/en/overrides/.qoder/agents/ui-verifier.md +10 -0
  132. package/locales/en/overrides/.qoder/agents/wiki-compiler.md +10 -0
  133. package/locales/en/overrides/.qoder/commands/wf-deploy.md +8 -0
  134. package/locales/en/overrides/.qoder/commands/wf-maintain.md +8 -0
  135. package/locales/en/overrides/.qoder/commands/wf-new-task.md +8 -0
  136. package/locales/en/overrides/.qoder/commands/wf-pipeline-run.md +8 -0
  137. package/locales/en/overrides/.qoder/commands/wf-pr-review.md +8 -0
  138. package/locales/en/overrides/.qoder/commands/wf-review.md +8 -0
  139. package/locales/en/overrides/.qoder/commands/wf-stages.md +8 -0
  140. package/locales/en/overrides/.qwen/agents/implementer.md +10 -0
  141. package/locales/en/overrides/.qwen/agents/independent-reviewer.md +10 -0
  142. package/locales/en/overrides/.qwen/agents/pr-adjudicator.md +10 -0
  143. package/locales/en/overrides/.qwen/agents/pr-finder.md +10 -0
  144. package/locales/en/overrides/.qwen/agents/pr-verifier.md +10 -0
  145. package/locales/en/overrides/.qwen/agents/ui-verifier.md +10 -0
  146. package/locales/en/overrides/.qwen/agents/wiki-compiler.md +13 -0
  147. package/locales/en/overrides/.qwen/commands/wf-deploy.md +7 -0
  148. package/locales/en/overrides/.qwen/commands/wf-maintain.md +7 -0
  149. package/locales/en/overrides/.qwen/commands/wf-new-task.md +7 -0
  150. package/locales/en/overrides/.qwen/commands/wf-pipeline-run.md +7 -0
  151. package/locales/en/overrides/.qwen/commands/wf-pr-review.md +7 -0
  152. package/locales/en/overrides/.qwen/commands/wf-review.md +7 -0
  153. package/locales/en/overrides/.qwen/commands/wf-stages.md +7 -0
  154. package/locales/en/overrides/.trae/agents/implementer.md +9 -0
  155. package/locales/en/overrides/.trae/agents/independent-reviewer.md +10 -0
  156. package/locales/en/overrides/.trae/agents/pr-adjudicator.md +10 -0
  157. package/locales/en/overrides/.trae/agents/pr-finder.md +10 -0
  158. package/locales/en/overrides/.trae/agents/pr-verifier.md +10 -0
  159. package/locales/en/overrides/.trae/agents/ui-verifier.md +10 -0
  160. package/locales/en/overrides/.trae/agents/wiki-compiler.md +10 -0
  161. package/locales/en/overrides/.trae/commands/wf-deploy.md +8 -0
  162. package/locales/en/overrides/.trae/commands/wf-maintain.md +8 -0
  163. package/locales/en/overrides/.trae/commands/wf-new-task.md +8 -0
  164. package/locales/en/overrides/.trae/commands/wf-pipeline-run.md +8 -0
  165. package/locales/en/overrides/.trae/commands/wf-pr-review.md +8 -0
  166. package/locales/en/overrides/.trae/commands/wf-review.md +8 -0
  167. package/locales/en/overrides/.trae/commands/wf-stages.md +8 -0
  168. package/locales/en/overrides/.trae/rules/backend-scope.md +5 -0
  169. package/locales/en/overrides/.trae/rules/frontend-scope.md +5 -0
  170. package/locales/en/overrides/.zcode/agents/implementer.md +8 -0
  171. package/locales/en/overrides/.zcode/agents/independent-reviewer.md +8 -0
  172. package/locales/en/overrides/.zcode/agents/ui-verifier.md +8 -0
  173. package/locales/en/overrides/.zcode/agents/wiki-compiler.md +8 -0
  174. package/locales/en/overrides/AGENTS.md +52 -0
  175. package/locales/en/overrides/CLAUDE.md +20 -0
  176. package/locales/en/overrides/wiki/INDEX.md +33 -0
  177. package/locales/en/overrides/wiki/drafts-archive/README.md +6 -0
  178. package/locales/en/overrides/wiki//346/225/260/346/215/256/347/273/264/346/212/244/README.md +22 -0
  179. package/locales/en/overrides/wiki//347/237/245/350/257/206/346/262/211/346/267/200/346/200/273/350/247/210.html +230 -0
  180. package/locales/en/overrides/workflow/INDEX.md +13 -0
  181. package/locales/en/overrides/workflow/README.md +67 -0
  182. package/locales/en/overrides/workflow/archive/README.md +3 -0
  183. package/locales/en/overrides/workflow/delegations.md +4 -0
  184. package/locales/en/overrides/workflow/effective-policy.md +4 -0
  185. package/locales/en/overrides/workflow/gate-cost.md +4 -0
  186. package/locales/en/overrides/workflow/incidents/_TEMPLATE.md +49 -0
  187. package/locales/en/overrides/workflow/intents/_TEMPLATE.md +43 -0
  188. package/locales/en/overrides/workflow/metrics.md +4 -0
  189. package/locales/en/overrides/workflow/papercuts.md +3 -0
  190. package/locales/en/overrides/workflow/plans/_TEMPLATE.md +47 -0
  191. package/locales/en/overrides/workflow/regression-checklist.md +3 -0
  192. package/locales/en/overrides/workflow/specs/_TEMPLATE.md +86 -0
  193. package/locales/en/overrides/workflow/verify-evidence.md +4 -0
  194. package/locales/zh-CN/installer.json +25 -0
  195. package/package.json +23 -36
  196. package/schemas/install-manifest.schema.json +24 -0
  197. package/schemas/project-profile.schema.json +28 -0
  198. package/src/cli/args.mjs +41 -0
  199. package/src/cli/main.mjs +72 -0
  200. package/src/commands/add-host.mjs +29 -0
  201. package/src/commands/doctor.mjs +80 -0
  202. package/src/commands/install.mjs +149 -0
  203. package/src/commands/upgrade.mjs +30 -0
  204. package/src/git/config.mjs +61 -0
  205. package/src/hosts/registry.mjs +26 -0
  206. package/src/i18n.mjs +32 -0
  207. package/src/install/apply.mjs +124 -0
  208. package/src/install/manifest.mjs +40 -0
  209. package/src/install/plan.mjs +83 -0
  210. package/src/render/tokens.mjs +58 -0
  211. package/templates/engine/.agents/board/dev-tour.html +1940 -0
  212. package/templates/engine/.agents/board/index.html +835 -0
  213. package/templates/engine/.agents/commands/deploy.md +52 -0
  214. package/templates/engine/.agents/commands/maintain.md +76 -0
  215. package/templates/engine/.agents/commands/new-task.md +89 -0
  216. package/templates/engine/.agents/commands/pipeline-run.md +56 -0
  217. package/templates/engine/.agents/commands/pr-review.md +30 -0
  218. package/templates/engine/.agents/commands/review.md +93 -0
  219. package/templates/engine/.agents/commands/stages.md +256 -0
  220. package/templates/engine/.agents/config/commit-msg-rules.json +7 -0
  221. package/templates/engine/.agents/config/engine.json +11 -0
  222. package/templates/engine/.agents/config/gate-budgets.json +5 -0
  223. package/templates/engine/.agents/config/mechanism-registry.md +62 -0
  224. package/templates/engine/.agents/config/model-roles.json +26 -0
  225. package/templates/engine/.agents/config/model-roles.local.example.json +7 -0
  226. package/templates/engine/.agents/config/pr-review.json +67 -0
  227. package/templates/engine/.agents/config/principles.md +32 -0
  228. package/templates/engine/.agents/config/workflow-enums.txt +25 -0
  229. package/templates/engine/.agents/hooks/check-architecture.mjs +168 -0
  230. package/templates/engine/.agents/hooks/check-audit-fields.mjs +56 -0
  231. package/templates/engine/.agents/hooks/check-commit-plan-ref.mjs +84 -0
  232. package/templates/engine/.agents/hooks/check-confirm-order.mjs +281 -0
  233. package/templates/engine/.agents/hooks/check-delete-guard.mjs +63 -0
  234. package/templates/engine/.agents/hooks/check-notify-guard.mjs +55 -0
  235. package/templates/engine/.agents/hooks/commit-check.cjs +208 -0
  236. package/templates/engine/.agents/hooks/gates.json +33 -0
  237. package/templates/engine/.agents/hooks/local-pre-commit.mjs +27 -0
  238. package/templates/engine/.agents/hooks/pre-shell-check.cjs +361 -0
  239. package/templates/engine/.agents/hooks/pre-write-check.cjs +220 -0
  240. package/templates/engine/.agents/notes/gate-diet.md +75 -0
  241. package/templates/engine/.agents/notes/pr-review-cost-discipline.md +48 -0
  242. package/templates/engine/.agents/notes/process-order-rule.md +21 -0
  243. package/templates/engine/.agents/notes/runtime-env.local.example.md +21 -0
  244. package/templates/engine/.agents/pipelines/_TEMPLATE.md +75 -0
  245. package/templates/engine/.agents/pipelines/pipeline-closing.md +28 -0
  246. package/templates/engine/.agents/pipelines//347/244/272/344/276/213-/345/271/266/350/241/214/345/256/236/347/216/260/350/257/204/345/256/241.md +16 -0
  247. package/templates/engine/.agents/roles/implementer.md +28 -0
  248. package/templates/engine/.agents/roles/independent-reviewer.md +39 -0
  249. package/templates/engine/.agents/roles/pr-adjudicator.md +27 -0
  250. package/templates/engine/.agents/roles/pr-finder.md +51 -0
  251. package/templates/engine/.agents/roles/pr-verifier.md +44 -0
  252. package/templates/engine/.agents/roles/ui-verifier.md +22 -0
  253. package/templates/engine/.agents/roles/wiki-compiler.md +47 -0
  254. package/templates/engine/.agents/scripts/agg-delegations.mjs +439 -0
  255. package/templates/engine/.agents/scripts/agg-judgments.mjs +301 -0
  256. package/templates/engine/.agents/scripts/agg-kb-usage.mjs +196 -0
  257. package/templates/engine/.agents/scripts/append-compensation.mjs +109 -0
  258. package/templates/engine/.agents/scripts/archive-sweep.mjs +229 -0
  259. package/templates/engine/.agents/scripts/auto-tour.mjs +77 -0
  260. package/templates/engine/.agents/scripts/board-port.mjs +17 -0
  261. package/templates/engine/.agents/scripts/check-doc-body-drift.mjs +102 -0
  262. package/templates/engine/.agents/scripts/check-engine-artifacts.mjs +54 -0
  263. package/templates/engine/.agents/scripts/check-hook-wiring.mjs +75 -0
  264. package/templates/engine/.agents/scripts/check-lane-surface.mjs +393 -0
  265. package/templates/engine/.agents/scripts/check-ledger-doc-presence.mjs +71 -0
  266. package/templates/engine/.agents/scripts/check-ledger-invariant.mjs +873 -0
  267. package/templates/engine/.agents/scripts/check-local-gate-config.mjs +95 -0
  268. package/templates/engine/.agents/scripts/check-local-gates.mjs +248 -0
  269. package/templates/engine/.agents/scripts/check-loop-kit.mjs +550 -0
  270. package/templates/engine/.agents/scripts/check-loop-rev-semantics.mjs +59 -0
  271. package/templates/engine/.agents/scripts/check-loop-sections/s01-pairing.mjs +116 -0
  272. package/templates/engine/.agents/scripts/check-loop-sections/s03x-regr-evidence.mjs +38 -0
  273. package/templates/engine/.agents/scripts/check-loop-sections/s08-acceptance.mjs +130 -0
  274. package/templates/engine/.agents/scripts/check-loop-sections/s15-fingerprint.mjs +152 -0
  275. package/templates/engine/.agents/scripts/check-loop-sections/s23-convergence-audit.mjs +182 -0
  276. package/templates/engine/.agents/scripts/check-loop-sections/s29-regr-enforcer.mjs +69 -0
  277. package/templates/engine/.agents/scripts/check-loop.mjs +131 -0
  278. package/templates/engine/.agents/scripts/check-regression-consistency.mjs +252 -0
  279. package/templates/engine/.agents/scripts/check-schema-residue.mjs +144 -0
  280. package/templates/engine/.agents/scripts/check-scope-range.mjs +156 -0
  281. package/templates/engine/.agents/scripts/check-scope.mjs +121 -0
  282. package/templates/engine/.agents/scripts/check-scripts-syntax.mjs +138 -0
  283. package/templates/engine/.agents/scripts/check-secrets-lite.mjs +239 -0
  284. package/templates/engine/.agents/scripts/check-secrets.mjs +104 -0
  285. package/templates/engine/.agents/scripts/check-test-impact-map.mjs +107 -0
  286. package/templates/engine/.agents/scripts/ci-assert-frontend-deps.mjs +42 -0
  287. package/templates/engine/.agents/scripts/ci-commit-hygiene.mjs +86 -0
  288. package/templates/engine/.agents/scripts/ci-install-gitleaks.mjs +75 -0
  289. package/templates/engine/.agents/scripts/ci-push-before.mjs +49 -0
  290. package/templates/engine/.agents/scripts/closeout.mjs +127 -0
  291. package/templates/engine/.agents/scripts/compact-ledgers.mjs +70 -0
  292. package/templates/engine/.agents/scripts/confirm-doc.mjs +737 -0
  293. package/templates/engine/.agents/scripts/delegation-presence.mjs +38 -0
  294. package/templates/engine/.agents/scripts/dev-tour-model.mjs +1398 -0
  295. package/templates/engine/.agents/scripts/dream-run.mjs +283 -0
  296. package/templates/engine/.agents/scripts/ensure-board.mjs +187 -0
  297. package/templates/engine/.agents/scripts/failure-attribution.mjs +11 -0
  298. package/templates/engine/.agents/scripts/fill-intent.mjs +60 -0
  299. package/templates/engine/.agents/scripts/fill-plan.mjs +105 -0
  300. package/templates/engine/.agents/scripts/fill-spec.mjs +72 -0
  301. package/templates/engine/.agents/scripts/gen-dev-tour.mjs +191 -0
  302. package/templates/engine/.agents/scripts/gen-effective-policy.mjs +113 -0
  303. package/templates/engine/.agents/scripts/gen-engine-map.mjs +292 -0
  304. package/templates/engine/.agents/scripts/gen-narrative-claims.mjs +275 -0
  305. package/templates/engine/.agents/scripts/gen-verify-evidence.mjs +115 -0
  306. package/templates/engine/.agents/scripts/gen-wiki-board.mjs +221 -0
  307. package/templates/engine/.agents/scripts/gen-workflow-index.mjs +201 -0
  308. package/templates/engine/.agents/scripts/gen-workflow-metrics.mjs +692 -0
  309. package/templates/engine/.agents/scripts/git-merge-jsonl.mjs +62 -0
  310. package/templates/engine/.agents/scripts/judgment-ledger.mjs +68 -0
  311. package/templates/engine/.agents/scripts/kb-cache-evict.mjs +50 -0
  312. package/templates/engine/.agents/scripts/kb-search.mjs +423 -0
  313. package/templates/engine/.agents/scripts/lane-surface-audit.mjs +145 -0
  314. package/templates/engine/.agents/scripts/ledger-loader.mjs +77 -0
  315. package/templates/engine/.agents/scripts/ledger-shards.mjs +346 -0
  316. package/templates/engine/.agents/scripts/metrics-core.mjs +131 -0
  317. package/templates/engine/.agents/scripts/pipeline-run.mjs +964 -0
  318. package/templates/engine/.agents/scripts/policy.mjs +250 -0
  319. package/templates/engine/.agents/scripts/pr-create.mjs +54 -0
  320. package/templates/engine/.agents/scripts/pr-review.mjs +1551 -0
  321. package/templates/engine/.agents/scripts/quote-grounding.mjs +233 -0
  322. package/templates/engine/.agents/scripts/regen-ledgers.mjs +59 -0
  323. package/templates/engine/.agents/scripts/rewrite-file.mjs +135 -0
  324. package/templates/engine/.agents/scripts/rule-budget.mjs +246 -0
  325. package/templates/engine/.agents/scripts/stage-gates.mjs +812 -0
  326. package/templates/engine/.agents/scripts/test-impact-lib.mjs +451 -0
  327. package/templates/engine/.agents/scripts/test-impact.mjs +105 -0
  328. package/templates/engine/.agents/scripts/trust-mode.mjs +130 -0
  329. package/templates/engine/.agents/scripts/verify-scope.mjs +163 -0
  330. package/templates/engine/.agents/scripts/verify-wiki-consistency.mjs +264 -0
  331. package/templates/engine/.agents/scripts/verify.mjs +847 -0
  332. package/templates/engine/.agents/scripts/warn-format.mjs +57 -0
  333. package/templates/engine/.agents/scripts/wf-journal.mjs +230 -0
  334. package/templates/engine/.agents/scripts/workflow-board-server.mjs +696 -0
  335. package/templates/engine/.agents/scripts/workflow-enums.mjs +70 -0
  336. package/templates/engine/.agents/scripts/workflows-check.mjs +260 -0
  337. package/templates/engine/.agents/settings.json +33 -0
  338. package/templates/engine/.agents/skills/arena/SKILL.md +50 -0
  339. package/templates/engine/.agents/skills/autonomous-run/SKILL.md +45 -0
  340. package/templates/engine/.agents/skills/blast-radius/SKILL.md +53 -0
  341. package/templates/engine/.agents/skills/csharp-quality-gates/SKILL.md +109 -0
  342. package/templates/engine/.agents/skills/csharp-quality-gates/scripts/quality-gates.mjs +430 -0
  343. package/templates/engine/.agents/skills/dev-tour/SKILL.md +98 -0
  344. package/templates/engine/.agents/skills/dream/SKILL.md +63 -0
  345. package/templates/engine/.agents/skills/eval/SKILL.md +42 -0
  346. package/templates/engine/.agents/skills/eval/scripts/eval-score.mjs +59 -0
  347. package/templates/engine/.agents/skills/interrogate/SKILL.md +49 -0
  348. package/templates/engine/.agents/skills/interrogate/scripts/interrogate-plan.mjs +72 -0
  349. package/templates/engine/.agents/skills/number-vetting/SKILL.md +35 -0
  350. package/templates/engine/.agents/skills/page-refactor/SKILL.md +192 -0
  351. package/templates/engine/.agents/skills/page-refactor/references/templates.md +989 -0
  352. package/templates/engine/.agents/skills/papercut-fix/SKILL.md +92 -0
  353. package/templates/engine/.agents/skills/papercut-fix/references/response-template.md +81 -0
  354. package/templates/engine/.agents/skills/pause-safely/SKILL.md +31 -0
  355. package/templates/engine/.agents/skills/perf/SKILL.md +42 -0
  356. package/templates/engine/.agents/skills/review-method/SKILL.md +92 -0
  357. package/templates/engine/.agents/skills/review-method/golden-set.md +61 -0
  358. package/templates/engine/.agents/skills/session-resume/SKILL.md +69 -0
  359. package/templates/engine/.agents/skills/session-resume/scripts/worktree-audit.mjs +110 -0
  360. package/templates/engine/.agents/skills/shipyard-workflow-closeout/SKILL.md +109 -0
  361. package/templates/engine/.agents/skills/show-me-your-work/SKILL.md +57 -0
  362. package/templates/engine/.agents/skills/show-me-your-work/references/decision-log-template.tsv +1 -0
  363. package/templates/engine/.agents/skills/show-me-your-work/scripts/decision-log.mjs +24 -0
  364. package/templates/engine/.agents/skills/tfs-pr/SKILL.md +95 -0
  365. package/templates/engine/.agents/skills/tfs-pr/scripts/tfs-pr.mjs +947 -0
  366. package/templates/engine/.agents/skills/verify-ui/SKILL.md +116 -0
  367. package/templates/engine/.agents/skills/verify-ui/features/.gitkeep +0 -0
  368. package/templates/engine/.agents/skills/verify-ui/scripts/gen-verify-feature.mjs +114 -0
  369. package/templates/engine/.agents/skills/visual-parity/SKILL.md +35 -0
  370. package/templates/engine/.agents/skills/why/SKILL.md +56 -0
  371. package/templates/engine/.agents/skills/why/scripts/why-evidence.mjs +74 -0
  372. package/templates/engine/.agents/skills/wiki/SKILL.md +53 -0
  373. package/templates/engine/.agents/tests/auto-tour.test.mjs +82 -0
  374. package/templates/engine/.agents/tests/dev-tour-model.test.mjs +452 -0
  375. package/templates/engine/.agents/tests/fill-plan-lite.test.mjs +46 -0
  376. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-bare-mid-path.md +14 -0
  377. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-bare-mid-path.model.json +179 -0
  378. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-fake-path-guard.md +21 -0
  379. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-fake-path-guard.model.json +194 -0
  380. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-h3-batches.md +20 -0
  381. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-h3-batches.model.json +207 -0
  382. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-issues.md +24 -0
  383. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-issues.model.json +225 -0
  384. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-l1-single-file.md +15 -0
  385. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-l1-single-file.model.json +160 -0
  386. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-layer-word-unique.md +29 -0
  387. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-layer-word-unique.model.json +266 -0
  388. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-layers-fallback.md +17 -0
  389. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-layers-fallback.model.json +207 -0
  390. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-numbered-steps.md +39 -0
  391. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-numbered-steps.model.json +359 -0
  392. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-path-guess.md +16 -0
  393. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-path-guess.model.json +131 -0
  394. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-table-rows.md +29 -0
  395. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-table-rows.model.json +190 -0
  396. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-task-checkboxes.md +39 -0
  397. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-task-checkboxes.model.json +362 -0
  398. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-union-label.md +24 -0
  399. package/templates/engine/.agents/tests/fixtures/dev-tour/plan-union-label.model.json +224 -0
  400. package/templates/engine/.agents/tests/tools/gen-synthetic-plan.mjs +108 -0
  401. package/templates/git/.gitattributes +22 -0
  402. package/templates/git/.githooks/commit-msg +72 -0
  403. package/templates/git/.githooks/post-commit +62 -0
  404. package/templates/git/.githooks/pre-commit +197 -0
  405. package/templates/git/.githooks/pre-merge-commit +28 -0
  406. package/templates/git/.githooks/pre-push +201 -0
  407. package/templates/git/.gitignore.engine +11 -0
  408. package/templates/hosts/claude/CLAUDE.md +55 -0
  409. package/templates/hosts/claude/agents/implementer.md +7 -0
  410. package/templates/hosts/claude/agents/independent-reviewer.md +8 -0
  411. package/templates/hosts/claude/agents/pr-adjudicator.md +8 -0
  412. package/templates/hosts/claude/agents/pr-finder.md +8 -0
  413. package/templates/hosts/claude/agents/pr-verifier.md +8 -0
  414. package/templates/hosts/claude/agents/ui-verifier.md +8 -0
  415. package/templates/hosts/claude/agents/wiki-compiler.md +8 -0
  416. package/templates/hosts/claude/commands/wf-deploy.md +10 -0
  417. package/templates/hosts/claude/commands/wf-maintain.md +10 -0
  418. package/templates/hosts/claude/commands/wf-new-task.md +10 -0
  419. package/templates/hosts/claude/commands/wf-pipeline-run.md +10 -0
  420. package/templates/hosts/claude/commands/wf-pr-review.md +10 -0
  421. package/templates/hosts/claude/commands/wf-review.md +10 -0
  422. package/templates/hosts/claude/commands/wf-stages.md +10 -0
  423. package/templates/hosts/claude/settings.json +57 -0
  424. package/templates/hosts/cline/agents/implementer.yml +15 -0
  425. package/templates/hosts/cline/agents/independent-reviewer.yml +20 -0
  426. package/templates/hosts/cline/agents/pr-adjudicator.yml +18 -0
  427. package/templates/hosts/cline/agents/pr-finder.yml +19 -0
  428. package/templates/hosts/cline/agents/pr-verifier.yml +18 -0
  429. package/templates/hosts/cline/agents/ui-verifier.yml +17 -0
  430. package/templates/hosts/cline/agents/wiki-compiler.yml +20 -0
  431. package/templates/hosts/cline/hooks/PreToolUse.cjs +260 -0
  432. package/templates/hosts/cline/workflows/wf-deploy.md +8 -0
  433. package/templates/hosts/cline/workflows/wf-maintain.md +8 -0
  434. package/templates/hosts/cline/workflows/wf-new-task.md +8 -0
  435. package/templates/hosts/cline/workflows/wf-pipeline-run.md +8 -0
  436. package/templates/hosts/cline/workflows/wf-pr-review.md +8 -0
  437. package/templates/hosts/cline/workflows/wf-review.md +8 -0
  438. package/templates/hosts/cline/workflows/wf-stages.md +8 -0
  439. package/templates/hosts/codebuddy/CODEBUDDY.md +7 -0
  440. package/templates/hosts/codebuddy/agents/implementer.md +9 -0
  441. package/templates/hosts/codebuddy/agents/independent-reviewer.md +10 -0
  442. package/templates/hosts/codebuddy/agents/pr-adjudicator.md +10 -0
  443. package/templates/hosts/codebuddy/agents/pr-finder.md +10 -0
  444. package/templates/hosts/codebuddy/agents/pr-verifier.md +10 -0
  445. package/templates/hosts/codebuddy/agents/ui-verifier.md +11 -0
  446. package/templates/hosts/codebuddy/agents/wiki-compiler.md +10 -0
  447. package/templates/hosts/codebuddy/commands/wf-deploy.md +10 -0
  448. package/templates/hosts/codebuddy/commands/wf-maintain.md +10 -0
  449. package/templates/hosts/codebuddy/commands/wf-new-task.md +10 -0
  450. package/templates/hosts/codebuddy/commands/wf-pipeline-run.md +10 -0
  451. package/templates/hosts/codebuddy/commands/wf-pr-review.md +10 -0
  452. package/templates/hosts/codebuddy/commands/wf-review.md +10 -0
  453. package/templates/hosts/codebuddy/commands/wf-stages.md +10 -0
  454. package/templates/hosts/codebuddy/rules/backend-scope.md +5 -0
  455. package/templates/hosts/codebuddy/rules/frontend-scope.md +5 -0
  456. package/templates/hosts/codebuddy/settings.json +31 -0
  457. package/templates/hosts/codebuddy/skills/tfs-pr/SKILL.md +6 -0
  458. package/templates/hosts/commandcode/agents/implementer.md +10 -0
  459. package/templates/hosts/commandcode/agents/independent-reviewer.md +9 -0
  460. package/templates/hosts/commandcode/agents/pr-adjudicator.md +9 -0
  461. package/templates/hosts/commandcode/agents/pr-finder.md +9 -0
  462. package/templates/hosts/commandcode/agents/pr-verifier.md +9 -0
  463. package/templates/hosts/commandcode/agents/ui-verifier.md +11 -0
  464. package/templates/hosts/commandcode/agents/wiki-compiler.md +10 -0
  465. package/templates/hosts/commandcode/commands/wf-deploy.md +9 -0
  466. package/templates/hosts/commandcode/commands/wf-maintain.md +9 -0
  467. package/templates/hosts/commandcode/commands/wf-new-task.md +9 -0
  468. package/templates/hosts/commandcode/commands/wf-pipeline-run.md +9 -0
  469. package/templates/hosts/commandcode/commands/wf-pr-review.md +9 -0
  470. package/templates/hosts/commandcode/commands/wf-review.md +9 -0
  471. package/templates/hosts/commandcode/commands/wf-stages.md +9 -0
  472. package/templates/hosts/commandcode/settings.json +59 -0
  473. package/templates/hosts/opencode/agents/implementer.md +51 -0
  474. package/templates/hosts/opencode/agents/independent-reviewer.md +18 -0
  475. package/templates/hosts/opencode/agents/pr-adjudicator.md +18 -0
  476. package/templates/hosts/opencode/agents/pr-finder.md +18 -0
  477. package/templates/hosts/opencode/agents/pr-verifier.md +18 -0
  478. package/templates/hosts/opencode/agents/ui-verifier.md +45 -0
  479. package/templates/hosts/opencode/agents/wiki-compiler.md +51 -0
  480. package/templates/hosts/opencode/commands/wf-deploy.md +7 -0
  481. package/templates/hosts/opencode/commands/wf-maintain.md +7 -0
  482. package/templates/hosts/opencode/commands/wf-new-task.md +7 -0
  483. package/templates/hosts/opencode/commands/wf-pipeline-run.md +7 -0
  484. package/templates/hosts/opencode/commands/wf-pr-review.md +7 -0
  485. package/templates/hosts/opencode/commands/wf-review.md +7 -0
  486. package/templates/hosts/opencode/commands/wf-stages.md +7 -0
  487. package/templates/hosts/pi/agents/implementer.md +10 -0
  488. package/templates/hosts/pi/agents/independent-reviewer.md +11 -0
  489. package/templates/hosts/pi/agents/pr-adjudicator.md +11 -0
  490. package/templates/hosts/pi/agents/pr-finder.md +10 -0
  491. package/templates/hosts/pi/agents/pr-verifier.md +10 -0
  492. package/templates/hosts/pi/agents/ui-verifier.md +10 -0
  493. package/templates/hosts/pi/agents/wiki-compiler.md +10 -0
  494. package/templates/hosts/pi/extensions/lib/gates-core.cjs +196 -0
  495. package/templates/hosts/pi/extensions/shipyard-gates.ts +95 -0
  496. package/templates/hosts/pi/settings.json +5 -0
  497. package/templates/hosts/qoder/agents/implementer.md +9 -0
  498. package/templates/hosts/qoder/agents/independent-reviewer.md +10 -0
  499. package/templates/hosts/qoder/agents/pr-adjudicator.md +10 -0
  500. package/templates/hosts/qoder/agents/pr-finder.md +10 -0
  501. package/templates/hosts/qoder/agents/pr-verifier.md +10 -0
  502. package/templates/hosts/qoder/agents/ui-verifier.md +10 -0
  503. package/templates/hosts/qoder/agents/wiki-compiler.md +10 -0
  504. package/templates/hosts/qoder/commands/wf-deploy.md +8 -0
  505. package/templates/hosts/qoder/commands/wf-maintain.md +8 -0
  506. package/templates/hosts/qoder/commands/wf-new-task.md +8 -0
  507. package/templates/hosts/qoder/commands/wf-pipeline-run.md +8 -0
  508. package/templates/hosts/qoder/commands/wf-pr-review.md +8 -0
  509. package/templates/hosts/qoder/commands/wf-review.md +8 -0
  510. package/templates/hosts/qoder/commands/wf-stages.md +8 -0
  511. package/templates/hosts/qoder/settings.json +63 -0
  512. package/templates/hosts/qwen/agents/implementer.md +8 -0
  513. package/templates/hosts/qwen/agents/independent-reviewer.md +8 -0
  514. package/templates/hosts/qwen/agents/pr-adjudicator.md +8 -0
  515. package/templates/hosts/qwen/agents/pr-finder.md +8 -0
  516. package/templates/hosts/qwen/agents/pr-verifier.md +8 -0
  517. package/templates/hosts/qwen/agents/ui-verifier.md +8 -0
  518. package/templates/hosts/qwen/agents/wiki-compiler.md +11 -0
  519. package/templates/hosts/qwen/commands/wf-deploy.md +9 -0
  520. package/templates/hosts/qwen/commands/wf-maintain.md +9 -0
  521. package/templates/hosts/qwen/commands/wf-new-task.md +9 -0
  522. package/templates/hosts/qwen/commands/wf-pipeline-run.md +9 -0
  523. package/templates/hosts/qwen/commands/wf-pr-review.md +9 -0
  524. package/templates/hosts/qwen/commands/wf-review.md +9 -0
  525. package/templates/hosts/qwen/commands/wf-stages.md +9 -0
  526. package/templates/hosts/qwen/settings.json +59 -0
  527. package/templates/hosts/trae/agents/implementer.md +9 -0
  528. package/templates/hosts/trae/agents/independent-reviewer.md +10 -0
  529. package/templates/hosts/trae/agents/pr-adjudicator.md +10 -0
  530. package/templates/hosts/trae/agents/pr-finder.md +10 -0
  531. package/templates/hosts/trae/agents/pr-verifier.md +10 -0
  532. package/templates/hosts/trae/agents/ui-verifier.md +10 -0
  533. package/templates/hosts/trae/agents/wiki-compiler.md +10 -0
  534. package/templates/hosts/trae/commands/wf-deploy.md +8 -0
  535. package/templates/hosts/trae/commands/wf-maintain.md +8 -0
  536. package/templates/hosts/trae/commands/wf-new-task.md +8 -0
  537. package/templates/hosts/trae/commands/wf-pipeline-run.md +8 -0
  538. package/templates/hosts/trae/commands/wf-pr-review.md +8 -0
  539. package/templates/hosts/trae/commands/wf-review.md +8 -0
  540. package/templates/hosts/trae/commands/wf-stages.md +8 -0
  541. package/templates/hosts/trae/hooks.json +17 -0
  542. package/templates/hosts/trae/rules/backend-scope.md +5 -0
  543. package/templates/hosts/trae/rules/frontend-scope.md +5 -0
  544. package/templates/hosts/zcode/agents/implementer.md +8 -0
  545. package/templates/hosts/zcode/agents/independent-reviewer.md +8 -0
  546. package/templates/hosts/zcode/agents/ui-verifier.md +8 -0
  547. package/templates/hosts/zcode/agents/wiki-compiler.md +8 -0
  548. package/templates/hosts/zcode/config.json +31 -0
  549. package/templates/hosts/zcode/workflows/code-review.dwf.ts +428 -0
  550. package/templates/hosts/zcode/workflows/fullchain-flow-test.dwf.ts +898 -0
  551. package/templates/profiles/dotnet/.agents/config/lane-surfaces.txt +33 -0
  552. package/templates/profiles/dotnet/.agents/config/rule-budgets.txt +84 -0
  553. package/templates/profiles/dotnet/.agents/config/test-impact-map.json +590 -0
  554. package/templates/profiles/dotnet/.agents/config/workflow-modules.txt +18 -0
  555. package/templates/profiles/dotnet/.agents/hooks/commit-check.config.json +18 -0
  556. package/templates/profiles/dotnet/.agents/hooks/delete-guard-allowlist.txt +28 -0
  557. package/templates/profiles/dotnet/.agents/notes/runtime-env.md +178 -0
  558. package/templates/profiles/dotnet/AGENTS.md +52 -0
  559. package/templates/profiles/empty/.agents/config/lane-surfaces.txt +12 -0
  560. package/templates/profiles/empty/.agents/config/rule-budgets.txt +3 -0
  561. package/templates/profiles/empty/.agents/config/test-impact-map.json +8 -0
  562. package/templates/profiles/empty/.agents/config/workflow-modules.txt +2 -0
  563. package/templates/profiles/empty/.agents/hooks/commit-check.config.json +5 -0
  564. package/templates/profiles/empty/.agents/hooks/delete-guard-allowlist.txt +2 -0
  565. package/templates/profiles/empty/.agents/notes/runtime-env.md +44 -0
  566. package/templates/profiles/empty/AGENTS.md +52 -0
  567. package/templates/profiles/node/.agents/config/lane-surfaces.txt +12 -0
  568. package/templates/profiles/node/.agents/config/rule-budgets.txt +3 -0
  569. package/templates/profiles/node/.agents/config/test-impact-map.json +8 -0
  570. package/templates/profiles/node/.agents/config/workflow-modules.txt +2 -0
  571. package/templates/profiles/node/.agents/hooks/commit-check.config.json +5 -0
  572. package/templates/profiles/node/.agents/hooks/delete-guard-allowlist.txt +2 -0
  573. package/templates/profiles/node/.agents/notes/runtime-env.md +44 -0
  574. package/templates/profiles/node/AGENTS.md +52 -0
  575. package/templates/scaffold/wiki/INDEX.md +33 -0
  576. package/templates/scaffold/wiki/drafts-archive/README.md +7 -0
  577. package/templates/scaffold/wiki//344/272/247/345/223/201/351/234/200/346/261/202/.gitkeep +0 -0
  578. package/templates/scaffold/wiki//345/274/200/345/217/221/346/226/271/346/241/210/.gitkeep +0 -0
  579. package/templates/scaffold/wiki//346/225/260/346/215/256/347/273/264/346/212/244/README.md +22 -0
  580. package/templates/scaffold/wiki//346/265/213/350/257/225/346/212/245/345/221/212/.gitkeep +0 -0
  581. package/templates/scaffold/wiki//347/224/250/346/210/267/345/212/237/350/203/275/.gitkeep +0 -0
  582. package/templates/scaffold/wiki//347/237/245/350/257/206/346/262/211/346/267/200/346/200/273/350/247/210.html +230 -0
  583. package/templates/scaffold/wiki//351/241/271/347/233/256/350/247/204/350/214/203/.gitkeep +0 -0
  584. package/templates/scaffold/workflow/INDEX.md +13 -0
  585. package/templates/scaffold/workflow/README.md +67 -0
  586. package/templates/scaffold/workflow/archive/README.md +3 -0
  587. package/templates/scaffold/workflow/delegations.d/.gitkeep +0 -0
  588. package/templates/scaffold/workflow/delegations.md +4 -0
  589. package/templates/scaffold/workflow/effective-policy.md +4 -0
  590. package/templates/scaffold/workflow/evidence/.gitkeep +0 -0
  591. package/templates/scaffold/workflow/gate-cost.md +4 -0
  592. package/templates/scaffold/workflow/gate-metrics.d/.gitkeep +0 -0
  593. package/templates/scaffold/workflow/incidents/_TEMPLATE.md +49 -0
  594. package/templates/scaffold/workflow/intents/_TEMPLATE.md +43 -0
  595. package/templates/scaffold/workflow/metrics.md +4 -0
  596. package/templates/scaffold/workflow/papercuts.d/.gitkeep +0 -0
  597. package/templates/scaffold/workflow/papercuts.md +3 -0
  598. package/templates/scaffold/workflow/plans/_TEMPLATE.md +47 -0
  599. package/templates/scaffold/workflow/regression-checklist.d/.gitkeep +0 -0
  600. package/templates/scaffold/workflow/regression-checklist.md +3 -0
  601. package/templates/scaffold/workflow/research/.gitkeep +0 -0
  602. package/templates/scaffold/workflow/reviews.d/.gitkeep +0 -0
  603. package/templates/scaffold/workflow/specs/_TEMPLATE.md +87 -0
  604. package/templates/scaffold/workflow/verify-evidence/.gitkeep +0 -0
  605. package/templates/scaffold/workflow/verify-evidence.md +4 -0
  606. package/LICENSE +0 -21
  607. package/modules/gates/README.md +0 -25
  608. package/modules/gates/dotnet-ca/README.md +0 -24
  609. package/modules/gates/dotnet-ca/check-architecture.sh +0 -150
  610. package/modules/gates/dotnet-ca/local-pre-commit.line +0 -1
  611. package/modules/gates/generated-readonly/README.md +0 -13
  612. package/modules/gates/generated-readonly/check-generated.mjs +0 -61
  613. package/modules/gates/generated-readonly/local-pre-commit.line +0 -1
  614. package/modules/gates/node-layer/README.md +0 -9
  615. package/modules/gates/node-layer/check-node-layer.mjs +0 -29
  616. package/modules/gates/node-layer/local-pre-commit.line +0 -1
  617. package/modules/gates/py-import/README.md +0 -9
  618. package/modules/gates/py-import/check-py-import.mjs +0 -30
  619. package/modules/gates/py-import/local-pre-commit.line +0 -1
  620. package/modules/gates/test-protect/README.md +0 -23
  621. package/modules/gates/test-protect/check-test-protect.mjs +0 -64
  622. package/modules/gates/test-protect/local-pre-commit.line +0 -1
  623. package/modules/hosts/README.md +0 -57
  624. package/modules/hosts/claude/agents/implementer.md +0 -51
  625. package/modules/hosts/claude/agents/independent-reviewer.md +0 -49
  626. package/modules/hosts/claude/agents/ui-verifier.md +0 -48
  627. package/modules/hosts/claude/commands/wf-build.md +0 -69
  628. package/modules/hosts/claude/commands/wf-deploy.md +0 -58
  629. package/modules/hosts/claude/commands/wf-design.md +0 -49
  630. package/modules/hosts/claude/commands/wf-gate-checklist.md +0 -77
  631. package/modules/hosts/claude/commands/wf-maintain.md +0 -76
  632. package/modules/hosts/claude/commands/wf-new-task.md +0 -102
  633. package/modules/hosts/claude/commands/wf-pipeline-run.md +0 -35
  634. package/modules/hosts/claude/commands/wf-plan.md +0 -40
  635. package/modules/hosts/claude/commands/wf-review.md +0 -49
  636. package/modules/hosts/claude/commands/wf-sync-hosts.md +0 -64
  637. package/modules/hosts/claude/commands/wf-test.md +0 -68
  638. package/modules/hosts/codex/agents/implementer.md +0 -51
  639. package/modules/hosts/codex/agents/independent-reviewer.md +0 -49
  640. package/modules/hosts/codex/agents/ui-verifier.md +0 -48
  641. package/modules/hosts/codex/commands/wf-build.md +0 -69
  642. package/modules/hosts/codex/commands/wf-deploy.md +0 -58
  643. package/modules/hosts/codex/commands/wf-design.md +0 -49
  644. package/modules/hosts/codex/commands/wf-gate-checklist.md +0 -77
  645. package/modules/hosts/codex/commands/wf-maintain.md +0 -76
  646. package/modules/hosts/codex/commands/wf-new-task.md +0 -102
  647. package/modules/hosts/codex/commands/wf-pipeline-run.md +0 -35
  648. package/modules/hosts/codex/commands/wf-plan.md +0 -40
  649. package/modules/hosts/codex/commands/wf-review.md +0 -49
  650. package/modules/hosts/codex/commands/wf-sync-hosts.md +0 -64
  651. package/modules/hosts/codex/commands/wf-test.md +0 -68
  652. package/modules/hosts/codex/skills/flow-kit/SKILL.md +0 -14
  653. package/modules/hosts/cursor/agents/implementer.md +0 -51
  654. package/modules/hosts/cursor/agents/independent-reviewer.md +0 -49
  655. package/modules/hosts/cursor/agents/ui-verifier.md +0 -48
  656. package/modules/hosts/cursor/commands/wf-build.md +0 -69
  657. package/modules/hosts/cursor/commands/wf-deploy.md +0 -58
  658. package/modules/hosts/cursor/commands/wf-design.md +0 -49
  659. package/modules/hosts/cursor/commands/wf-gate-checklist.md +0 -77
  660. package/modules/hosts/cursor/commands/wf-maintain.md +0 -76
  661. package/modules/hosts/cursor/commands/wf-new-task.md +0 -102
  662. package/modules/hosts/cursor/commands/wf-pipeline-run.md +0 -35
  663. package/modules/hosts/cursor/commands/wf-plan.md +0 -40
  664. package/modules/hosts/cursor/commands/wf-review.md +0 -49
  665. package/modules/hosts/cursor/commands/wf-sync-hosts.md +0 -64
  666. package/modules/hosts/cursor/commands/wf-test.md +0 -68
  667. package/modules/hosts/omp/agents/implementer.md +0 -52
  668. package/modules/hosts/omp/agents/independent-reviewer.md +0 -50
  669. package/modules/hosts/omp/agents/ui-verifier.md +0 -49
  670. package/modules/hosts/opencode/agents/implementer.md +0 -59
  671. package/modules/hosts/opencode/agents/independent-reviewer.md +0 -53
  672. package/modules/hosts/opencode/agents/ui-verifier.md +0 -52
  673. package/modules/hosts/opencode/commands/wf-build.md +0 -69
  674. package/modules/hosts/opencode/commands/wf-deploy.md +0 -58
  675. package/modules/hosts/opencode/commands/wf-design.md +0 -49
  676. package/modules/hosts/opencode/commands/wf-gate-checklist.md +0 -77
  677. package/modules/hosts/opencode/commands/wf-maintain.md +0 -76
  678. package/modules/hosts/opencode/commands/wf-new-task.md +0 -102
  679. package/modules/hosts/opencode/commands/wf-pipeline-run.md +0 -35
  680. package/modules/hosts/opencode/commands/wf-plan.md +0 -40
  681. package/modules/hosts/opencode/commands/wf-review.md +0 -49
  682. package/modules/hosts/opencode/commands/wf-sync-hosts.md +0 -64
  683. package/modules/hosts/opencode/commands/wf-test.md +0 -68
  684. package/modules/hosts/trae/agents/implementer.md +0 -52
  685. package/modules/hosts/trae/agents/independent-reviewer.md +0 -51
  686. package/modules/hosts/trae/agents/ui-verifier.md +0 -50
  687. package/modules/hosts/trae/commands/wf-build.md +0 -70
  688. package/modules/hosts/trae/commands/wf-deploy.md +0 -59
  689. package/modules/hosts/trae/commands/wf-design.md +0 -50
  690. package/modules/hosts/trae/commands/wf-gate-checklist.md +0 -78
  691. package/modules/hosts/trae/commands/wf-maintain.md +0 -77
  692. package/modules/hosts/trae/commands/wf-new-task.md +0 -103
  693. package/modules/hosts/trae/commands/wf-pipeline-run.md +0 -36
  694. package/modules/hosts/trae/commands/wf-plan.md +0 -41
  695. package/modules/hosts/trae/commands/wf-review.md +0 -50
  696. package/modules/hosts/trae/commands/wf-sync-hosts.md +0 -65
  697. package/modules/hosts/trae/commands/wf-test.md +0 -69
  698. package/modules/hosts/trae/hooks/post-edit-check.cjs +0 -98
  699. package/modules/hosts/trae/hooks/pre-shell-check.cjs +0 -192
  700. package/modules/hosts/trae/hooks/resolve-shell.cjs +0 -30
  701. package/modules/hosts/trae/hooks.json +0 -29
  702. package/modules/hosts/trae/rules/beautify-input-chinese.md +0 -6
  703. package/modules/hosts/trae/rules/git-commit-message.md +0 -10
  704. package/modules/hosts/trae/rules/project-workflow.md +0 -14
  705. package/modules/hosts/zcode/agents/implementer.md +0 -51
  706. package/modules/hosts/zcode/agents/independent-reviewer.md +0 -49
  707. package/modules/hosts/zcode/agents/ui-verifier.md +0 -48
  708. package/src/add-gate.mjs +0 -116
  709. package/src/add-gate.test.mjs +0 -103
  710. package/src/add-host.mjs +0 -68
  711. package/src/cli.mjs +0 -169
  712. package/src/doctor.mjs +0 -629
  713. package/src/executor.mjs +0 -262
  714. package/src/executor.test.mjs +0 -295
  715. package/src/fresh-init.test.mjs +0 -64
  716. package/src/gate-dotnet-ca.test.mjs +0 -124
  717. package/src/gates.test.mjs +0 -143
  718. package/src/init.mjs +0 -382
  719. package/src/init.test.mjs +0 -163
  720. package/src/instance-ref.test.mjs +0 -62
  721. package/src/jscpd-drift.test.mjs +0 -144
  722. package/src/pack.test.mjs +0 -24
  723. package/src/profiles.mjs +0 -183
  724. package/src/render.mjs +0 -132
  725. package/src/rule-budget.test.mjs +0 -103
  726. package/src/run-tests.mjs +0 -56
  727. package/src/stack-profile.test.mjs +0 -95
  728. package/src/sync-hosts.mjs +0 -257
  729. package/src/sync-hosts.test.mjs +0 -291
  730. package/src/sync.mjs +0 -228
  731. package/src/sync.test.mjs +0 -452
  732. package/src/trae-hooks.test.mjs +0 -127
  733. package/src/validate.mjs +0 -77
  734. package/src/validate.test.mjs +0 -95
  735. package/templates/.gitattributes +0 -17
  736. package/templates/AGENTS.md +0 -57
  737. package/templates/_agents/board/index.html +0 -676
  738. package/templates/_agents/commands/build.md +0 -84
  739. package/templates/_agents/commands/deploy.md +0 -66
  740. package/templates/_agents/commands/design.md +0 -63
  741. package/templates/_agents/commands/gate-checklist.md +0 -91
  742. package/templates/_agents/commands/maintain.md +0 -90
  743. package/templates/_agents/commands/new-task.md +0 -110
  744. package/templates/_agents/commands/pipeline-run.md +0 -43
  745. package/templates/_agents/commands/plan.md +0 -48
  746. package/templates/_agents/commands/review.md +0 -62
  747. package/templates/_agents/commands/sync-hosts.md +0 -77
  748. package/templates/_agents/commands/test.md +0 -87
  749. package/templates/_agents/control-surfaces.txt +0 -53
  750. package/templates/_agents/engine-lock.json +0 -342
  751. package/templates/_agents/hooks/check-index-incremental.mjs +0 -48
  752. package/templates/_agents/hooks/check-pairing-incremental.mjs +0 -66
  753. package/templates/_agents/hooks/check-wiki-ledger.mjs +0 -30
  754. package/templates/_agents/hooks/commit-check.cjs +0 -193
  755. package/templates/_agents/hooks/local-pre-commit +0 -6
  756. package/templates/_agents/lane-surfaces.txt +0 -17
  757. package/templates/_agents/level-hints.txt +0 -30
  758. package/templates/_agents/loop/papercut-debt/DESIGN.md +0 -112
  759. package/templates/_agents/mechanism-registry.md +0 -27
  760. package/templates/_agents/metric-claims.txt +0 -81
  761. package/templates/_agents/notes/engine-internals.md +0 -17
  762. package/templates/_agents/notes/runtime-env.md +0 -18
  763. package/templates/_agents/roles/implementer.md +0 -47
  764. package/templates/_agents/roles/independent-reviewer.md +0 -45
  765. package/templates/_agents/roles/ui-verifier.md +0 -44
  766. package/templates/_agents/rule-budgets.txt +0 -18
  767. package/templates/_agents/scripts/agg-delegations.cjs +0 -302
  768. package/templates/_agents/scripts/agg-delegations.test.mjs +0 -282
  769. package/templates/_agents/scripts/agg-gate-stats.mjs +0 -113
  770. package/templates/_agents/scripts/agg-gate-stats.test.mjs +0 -95
  771. package/templates/_agents/scripts/agg-judgments.mjs +0 -156
  772. package/templates/_agents/scripts/agg-judgments.test.mjs +0 -93
  773. package/templates/_agents/scripts/archive-sweep.mjs +0 -235
  774. package/templates/_agents/scripts/archive-sweep.test.mjs +0 -129
  775. package/templates/_agents/scripts/check-attribution.mjs +0 -37
  776. package/templates/_agents/scripts/check-convergence.mjs +0 -124
  777. package/templates/_agents/scripts/check-engine-integrity.mjs +0 -220
  778. package/templates/_agents/scripts/check-engine-integrity.test.mjs +0 -215
  779. package/templates/_agents/scripts/check-funnel.mjs +0 -56
  780. package/templates/_agents/scripts/check-hygiene.mjs +0 -167
  781. package/templates/_agents/scripts/check-hygiene.test.mjs +0 -182
  782. package/templates/_agents/scripts/check-index-incremental.test.mjs +0 -73
  783. package/templates/_agents/scripts/check-lane-surface.mjs +0 -238
  784. package/templates/_agents/scripts/check-lane-surface.test.mjs +0 -302
  785. package/templates/_agents/scripts/check-ledger-invariant.mjs +0 -527
  786. package/templates/_agents/scripts/check-ledger-invariant.test.mjs +0 -639
  787. package/templates/_agents/scripts/check-loop-rev.test.mjs +0 -336
  788. package/templates/_agents/scripts/check-loop-unit.test.mjs +0 -103
  789. package/templates/_agents/scripts/check-loop.mjs +0 -1958
  790. package/templates/_agents/scripts/check-loop.sh +0 -6
  791. package/templates/_agents/scripts/check-loop.test.mjs +0 -3875
  792. package/templates/_agents/scripts/check-metric-claims.mjs +0 -277
  793. package/templates/_agents/scripts/check-prejudgment.mjs +0 -64
  794. package/templates/_agents/scripts/check-registry.mjs +0 -45
  795. package/templates/_agents/scripts/check-scope.mjs +0 -96
  796. package/templates/_agents/scripts/check-scope.test.mjs +0 -96
  797. package/templates/_agents/scripts/check-scripts-syntax.mjs +0 -61
  798. package/templates/_agents/scripts/check-scripts-syntax.test.mjs +0 -72
  799. package/templates/_agents/scripts/check-spec-verif.mjs +0 -47
  800. package/templates/_agents/scripts/check-stage-index.mjs +0 -40
  801. package/templates/_agents/scripts/check-stage-index.test.mjs +0 -77
  802. package/templates/_agents/scripts/closeout-l1.mjs +0 -217
  803. package/templates/_agents/scripts/closeout-l1.test.mjs +0 -105
  804. package/templates/_agents/scripts/commit-check-trigger.test.mjs +0 -205
  805. package/templates/_agents/scripts/compact-ledgers.mjs +0 -97
  806. package/templates/_agents/scripts/compact-ledgers.test.mjs +0 -162
  807. package/templates/_agents/scripts/confirm-doc.mjs +0 -603
  808. package/templates/_agents/scripts/confirm-doc.test.mjs +0 -1103
  809. package/templates/_agents/scripts/doctor.test.mjs +0 -770
  810. package/templates/_agents/scripts/draft-sediment.mjs +0 -178
  811. package/templates/_agents/scripts/draft-sediment.test.mjs +0 -272
  812. package/templates/_agents/scripts/edit-face-check.mjs +0 -109
  813. package/templates/_agents/scripts/edit-face-check.test.mjs +0 -99
  814. package/templates/_agents/scripts/ensure-board.mjs +0 -185
  815. package/templates/_agents/scripts/fill-intent.mjs +0 -91
  816. package/templates/_agents/scripts/fill-intent.test.mjs +0 -113
  817. package/templates/_agents/scripts/fill-plan.mjs +0 -79
  818. package/templates/_agents/scripts/fill-plan.test.mjs +0 -224
  819. package/templates/_agents/scripts/fill-spec.mjs +0 -70
  820. package/templates/_agents/scripts/fill-spec.test.mjs +0 -266
  821. package/templates/_agents/scripts/fixture-scripts.mjs +0 -153
  822. package/templates/_agents/scripts/fixture-scripts.test.mjs +0 -112
  823. package/templates/_agents/scripts/gate-checklist.mjs +0 -209
  824. package/templates/_agents/scripts/gate-checklist.test.mjs +0 -201
  825. package/templates/_agents/scripts/gate-seg.mjs +0 -54
  826. package/templates/_agents/scripts/gate-seg.test.mjs +0 -112
  827. package/templates/_agents/scripts/gen-effective-policy.mjs +0 -107
  828. package/templates/_agents/scripts/gen-effective-policy.test.mjs +0 -100
  829. package/templates/_agents/scripts/gen-verify-evidence.mjs +0 -107
  830. package/templates/_agents/scripts/gen-verify-evidence.test.mjs +0 -84
  831. package/templates/_agents/scripts/gen-wiki-board.mjs +0 -276
  832. package/templates/_agents/scripts/gen-wiki-board.test.mjs +0 -249
  833. package/templates/_agents/scripts/gen-workflow-dashboard.mjs +0 -251
  834. package/templates/_agents/scripts/gen-workflow-dashboard.test.mjs +0 -188
  835. package/templates/_agents/scripts/gen-workflow-index.mjs +0 -201
  836. package/templates/_agents/scripts/gen-workflow-index.test.mjs +0 -152
  837. package/templates/_agents/scripts/gen-workflow-metrics.mjs +0 -334
  838. package/templates/_agents/scripts/gen-workflow-metrics.test.mjs +0 -224
  839. package/templates/_agents/scripts/git-merge-jsonl.mjs +0 -63
  840. package/templates/_agents/scripts/git-merge-jsonl.test.mjs +0 -125
  841. package/templates/_agents/scripts/judgment-ledger.mjs +0 -65
  842. package/templates/_agents/scripts/judgment-ledger.test.mjs +0 -129
  843. package/templates/_agents/scripts/kb-cache-evict.mjs +0 -50
  844. package/templates/_agents/scripts/kb-cache-evict.test.mjs +0 -104
  845. package/templates/_agents/scripts/kb-search.mjs +0 -413
  846. package/templates/_agents/scripts/kb-search.test.mjs +0 -299
  847. package/templates/_agents/scripts/lane-surface-audit.mjs +0 -143
  848. package/templates/_agents/scripts/lane-surface-audit.test.mjs +0 -93
  849. package/templates/_agents/scripts/ledger-doc-chain.test.mjs +0 -166
  850. package/templates/_agents/scripts/ledger-loader.mjs +0 -108
  851. package/templates/_agents/scripts/ledger-loader.test.mjs +0 -131
  852. package/templates/_agents/scripts/ledger-prune.mjs +0 -96
  853. package/templates/_agents/scripts/ledger-prune.test.mjs +0 -120
  854. package/templates/_agents/scripts/ledger-shards.mjs +0 -344
  855. package/templates/_agents/scripts/ledger-shards.test.mjs +0 -356
  856. package/templates/_agents/scripts/ledger-ts-monotonic.test.mjs +0 -135
  857. package/templates/_agents/scripts/level-criteria.mjs +0 -231
  858. package/templates/_agents/scripts/level-criteria.test.mjs +0 -281
  859. package/templates/_agents/scripts/mechanism-retire-watch.mjs +0 -183
  860. package/templates/_agents/scripts/mechanism-retire-watch.test.mjs +0 -114
  861. package/templates/_agents/scripts/papercut-debt-controller.mjs +0 -141
  862. package/templates/_agents/scripts/papercut-debt-controller.test.mjs +0 -127
  863. package/templates/_agents/scripts/papercut-debt-sensor.mjs +0 -170
  864. package/templates/_agents/scripts/papercut-debt-sensor.test.mjs +0 -146
  865. package/templates/_agents/scripts/pipeline-run-docs.mjs +0 -76
  866. package/templates/_agents/scripts/pipeline-run-run.mjs +0 -63
  867. package/templates/_agents/scripts/pipeline-run.mjs +0 -867
  868. package/templates/_agents/scripts/pipeline-run.test.mjs +0 -517
  869. package/templates/_agents/scripts/policy.mjs +0 -281
  870. package/templates/_agents/scripts/pre-push.test.mjs +0 -105
  871. package/templates/_agents/scripts/resolve-merge-conflicts.mjs +0 -110
  872. package/templates/_agents/scripts/resolve-merge-conflicts.test.mjs +0 -87
  873. package/templates/_agents/scripts/rewrite-file.mjs +0 -133
  874. package/templates/_agents/scripts/rewrite-file.test.mjs +0 -148
  875. package/templates/_agents/scripts/rule-budget.mjs +0 -96
  876. package/templates/_agents/scripts/rule-budget.test.mjs +0 -118
  877. package/templates/_agents/scripts/run-tests.mjs +0 -99
  878. package/templates/_agents/scripts/run-tests.test.mjs +0 -105
  879. package/templates/_agents/scripts/solidify-task.mjs +0 -107
  880. package/templates/_agents/scripts/solidify-task.test.mjs +0 -129
  881. package/templates/_agents/scripts/stage-gates.mjs +0 -343
  882. package/templates/_agents/scripts/stage-gates.test.mjs +0 -105
  883. package/templates/_agents/scripts/trust-mode.mjs +0 -67
  884. package/templates/_agents/scripts/trust-mode.test.mjs +0 -110
  885. package/templates/_agents/scripts/verify-wiki-consistency.mjs +0 -253
  886. package/templates/_agents/scripts/verify-wiki-consistency.test.mjs +0 -123
  887. package/templates/_agents/scripts/verify.mjs +0 -210
  888. package/templates/_agents/scripts/verify.test.mjs +0 -358
  889. package/templates/_agents/scripts/wf-journal.mjs +0 -231
  890. package/templates/_agents/scripts/wf-journal.test.mjs +0 -216
  891. package/templates/_agents/scripts/wiki-search.mjs +0 -19
  892. package/templates/_agents/scripts/wiki-search.test.mjs +0 -57
  893. package/templates/_agents/scripts/workflow-board-server.mjs +0 -393
  894. package/templates/_agents/scripts/workflow-board-server.test.mjs +0 -201
  895. package/templates/_agents/scripts/workflow-enums.mjs +0 -70
  896. package/templates/_agents/scripts/workflow-enums.test.mjs +0 -118
  897. package/templates/_agents/scripts/workflows-check.mjs +0 -260
  898. package/templates/_agents/scripts/workflows-check.test.mjs +0 -278
  899. package/templates/_agents/scripts-test-exempt.txt +0 -16
  900. package/templates/_agents/skills/papercut-fix/SKILL.md +0 -60
  901. package/templates/_agents/skills/papercut-fix/references/response-template.md +0 -81
  902. package/templates/_agents/skills/review-method/SKILL.md +0 -55
  903. package/templates/_agents/skills/verify-ui/SKILL.md +0 -61
  904. package/templates/_agents/skills/wiki/SKILL.md +0 -106
  905. package/templates/_agents/workflow-enums.txt +0 -26
  906. package/templates/_agents/workflow-modules.txt +0 -11
  907. package/templates/_agents/workflows/_TEMPLATE.md +0 -75
  908. package/templates/_agents/workflows/pipeline-closing.md +0 -28
  909. package/templates/_agents/workflows//347/244/272/344/276/213-/345/271/266/350/241/214/345/256/236/347/216/260/350/257/204/345/256/241.md +0 -16
  910. package/templates/_githooks/commit-msg +0 -17
  911. package/templates/_githooks/post-commit +0 -10
  912. package/templates/_githooks/pre-commit +0 -126
  913. package/templates/_githooks/pre-merge-commit +0 -29
  914. package/templates/_githooks/pre-push +0 -136
  915. package/templates/_github/workflows/kit-ci.yml +0 -49
  916. package/templates/wiki/INDEX.md +0 -73
  917. package/templates/wiki/drafts-archive/README.md +0 -3
  918. package/templates/wiki//344/272/247/345/223/201/351/234/200/346/261/202/README.md +0 -3
  919. package/templates/wiki//345/274/200/345/217/221/346/226/271/346/241/210/README.md +0 -3
  920. package/templates/wiki//346/225/260/346/215/256/347/273/264/346/212/244/README.md +0 -3
  921. package/templates/wiki//346/265/213/350/257/225/346/212/245/345/221/212/README.md +0 -3
  922. package/templates/wiki//347/224/250/346/210/267/345/212/237/350/203/275/README.md +0 -3
  923. package/templates/wiki//347/237/245/350/257/206/346/262/211/346/267/200/346/200/273/350/247/210.html +0 -77
  924. package/templates/wiki//351/241/271/347/233/256/350/247/204/350/214/203/README.md +0 -3
  925. package/templates/wiki//351/241/271/347/233/256/350/256/241/345/210/222/README.md +0 -3
  926. package/templates/workflow/README.md +0 -90
  927. package/templates/workflow/delegations.md +0 -29
  928. package/templates/workflow/incidents/_TEMPLATE.md +0 -51
  929. package/templates/workflow/intents/_TEMPLATE.md +0 -46
  930. package/templates/workflow/papercuts.md +0 -9
  931. package/templates/workflow/plans/_TEMPLATE.md +0 -47
  932. package/templates/workflow/regression-checklist.md +0 -23
  933. package/templates/workflow/specs/_TEMPLATE.md +0 -80
  934. /package/templates/{_agents → engine/.agents}/board/marked.min.js +0 -0
  935. /package/templates/{_agents/workflows → engine/.agents/pipelines}/steps//347/244/272/344/276/213-/351/203/250/347/275/262/351/252/214/350/257/201.md" +0 -0
  936. /package/templates/{_agents → engine/.agents}/scripts/git-core.mjs +0 -0
@@ -0,0 +1,1398 @@
1
+ #!/usr/bin/env node
2
+ // dev-tour-model.mjs — 开发全景图数据模型(spec/plan/todo → 步骤 × 文件 × 时序)
3
+ // 单源:workflow 文档正文(步骤 / 任务拆解 / 改动方案 / 系统改动)+ git 工作树(--base 对比)。
4
+ // 零依赖;供 gen-dev-tour.mjs(静态快照)与 workflow-board-server.mjs(/api/tour 实时视图)共用。
5
+ //
6
+ // 设计口径:
7
+ // 1. 文档是「计划面」:步骤顺序、每步做什么、预计改哪些文件,全部从正文解析,不另造一份台账。
8
+ // 2. git 是「实际面」:--base 之后的新增/修改/删除行数、真实文件清单,用于发现「文档外改动」。
9
+ // 3. 两者按文件路径(含相对路径后缀 / basename 兜底)合并;解析不到步骤的文件进「未分步」,
10
+ // 解析不到文档的实际改动进「文档外改动」,不静默丢弃。
11
+ // 4. 时序图不猜业务消息:按每步涉及文件的架构层(前端→API→应用→领域→基础设施→DB)连箭头;
12
+ // 步骤正文若显式写「A → B」仍以文件层为准,保证图与改动面一致。
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+ import { execFileSync } from 'node:child_process';
16
+ import { fileURLToPath } from 'node:url';
17
+
18
+ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
19
+ export const DEFAULT_ROOT = path.resolve(SCRIPT_DIR, '../..');
20
+
21
+ const CODE_ROOTS = ['backend/', 'frontend/', 'tests/', 'db/', 'database/', 'sql/', 'scripts/', 'tools/', 'integration/'];
22
+ const DOC_ROOTS = ['workflow/', 'wiki/', '.agents/'];
23
+ const IGNORE_DIRS = ['node_modules/', 'bin/', 'obj/', '.git/', '.vs/', 'dist/', 'coverage/', '.agents/cache/', '.agents/tmp/'];
24
+ const CODE_EXT = /\.(cs|ts|tsx|js|jsx|mjs|cjs|vue|sql|json|html|css|scss|csproj|sln|yml|yaml|xml|config|props|targets)$/i;
25
+
26
+ const LAYERS = [
27
+ { id: 'frontend', label: '前端', rank: 0, test: '前端' },
28
+ { id: 'api', label: 'API', rank: 1, test: 'API/控制器' },
29
+ { id: 'app', label: '应用层', rank: 2, test: '应用层' },
30
+ { id: 'domain', label: '领域层', rank: 3, test: '领域层' },
31
+ { id: 'infra', label: '基础设施', rank: 4, test: '基础设施' },
32
+ { id: 'db', label: '数据库', rank: 5, test: '数据库' },
33
+ { id: 'test', label: '测试', rank: 6, test: '测试' },
34
+ { id: 'doc', label: '文档', rank: 7, test: '文档' },
35
+ { id: 'other', label: '其他', rank: 8, test: '其他' },
36
+ ];
37
+ const LAYER_BY_ID = new Map(LAYERS.map((l) => [l.id, l]));
38
+
39
+ const STATUS_LABEL = { new: '新增', modified: '修改', deleted: '删除', renamed: '重命名', planned: '计划', unchanged: '未变' };
40
+ const TYPE_LABEL = { intents: 'INTENT', specs: 'SPEC', plans: 'PLAN', incidents: 'INCIDENT' };
41
+
42
+ // ---------------- 通用小工具 ----------------
43
+ export function normPath(p) {
44
+ return String(p || '').replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+/g, '/');
45
+ }
46
+ function stripLineNo(p) {
47
+ return p.replace(/:\d+(?:-\d+)?$/, '').replace(/[,。;、,;]+$/, '');
48
+ }
49
+ function uniq(a) { return [...new Set(a)]; }
50
+ function clamp(s, n) { s = String(s || ''); return s.length > n ? s.slice(0, n - 1) + '…' : s; }
51
+ function rel(root, abs) { return normPath(path.relative(root, abs)); }
52
+ function firstLine(s) { return String(s || '').split(/\r?\n/).map((x) => x.trim()).find(Boolean) || ''; }
53
+
54
+ function isIgnored(p) {
55
+ const s = '/' + normPath(p);
56
+ return IGNORE_DIRS.some((d) => s.includes('/' + d)) || /(^|\/)\.tour\.html$/.test(p);
57
+ }
58
+ function inCodeScope(p) {
59
+ return CODE_ROOTS.some((r) => p.startsWith(r)) && !isIgnored(p);
60
+ }
61
+ function isDocPath(p) {
62
+ return DOC_ROOTS.some((r) => p.startsWith(r)) || /\.md$/i.test(p);
63
+ }
64
+ function looksLikePath(s) {
65
+ if (!s) return false;
66
+ s = stripLineNo(normPath(s));
67
+ if (!s || /\s/.test(s)) return false;
68
+ if (/^[a-z]+:\/\//i.test(s)) return false;
69
+ if (/^(node:|System\.|Microsoft\.)/i.test(s)) return false;
70
+ if (s.includes('*') || s.includes('<') || s.includes('>') || s.startsWith('/')) return false;
71
+ if (s.includes('=') || s.startsWith('-')) return false;
72
+ if (/^[A-Za-z]:\//.test(s)) return false;
73
+ if (isIgnored(s)) return false;
74
+ if (!s.includes('/') && !CODE_EXT.test(s) && !/\.(md|cs|ts|vue|sql)$/i.test(s)) return false;
75
+ if (s.includes('/') && !CODE_EXT.test(s) && !CODE_ROOTS.some((r) => s.startsWith(r)) && !DOC_ROOTS.some((r) => s.startsWith(r))) {
76
+ // 允许 Application/Services/Foo.cs 这类相对路径
77
+ if (!/\/[^/]+\.[A-Za-z0-9]+$/.test(s)) return false;
78
+ }
79
+ return true;
80
+ }
81
+
82
+ // ---------------- 文档解析 ----------------
83
+ export function parseFrontmatter(text) {
84
+ const out = {};
85
+ const src = String(text || '').replace(/^\uFEFF/, '');
86
+ if (!src.startsWith('---')) return out;
87
+ const end = src.indexOf('\n---', 3);
88
+ if (end < 0) return out;
89
+ const head = src.slice(3, end).split(/\r?\n/);
90
+ for (const line of head) {
91
+ const m = line.replace(/\r$/, '').match(/^([^:#]+):\s*(.*)$/);
92
+ if (m) out[m[1].trim()] = m[2].trim();
93
+ }
94
+ return out;
95
+ }
96
+
97
+ export function parseSections(text) {
98
+ const lines = String(text || '').replace(/^\uFEFF/, '').split(/\r?\n/);
99
+ // 跳过 frontmatter 后再按标题切;正文标题只认 ^## / ^###
100
+ const sections = [];
101
+ let cur = null;
102
+ let fence = false;
103
+ // body 做 trim 的同时记录被剥掉的前导空行数:行号 = start + lead + j + 2(j 为 trim 后 body 行下标)
104
+ const sliceBody = (from, to) => {
105
+ const raw = lines.slice(from, to);
106
+ const lead = raw.findIndex((x) => x.trim() !== '');
107
+ return { body: raw.join('\n').trim(), lead: lead < 0 ? raw.length : lead };
108
+ };
109
+ for (let i = 0; i < lines.length; i++) {
110
+ const line = lines[i];
111
+ if (/^\s*```/.test(line)) fence = !fence;
112
+ const m = !fence && line.match(/^(#{2,4})\s+(.+?)\s*$/);
113
+ if (m) {
114
+ if (cur) { cur.end = i; Object.assign(cur, sliceBody(cur.start + 1, i)); sections.push(cur); }
115
+ cur = { level: m[1].length, title: m[2].trim(), start: i, end: lines.length, body: '', lead: 0 };
116
+ }
117
+ }
118
+ if (cur) { cur.end = lines.length; Object.assign(cur, sliceBody(cur.start + 1, lines.length)); sections.push(cur); }
119
+ // frontmatter 内的 `---` 不是标题;上面只匹配 # 开头,安全。
120
+ return sections;
121
+ }
122
+
123
+ export function extractTitle(text) {
124
+ const m = String(text || '').match(/^#\s+(.+?)\s*$/m);
125
+ if (!m) return '';
126
+ return m[1].replace(/^(PLAN|SPEC|INTENT|INCIDENT)\s*[—\-–]\s*/i, '').trim();
127
+ }
128
+
129
+ function sectionByTitle(sections, names) {
130
+ const wanted = names.map((n) => n.replace(/[((].*$/, '').trim());
131
+ return sections.find((s) => {
132
+ const t = s.title.replace(/[((].*$/, '').trim();
133
+ return wanted.includes(t) || wanted.some((w) => s.title.startsWith(w));
134
+ });
135
+ }
136
+ function childSections(sections, parent) {
137
+ const out = [];
138
+ for (const sec of sections) {
139
+ if (sec.start <= parent.start) continue;
140
+ if (sec.level <= parent.level) break;
141
+ if (sec.level === parent.level + 1) out.push(sec);
142
+ }
143
+ return out;
144
+ }
145
+
146
+ export function extractPaths(text, repo = null) {
147
+ const out = new Set();
148
+ const add = (raw) => {
149
+ let s = stripLineNo(String(raw || '').trim());
150
+ s = s.replace(/^[`'"“”‘’((\s]+|[`'"“”‘’())\s]+$/g, '');
151
+ s = normPath(s);
152
+ if (looksLikePath(s)) out.add(s);
153
+ };
154
+ const src = String(text || '');
155
+ // 1) 反引号:文档里最主要的路径书写方式
156
+ for (const m of src.matchAll(/`([^`\n]{2,240})`/g)) {
157
+ add(m[1]);
158
+ }
159
+ // 2) 已知根目录的裸路径(含中文标点边界)
160
+ for (const m of src.matchAll(/(?:^|[\s((::,,;;])((?:backend|frontend|tests|db|database|sql|scripts|tools|integration|wiki|workflow|\.agents)\/[^\s`'"“”‘’,。;、))]+)/g)) {
161
+ add(m[1]);
162
+ }
163
+ // 3) 未反引号的中段路径(如步骤标题里的 pml/index.vue):含 / + 已知代码扩展名 + 能消解到仓库真实文件,
164
+ // 三条件同时满足才收——否则静默丢弃(防 if/else 类杂型 token 噪音,上轮实测 719 个)。
165
+ // 消解复用 resolvePath 但不给上下文(确定性后缀/目录反推/basename 唯一,不打分不猜测)。
166
+ if (repo) {
167
+ for (const m of src.matchAll(/[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+/g)) {
168
+ const cand = normPath(m[0].replace(/\.+$/, ''));
169
+ if (out.has(cand) || !cand.includes('/') || /\/\//.test(cand) || !CODE_EXT.test(cand)) continue;
170
+ if (!repo.set.has(resolvePath(cand, new Set(), repo, []))) continue;
171
+ out.add(cand);
172
+ }
173
+ }
174
+ return [...out];
175
+ }
176
+
177
+ function labelOf(raw) {
178
+ const s = String(raw || '').trim();
179
+ const m = s.match(/^(?:批次\s*)?([A-Za-z]{1,4}[-_]?\d+[A-Za-z]?|[0-9]+[A-Za-z]?(?:\.[0-9]+)?|批次\s*[0-9A-Za-z]+)\s*[::、.\-—·•]?\s*(.*)$/);
180
+ if (!m) return { id: '', title: s };
181
+ return { id: m[1].replace(/\s+/g, ''), title: m[2].trim() || m[1] };
182
+ }
183
+ function sameLabel(a, b) {
184
+ const x = String(a || '').replace(/\s+/g, '').toLowerCase();
185
+ const y = String(b || '').replace(/\s+/g, '').toLowerCase();
186
+ return x && y && (x === y || x === 't' + y || y === 't' + x);
187
+ }
188
+
189
+ // 步骤可带并集标签成员集(任务行 `1. S1+S2:…` → ids=['S1','S2']);块↔步骤按成员交集命中
190
+ function stepLabels(st) {
191
+ return st.ids && st.ids.length ? st.ids : [st.id];
192
+ }
193
+ function blockMatchesStep(b, st) {
194
+ const bids = b.ids && b.ids.length ? b.ids : (b.id ? [b.id] : []);
195
+ return (bids.length && stepLabels(st).some((sid) => bids.some((bid) => sameLabel(bid, sid)))) ||
196
+ (b.title && (b.title.includes(st.title) || st.title.includes(b.title)));
197
+ }
198
+
199
+ function newStep(o) {
200
+ return {
201
+ id: '', index: 0, title: '', summary: '', detail: [], detailPos: [],
202
+ files: [], status: 'todo', source: 'doc', raw: '',
203
+ line: 0,
204
+ ...o,
205
+ };
206
+ }
207
+
208
+ // 从 `## 步骤` 的编号列表提取
209
+ function stepsFromNumberedSection(sec) {
210
+ const steps = [];
211
+ let cur = null;
212
+ for (const [j, line] of sec.body.split(/\r?\n/).entries()) {
213
+ const m = line.match(/^\s*(\d+)\.\s+(.*)$/);
214
+ if (m) {
215
+ if (cur) steps.push(cur);
216
+ let body = m[2].trim();
217
+ let title = body;
218
+ let summary = '';
219
+ const bold = body.match(/^\*\*(.+?)\*\*\s*[::]?\s*(.*)$/);
220
+ if (bold) { title = bold[1].trim(); summary = bold[2].trim(); }
221
+ else {
222
+ const c = body.indexOf(':');
223
+ if (c >= 0) { title = body.slice(0, c).trim(); summary = body.slice(c + 1).trim(); }
224
+ }
225
+ const lb = labelOf(title);
226
+ cur = newStep({
227
+ id: lb.id || `S${m[1]}`,
228
+ title: lb.title || title,
229
+ summary,
230
+ raw: line,
231
+ source: 'steps',
232
+ line: sec.start + sec.lead + j + 2,
233
+ });
234
+ if (lb.id && title.startsWith(lb.id)) cur.title = lb.title;
235
+ } else if (cur) {
236
+ const t = line.trim();
237
+ if (t) { cur.detail.push(t); cur.detailPos.push(sec.start + sec.lead + j + 2); }
238
+ }
239
+ }
240
+ if (cur) steps.push(cur);
241
+ return steps;
242
+ }
243
+
244
+ // 从 `## 任务拆解` 的 checkbox 列表提取
245
+ function stepsFromTaskSection(sec) {
246
+ const steps = [];
247
+ let cur = null;
248
+ for (const [j, line] of sec.body.split(/\r?\n/).entries()) {
249
+ const m = line.match(/^\s*-\s*\[([ xX])\]\s*(?:(\d+)[.、]\s*)?(.*)$/);
250
+ if (m) {
251
+ if (cur) steps.push(cur);
252
+ const done = m[1].toLowerCase() === 'x';
253
+ const raw = m[3].trim();
254
+ // 任务行标签:并集式(`S1+S2:标题` / `S8+S9 收口`)拆成员集;单个(`T1 标题` / `S7:标题`)走 labelOf
255
+ // ——仅认显式编号标签(并集/单个),粗体无编号锚与词面模糊匹配不做:宁可不绑,留失约可见
256
+ const um = raw.match(/^([A-Za-z]{1,4}[-_]?\d+[A-Za-z]?(?:\s*\+\s*[A-Za-z]{1,4}[-_]?\d+[A-Za-z]?)+)(?:\s|[::]\s*)(.*)$/);
257
+ const lb = /^[A-Za-z]{1,4}[-_]?\d+[A-Za-z]?(?:\s|[::])/.test(raw) ? labelOf(raw) : { id: '', title: raw };
258
+ const ids = um ? um[1].replace(/\s+/g, '').split('+') : null;
259
+ cur = newStep({
260
+ id: um ? ids[0] : (lb.id || (m[2] ? 'T' + m[2] : `S${steps.length + 1}`)),
261
+ ids,
262
+ title: um ? um[2].trim() : (lb.title || raw),
263
+ summary: '',
264
+ status: done ? 'done' : 'todo',
265
+ raw: line,
266
+ source: 'tasks',
267
+ line: sec.start + sec.lead + j + 2,
268
+ });
269
+ } else if (cur) {
270
+ const t = line.trim();
271
+ if (t) {
272
+ if (/^-\s*判据[::]/.test(t)) cur.summary = t.replace(/^-\s*判据[::]\s*/, '');
273
+ else { cur.detail.push(t); cur.detailPos.push(sec.start + sec.lead + j + 2); }
274
+ }
275
+ }
276
+ }
277
+ if (cur) steps.push(cur);
278
+ return steps;
279
+ }
280
+
281
+ // 把 `## 改动方案` / `## 改动面` / `## 系统改动清单` 拆成块
282
+ function blocksFromChangeSection(sec, splitFiles = false) {
283
+ const blocks = [];
284
+ let cur = null;
285
+ let last = null; // 最近落盘的块
286
+ let openParent = null; // 连续缩进锚共享的无锚父文件行
287
+ const flush = () => { if (cur) { blocks.push(cur); last = cur; cur = null; } };
288
+ for (const [j, line] of sec.body.split(/\r?\n/).entries()) {
289
+ const h = line.match(/^###\s+(.+)$/);
290
+ const b = line.match(/^\s*-\s+\*\*(.+?)\*\*\s*[::]?\s*(.*)$/);
291
+ const plain = line.match(/^\s*-\s+(.+)$/);
292
+ if (h || b) {
293
+ flush();
294
+ const raw = (h ? h[1] : b[1] + (b[2] ? ':' + b[2] : '')).trim();
295
+ const lb = labelOf(raw);
296
+ const indented = /^\s/.test(line);
297
+ cur = { id: lb.id, title: lb.title || raw, raw, lines: [], line: sec.start + sec.lead + j + 2 };
298
+ // 缩进锚(` - **S1**`)继承无锚父文件行:子锚描述的就是对父文件的改动,全部连续子锚都继承;
299
+ // 父块自身无标签,单独成块只会被重复归集进「改动面(未分步)」
300
+ if (indented && (openParent || (last && !last.id && last.isParentFile))) {
301
+ const parent = openParent || last;
302
+ cur.lines.unshift(...parent.lines);
303
+ parent.dead = true;
304
+ openParent = parent;
305
+ } else {
306
+ openParent = null;
307
+ }
308
+ } else if (plain) {
309
+ const txt = plain[1].trim();
310
+ const paths = extractPaths(txt);
311
+ const topLevel = !/^\s+/.test(line);
312
+ const startsWithPath = /^`[^`]+`/.test(txt);
313
+ // 顶层括注编号标签(`- \`path\`(**S7**):…`)也是块锚;须带路径才提升,纯散文括注不认
314
+ const pm = topLevel && paths.length ? txt.match(/[((]\*\*([A-Za-z]{1,4}[-_]?\d+[A-Za-z]?(?:\s*\+\s*[A-Za-z]{1,4}[-_]?\d+[A-Za-z]?)*)\*\*[))]/) : null;
315
+ if (pm || (splitFiles && topLevel && startsWithPath && cur && cur.lines.length)) flush();
316
+ if (topLevel) openParent = null;
317
+ if (pm) {
318
+ const first = paths[0] || '';
319
+ const ids = pm[1].replace(/\s+/g, '').split('+');
320
+ cur = { id: ids[0], ids: ids.length > 1 ? ids : undefined, title: first ? first.split('/').pop() : '', raw: txt, lines: [txt], line: sec.start + sec.lead + j + 2 };
321
+ continue;
322
+ }
323
+ if (!cur) {
324
+ const first = paths[0] || '';
325
+ cur = { id: '', title: first ? first.split('/').pop() : '', raw: txt, lines: [], line: sec.start + sec.lead + j + 2, isParentFile: topLevel && startsWithPath };
326
+ }
327
+ cur.lines.push(txt);
328
+ } else if (/^\s*\|/.test(line) && line.trim()) {
329
+ // 表格行(`| \`path\` | 说明 |`):路径归入当前块上下文;无块上下文时自成无锚块 → 未分步可见
330
+ const txt = line.trim();
331
+ const paths = extractPaths(txt);
332
+ if (!cur) {
333
+ const first = paths[0] || '';
334
+ cur = { id: '', title: first ? first.split('/').pop() : '', raw: txt, lines: [], line: sec.start + sec.lead + j + 2 };
335
+ }
336
+ cur.lines.push(txt);
337
+ } else if (cur && line.trim()) {
338
+ cur.lines.push(line.trim());
339
+ }
340
+ }
341
+ flush();
342
+ // 只保留有文件或标题的块;dead = 已被子锚继承的无锚父块
343
+ return blocks.filter((b) => !b.dead && (b.lines.length || b.title));
344
+ }
345
+
346
+ function inferLayer(p) {
347
+ const s = normPath(p);
348
+ if (s.startsWith('tests/') || /\.(test|spec)\.[tj]sx?$/i.test(s)) return 'test';
349
+ if (s.startsWith('frontend/') || /\.(vue|css|scss)$/i.test(s)) return 'frontend';
350
+ if (/\/Controllers?\//i.test(s) || s.startsWith('backend/') && /\.API\//i.test(s)) return 'api';
351
+ if (/\/Domain\//i.test(s) || /\/Entities?\//i.test(s)) return 'domain';
352
+ if (/\/Infrastructure\//i.test(s) || /DbContext/i.test(s) || /\/Repositories?\//i.test(s)) return 'infra';
353
+ if (s.startsWith('db/') || s.startsWith('database/') || s.startsWith('sql/') || /\.sql$/i.test(s)) return 'db';
354
+ if (s.startsWith('backend/') || /\/Application\//i.test(s) || /\/Services?\//i.test(s) || /\/Dtos?\//i.test(s)) return 'app';
355
+ if (isDocPath(s) || s.startsWith('.agents/')) return 'doc';
356
+ return 'other';
357
+ }
358
+
359
+ function inferLayersFromText(text) {
360
+ const s = String(text || '');
361
+ const out = [];
362
+ if (/前端|页面|按钮|表单|弹窗|抽屉|vue|store|路由/i.test(s)) out.push('frontend');
363
+ if (/API|控制器|Controller|端点|接口\s*\/|路由参数/i.test(s)) out.push('api');
364
+ if (/应用层|服务|Service|映射|编排|DTO|用例/i.test(s)) out.push('app');
365
+ if (/领域|Domain|实体|Entity|值对象/i.test(s)) out.push('domain');
366
+ if (/仓储|Repository|EF|DbContext|基础设施|Infrastructure/i.test(s)) out.push('infra');
367
+ if (/数据库|SQL|表结构|schema|迁移|字段/i.test(s)) out.push('db');
368
+ if (/测试|用例|单测|集成测试|Test/i.test(s)) out.push('test');
369
+ return [...new Set(out)];
370
+ }
371
+
372
+ function fileRecord(p, o = {}) {
373
+ return {
374
+ path: normPath(p), status: 'planned', badge: STATUS_LABEL.planned,
375
+ additions: 0, deletions: 0, binary: false, test: false, layer: inferLayer(p),
376
+ planned: false, actual: false, steps: [], diff: '', oldPath: '',
377
+ pathGuess: false,
378
+ ...o,
379
+ };
380
+ }
381
+
382
+ const REPO_CACHE = new Map();
383
+ function trackedIndex(root) {
384
+ const hit = REPO_CACHE.get(root);
385
+ if (hit && Date.now() - hit.at < 30000) return hit.index;
386
+ const out = git(['ls-files'], root);
387
+ const files = out ? out.split(/\r?\n/).map((x) => normPath(x.trim())).filter(Boolean) : [];
388
+ const set = new Set(files);
389
+ const byBase = new Map();
390
+ const dirs = new Set();
391
+ for (const p of files) {
392
+ const base = p.split('/').pop();
393
+ if (!byBase.has(base)) byBase.set(base, []);
394
+ byBase.get(base).push(p);
395
+ const parts = p.split('/');
396
+ for (let i = 1; i < parts.length; i++) dirs.add(parts.slice(0, i).join('/'));
397
+ }
398
+ const index = { files, set, byBase, dirs: [...dirs] };
399
+ REPO_CACHE.set(root, { at: Date.now(), index });
400
+ return index;
401
+ }
402
+
403
+ function contextTokens(doc, step) {
404
+ const raw = [
405
+ doc.title, doc.module, step?.title, step?.summary,
406
+ ...(step?.detail || []),
407
+ ].join(' ');
408
+ const tokens = raw.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length >= 3);
409
+ // 同一批文件里已解析出的目录名也是强上下文(如 api/materialCode.ts → materialcode)
410
+ for (const p of step?.files || []) {
411
+ for (const seg of p.split('/')) {
412
+ const t = seg.replace(/\.[A-Za-z0-9]+$/, '').toLowerCase();
413
+ if (t.length >= 4) tokens.push(t);
414
+ }
415
+ }
416
+ return [...new Set(tokens)];
417
+ }
418
+
419
+ export function resolvePath(p, universe, repo, context = [], guesses = null) {
420
+ const s = normPath(p);
421
+ if (!s) return s;
422
+ // 1) 真实仓库文件优先:相对路径先按后缀命中,避免被「文档里的原样路径」抢先
423
+ if (repo?.set.has(s)) return s;
424
+ const rsuffix = s.includes('/') ? [...(repo?.set || [])].find((x) => x.endsWith('/' + s)) : null;
425
+ if (rsuffix) return rsuffix;
426
+ // 2) 绝对根路径才直接信文档原样;相对路径继续找真实前缀
427
+ const rooted = CODE_ROOTS.some((r) => s.startsWith(r)) || DOC_ROOTS.some((r) => s.startsWith(r));
428
+ if (rooted && universe.has(s)) return s;
429
+ if (rooted && s.includes('/')) {
430
+ const us = [...universe].find((x) => x.endsWith('/' + s));
431
+ if (us) return us;
432
+ }
433
+
434
+ const base = s.split('/').pop();
435
+ const dir = s.includes('/') ? s.split('/').slice(0, -1).join('/') : '';
436
+ if (dir && repo) {
437
+ // 3a) 目录本身是已存在目录(候选必须是真实跟踪文件才可返回——反推不验证存在 = 制造假路径,宁可原样未解析)
438
+ const cands = repo.dirs.filter((d) => d === dir || d.endsWith('/' + dir));
439
+ if (cands.length === 1 && repo.set.has(cands[0] + '/' + base)) return cands[0] + '/' + base;
440
+ // 3b) 目录不是完整目录(如 Dtos 下只有子目录):用任意已跟踪文件的路径前缀反推(同样须验证候选真实存在)
441
+ const marker = dir + '/';
442
+ const hits = repo.files.filter((f) => { const i = f.indexOf(marker); return i >= 0 && (i === 0 || f[i - 1] === '/' || f[i - 1] === '.'); });
443
+ if (hits.length) {
444
+ const prefixes = uniq(hits.map((f) => f.slice(0, f.indexOf(marker))));
445
+ if (prefixes.length === 1 && repo.set.has(prefixes[0] + dir + '/' + base)) return prefixes[0] + dir + '/' + base;
446
+ if (prefixes.length > 1 && context.length) {
447
+ const ranked = prefixes.map((p) => ({ p, sc: context.reduce((n, t) => n + (p.toLowerCase().includes(t) ? 1 : 0), 0) }))
448
+ .sort((a, b) => b.sc - a.sc);
449
+ const cand = ranked[0].p + dir + '/' + base;
450
+ if (ranked[0].sc > 0 && ranked[0].sc > (ranked[1]?.sc || 0) && repo.set.has(cand)) {
451
+ // 多候选上下文打分 = 路径猜测(basename 唯一命中不算)——guesses 供 parseHealth.pathGuesses 计量
452
+ if (guesses) guesses.push({ path: s, candidates: prefixes.length });
453
+ return cand;
454
+ }
455
+ }
456
+ }
457
+ // 3c) 从最长后缀逐段回退(同样须验证候选真实存在,否则继续回退直至原样)
458
+ const segs = dir.split('/');
459
+ for (let i = 1; i < segs.length; i++) {
460
+ const tail = segs.slice(i).join('/');
461
+ const c = repo.dirs.filter((d) => d === tail || d.endsWith('/' + tail));
462
+ if (c.length === 1 && repo.set.has(c[0] + '/' + base)) return c[0] + '/' + base;
463
+ }
464
+ }
465
+ // 4) 已合并集合(实际改动 / 文档路径);裸文件名继续走 basename 解析
466
+ const isBare = !s.includes('/');
467
+ if (!isBare && universe.has(s)) return s;
468
+ const usuffix2 = [...universe].find((x) => x.endsWith('/' + s));
469
+ if (usuffix2) return usuffix2;
470
+ // 5) basename 唯一 → 直接命中
471
+ if (!isBare) {
472
+ const uBase = [...universe].filter((x) => x.split('/').pop() === base);
473
+ if (uBase.length === 1) return uBase[0];
474
+ }
475
+ const rBase = repo?.byBase.get(base) || [];
476
+ if (rBase.length === 1) {
477
+ // basename 唯一命中(裸文件名的 P1 机制);带目录的 raw 仅当其目录在仓库里确实存在才兜底,
478
+ // 否则该目录是笔误/未落库路径(如 tmp/perf/),原样返回保持可见,不跨目录冒名顶替
479
+ if (isBare || repo.dirs.some((d) => d === dir || d.endsWith('/' + dir))) return rBase[0];
480
+ }
481
+ // 6) basename 多候选 → 用文档/步骤上下文打分(materialCode 页面下的 detail.vue 等)
482
+ if (rBase.length > 1 && context.length) {
483
+ let best = null; let bestScore = 0; let tie = false;
484
+ for (const c of rBase) {
485
+ const lc = c.toLowerCase();
486
+ let sc = 0;
487
+ for (const t of context) if (lc.includes(t)) sc += t.length >= 8 ? 2 : 1;
488
+ if (sc > bestScore) { best = c; bestScore = sc; tie = false; }
489
+ else if (sc === bestScore && sc > 0) tie = true;
490
+ }
491
+ if (best && !tie) {
492
+ // basename 多候选打分 = 路径猜测;guesses 由 buildTour 聚合进 parseHealth.pathGuesses
493
+ if (guesses) guesses.push({ path: s, candidates: rBase.length });
494
+ return best;
495
+ }
496
+ }
497
+ return s;
498
+ }
499
+
500
+ // ---------------- git 实际面 ----------------
501
+ function git(args, root) {
502
+ try {
503
+ return execFileSync('git', args, {
504
+ cwd: root, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, windowsHide: true,
505
+ stdio: ['ignore', 'pipe', 'pipe'],
506
+ });
507
+ } catch {
508
+ return null;
509
+ }
510
+ }
511
+
512
+ export function collectGit({ root = DEFAULT_ROOT, base = 'HEAD', includeAll = false, includeDocs = false, withDiff = true, maxDiffLines = 160 } = {}) {
513
+ const info = { available: false, base, files: new Map(), basePaths: new Set(), note: '' };
514
+ const top = git(['rev-parse', '--show-toplevel'], root);
515
+ if (!top || normPath(top.trim()) !== normPath(root)) {
516
+ info.note = '不在 git 仓库根或 git 不可用';
517
+ return info;
518
+ }
519
+ const resolved = git(['rev-parse', '--verify', `${base}^{commit}`], root);
520
+ if (!resolved) { info.note = `base 不存在:${base}`; return info; }
521
+ info.available = true;
522
+ info.baseSha = resolved.trim().slice(0, 12);
523
+
524
+ // 合并进行中(MERGE_HEAD 存在):工作树 vs HEAD 的差异 = 被合入分支的整批变更,不是「文档外实际改动」。
525
+ // 2026-11-10 实证:merge 期间 pre-commit 的 dev-tour「默认路径回归」恒多出一层 git 步,合并提交被阻断
526
+ //(多文件合并无法自证——测试读的是活工作树,不是夹具)。合并完成后的下一次采集照常。
527
+ if (git(['rev-parse', '-q', '--verify', 'MERGE_HEAD'], root)) {
528
+ info.note = '合并进行中(MERGE_HEAD):跳过实际改动面采集';
529
+ return info;
530
+ }
531
+
532
+ const tree = git(['ls-tree', '-r', '--name-only', base], root);
533
+ if (tree) for (const l of tree.split(/\r?\n/)) if (l.trim()) info.basePaths.add(normPath(l.trim()));
534
+
535
+ const nameStatus = git(['diff', '--name-status', '-M', base, '--'], root);
536
+ if (nameStatus) {
537
+ for (const l of nameStatus.split(/\r?\n/)) {
538
+ if (!l.trim()) continue;
539
+ const parts = l.split('\t');
540
+ const code = parts[0][0];
541
+ const oldP = normPath(parts[1] || '');
542
+ const newP = normPath(parts[2] || parts[1] || '');
543
+ const p = newP;
544
+ if (!p || isIgnored(p)) continue;
545
+ const status = code === 'A' ? 'new' : code === 'D' ? 'deleted' : code === 'R' ? 'renamed' : 'modified';
546
+ const rec = fileRecord(p, { status, badge: STATUS_LABEL[status], actual: true, oldPath: code === 'R' ? oldP : '' });
547
+ info.files.set(p, rec);
548
+ }
549
+ }
550
+
551
+ const numstat = git(['diff', '--numstat', '-M', base, '--'], root);
552
+ if (numstat) {
553
+ for (const l of numstat.split(/\r?\n/)) {
554
+ if (!l.trim()) continue;
555
+ const parts = l.split('\t');
556
+ if (parts.length < 3) continue;
557
+ const add = parts[0] === '-' ? 0 : Number(parts[0]) || 0;
558
+ const del = parts[1] === '-' ? 0 : Number(parts[1]) || 0;
559
+ let p = parts.slice(2).join('\t');
560
+ if (p.includes('=>')) p = p.split('=>').pop().trim();
561
+ p = normPath(p);
562
+ const rec = info.files.get(p);
563
+ if (rec) { rec.additions = add; rec.deletions = del; rec.binary = parts[0] === '-' && parts[1] === '-'; }
564
+ else if (!isIgnored(p)) info.files.set(p, fileRecord(p, { status: 'modified', badge: STATUS_LABEL.modified, actual: true, additions: add, deletions: del }));
565
+ }
566
+ }
567
+
568
+ // untracked:git diff 看不到,单独枚举
569
+ const untracked = git(['ls-files', '--others', '--exclude-standard'], root);
570
+ if (untracked) {
571
+ for (const l of untracked.split(/\r?\n/)) {
572
+ const p = normPath(l.trim());
573
+ if (!p || isIgnored(p)) continue;
574
+ if (!info.files.has(p)) {
575
+ let lines = 0;
576
+ try { lines = fs.readFileSync(path.join(root, p), 'utf8').split(/\r?\n/).length; } catch { /* 读不到按 0 */ }
577
+ info.files.set(p, fileRecord(p, { status: 'new', badge: STATUS_LABEL.new, actual: true, additions: lines }));
578
+ }
579
+ }
580
+ }
581
+
582
+ // 代码面过滤:默认只留产品路径 + 文档显式登记的文件;--all 放开
583
+ for (const [p, rec] of [...info.files]) {
584
+ if (includeAll) continue;
585
+ if (!inCodeScope(p) && !includeDocs) info.files.delete(p);
586
+ }
587
+
588
+ if (withDiff && info.files.size) {
589
+ let budget = 220 * 1024;
590
+ for (const [p, rec] of info.files) {
591
+ if (rec.status === 'deleted' || budget <= 0) continue;
592
+ let d = git(['diff', '--no-color', '--unified=2', base, '--', p], root) || '';
593
+ if (!d && rec.status === 'new') {
594
+ try {
595
+ const body = fs.readFileSync(path.join(root, p), 'utf8');
596
+ const lines = body.split(/\r?\n/).slice(0, maxDiffLines);
597
+ d = [`diff --git a/${p} b/${p}`, 'new file mode 100644', '--- /dev/null', `+++ b/${p}`, `@@ -0,0 +1,${lines.length} @@`, ...lines.map((x) => '+' + x)].join('\n');
598
+ } catch { d = ''; }
599
+ }
600
+ if (!d) continue;
601
+ const arr = d.split(/\r?\n/);
602
+ const clipped = arr.length > maxDiffLines;
603
+ d = arr.slice(0, maxDiffLines).join('\n') + (clipped ? `\n… 已截断(原 ${arr.length} 行,用 git diff ${base} -- ${p} 查看完整)` : '');
604
+ rec.diff = d;
605
+ budget -= d.length;
606
+ }
607
+ }
608
+ return info;
609
+ }
610
+
611
+ // ---------------- 数据流 ----------------
612
+ // 从 diff 新增行 + 步骤正文静态派生「数据对象 × 文件 × 架构层 × 步骤」:
613
+ // 定义模式识别对象(entity/dto/table)→ 「diff 新增行出现」关联文件、「正文出现 / 文件归属」关联步骤。
614
+ const DF_DEF_PATTERNS = [
615
+ { re: /\b(?:class|record|interface|type)\s+([A-Z]\w{3,})/g, table: false }, // C#/TS 类型定义
616
+ { re: /\bDbSet<([A-Z]\w{3,})>/g, table: false },
617
+ { re: /\bToTable\(\s*["'`]?(\w{3,})/g, table: true },
618
+ { re: /\bCREATE\s+TABLE\s+(?:\[?\w+\]?\s*\.\s*)?\[?(\w{3,})\]?/gi, table: true },
619
+ ];
620
+ const DF_DTO_RE = /(Dto|Request|Response|Payload|ViewModel|Input|Result|Query)$/;
621
+ const DF_NOISE_RE = /(Controller|Service|Handler|Validator|Mapper|Profile|Extension|Configuration|Options|Settings|Exception|Middleware|Test|Tests|Fixture|Helper|Utility|Extensions|Constants|Enum|Client|Repository|Factory|Provider|Fetcher|Composer)$/;
622
+ const DF_NOISE_NAMES = new Set(['Program', 'App', 'Startup']);
623
+ const DF_MAX_OBJECTS = 12;
624
+
625
+ function dfKind(name, table) {
626
+ if (DF_DTO_RE.test(name)) return 'dto';
627
+ return table ? 'table' : 'entity';
628
+ }
629
+ // diff 里「+」开头的新增行(+++ 文件头不算);删除行不参与出现判定
630
+ function dfAddedText(diff) {
631
+ return String(diff || '').split(/\r?\n/).filter((l) => l.startsWith('+') && !l.startsWith('+++')).join('\n');
632
+ }
633
+ function dfDefNames(text) {
634
+ const out = new Set();
635
+ for (const p of DF_DEF_PATTERNS) {
636
+ for (const m of text.matchAll(p.re)) out.add(m[1]);
637
+ }
638
+ return out;
639
+ }
640
+
641
+ function deriveDataflow(files, steps) {
642
+ const addedByPath = new Map(files.map((f) => [f.path, dfAddedText(f.diff)]));
643
+ const defNamesByPath = new Map(files.map((f) => [f.path, dfDefNames(addedByPath.get(f.path))]));
644
+ const stepBody = new Map(steps.map((s) => [s.id, [s.title, s.summary, ...s.detail].join('\n')]));
645
+
646
+ // 1) 识别:定义模式扫 diff 新增行与步骤正文;噪音名丢弃(kind 后缀判定优先于来源)
647
+ const kindByName = new Map();
648
+ const scanDef = (text) => {
649
+ for (const p of DF_DEF_PATTERNS) {
650
+ for (const m of text.matchAll(p.re)) {
651
+ const name = m[1];
652
+ if (DF_NOISE_RE.test(name) || DF_NOISE_NAMES.has(name)) continue;
653
+ if (!kindByName.has(name)) kindByName.set(name, dfKind(name, p.table));
654
+ }
655
+ }
656
+ };
657
+ for (const f of files) scanDef(addedByPath.get(f.path));
658
+ for (const s of steps) scanDef(stepBody.get(s.id));
659
+
660
+ // 2) 关联:diff 新增行命中 → 文件(≥2 个才保留);文件归属 / 正文命中 → 步骤(保步骤顺序)
661
+ const candidates = [];
662
+ for (const [name, kind] of kindByName) {
663
+ const nameRe = new RegExp('\\b' + name + '\\b');
664
+ const hitFiles = files.filter((f) => nameRe.test(addedByPath.get(f.path)));
665
+ if (hitFiles.length < 2) continue;
666
+ hitFiles.sort((a, b) => a.path.localeCompare(b.path));
667
+ const layers = uniq(hitFiles.map((f) => f.layer).filter((l) => LAYER_BY_ID.has(l)))
668
+ .sort((a, b) => LAYER_BY_ID.get(a).rank - LAYER_BY_ID.get(b).rank);
669
+ const stepIds = [];
670
+ for (const s of steps) {
671
+ const viaFile = hitFiles.some((f) => f.steps.includes(s.id));
672
+ if (viaFile || nameRe.test(stepBody.get(s.id))) stepIds.push(s.id);
673
+ }
674
+ // defStep:优先「diff 新增行命中定义模式」的第一个文件所属的首个步骤;都没有则取首个涉及步骤
675
+ const defFile = hitFiles.find((f) => defNamesByPath.get(f.path).has(name));
676
+ const defStep = defFile ? (defFile.steps[0] || '') : (stepIds[0] || '');
677
+ candidates.push({ name, kind, files: hitFiles.map((f) => f.path), layers, steps: stepIds, defStep });
678
+ }
679
+
680
+ // 3) 截取:跨文件数降序、跨层数降序,最多 12 个
681
+ candidates.sort((a, b) => b.files.length - a.files.length || b.layers.length - a.layers.length);
682
+ const objects = candidates.slice(0, DF_MAX_OBJECTS);
683
+
684
+ // 4) 传递边:defStep → 其余涉及步骤
685
+ const flows = [];
686
+ for (const o of objects) {
687
+ if (!o.defStep) continue;
688
+ for (const s of o.steps) {
689
+ if (s === o.defStep) continue;
690
+ flows.push({ object: o.name, from: o.defStep, to: s });
691
+ }
692
+ }
693
+ return { objects, flows };
694
+ }
695
+
696
+ // ---------------- 依赖解析(`## 执行顺序` 子句 + 任务行内「依赖:」) ----------------
697
+ // 只收「声明的依赖边」:执行顺序箭头链本身由看板主链表达,不进 edges;
698
+ // 子句边在自身集合内做传递归约(A→B、B→C 都在时去掉 A→C),弧更少且不失真。
699
+ function deriveDeps(sections, steps) {
700
+ const empty = { edges: [], source: '', unresolved: 0, unresolvedItems: [] };
701
+ const docSteps = steps.filter((s) => s.source !== 'git' && s.source !== 'fallback');
702
+ if (docSteps.length < 2) return empty;
703
+
704
+ // 引用归一:裸数字(按 index)/ Tn / Sn / 标签 id → step.id
705
+ const ref = new Map();
706
+ const reg = (k, id) => { if (k && !ref.has(k)) ref.set(k, id); };
707
+ for (const s of docSteps) {
708
+ reg(String(s.index), s.id);
709
+ reg(String(s.id).toLowerCase(), s.id);
710
+ reg(String(s.id).replace(/^[A-Za-z]+/, ''), s.id);
711
+ }
712
+ const resolveRef = (tok) => {
713
+ const t = String(tok || '').trim().replace(/^[((\s]+|[))\s,。;、,;.::]+$/g, '');
714
+ if (!t) return '';
715
+ return ref.get(t) || ref.get(t.toLowerCase()) || ref.get(t.replace(/^[A-Za-z]+/, '')) || '';
716
+ };
717
+ // token 展开:4/5/6、4~7、T2、全部(= allIds)
718
+ const expand = (expr, allIds) => {
719
+ const e = String(expr || '').replace(/([^)]*)|\([^)]*\)/g, '');
720
+ const out = [];
721
+ for (const raw of e.split(/[\/、,,\s]+|与|和/g)) {
722
+ const p = raw.trim();
723
+ if (!p || /^(?:依赖|独立|先于|必须|见|UI|ui)$/.test(p)) continue;
724
+ if (/^全部|^所有/.test(p)) { out.push(...allIds); continue; }
725
+ const rm = p.match(/^([A-Za-z]{0,4})(\d+)[~\-—–][A-Za-z]{0,4}(\d+)$/);
726
+ if (rm) {
727
+ for (let i = +rm[2]; i <= +rm[3]; i++) { const id = resolveRef(rm[1] + i); if (id) out.push(id); }
728
+ continue;
729
+ }
730
+ const id = resolveRef(p);
731
+ if (id) out.push(id);
732
+ }
733
+ return [...new Set(out)];
734
+ };
735
+
736
+ const edges = new Map();
737
+ const addEdge = (from, to) => { if (from && to && from !== to) edges.set(from + '>' + to, { from, to }); };
738
+ let unresolved = 0;
739
+ const unresolvedItems = []; // 2026-11-10:unresolved 计数保留,另留 {line,title} 定位(parseHealth.issues 消费)
740
+ let source = '';
741
+ const noteUnresolved = (line, title) => {
742
+ unresolved++;
743
+ unresolvedItems.push({ line: line || 0, title: String(title || '').trim() });
744
+ };
745
+
746
+ const parseClauses = (text, lhsFixed, lineNo) => {
747
+ for (const clause of String(text || '').split(/[;;]/)) {
748
+ const c = clause.trim();
749
+ if (!c) continue;
750
+ const m = c.match(/^(.+?)\s*依赖\s*(.+)$/);
751
+ if (!m) {
752
+ // 「T5 先于 T7/T8」= T5 是被依赖方( prose 先于不可解析时静默跳过,不计 unresolved)
753
+ const m2 = c.match(/^(.+?)\s*(?:必须|须)?先于\s*(.+)$/);
754
+ if (!m2) continue; // 「9 独立」等无边子句
755
+ const lhs = expand(m2[1], []);
756
+ const rhs = expand(m2[2], []);
757
+ for (const b of rhs) for (const a of lhs) addEdge(a, b);
758
+ continue;
759
+ }
760
+ const lhs = lhsFixed ? [lhsFixed] : expand(m[1], []);
761
+ // 「依赖全部」= 依赖 LHS 之前的全部步骤(按 index,排除自身与后至步骤)
762
+ const lhsMin = Math.min(...lhs.map((id) => (docSteps.find((s) => s.id === id) || {}).index || Infinity));
763
+ const rhs = expand(m[2], docSteps.filter((s) => !lhs.includes(s.id) && s.index < lhsMin).map((s) => s.id));
764
+ if (!lhs.length || !rhs.length) { noteUnresolved(lineNo, c); continue; }
765
+ for (const b of rhs) for (const a of lhs) addEdge(b, a);
766
+ }
767
+ };
768
+
769
+ // a) `## 执行顺序` 节里的「依赖:」行 / 含依赖子句的括注行(箭头链不产边)
770
+ const orderSec = sectionByTitle(sections, ['执行顺序', '实施顺序']);
771
+ if (orderSec) {
772
+ for (const [j, rawLine] of orderSec.body.split(/\r?\n/).entries()) {
773
+ const line = rawLine.trim().replace(/^[-*>\s]+/, '');
774
+ // 箭头链行(含批注里的「依赖」字样)不产子句边——链本身由看板主链表达
775
+ if (!line || line.includes('→') || line.includes('->') || !line.includes('依赖')) continue;
776
+ if (!source) source = 'exec-order';
777
+ parseClauses(line.replace(/^(?:执行)?依赖\s*[::]\s*/, ''), false, orderSec.start + orderSec.lead + j + 2);
778
+ }
779
+ }
780
+ // b) 任务行内 `- 依赖:T1、T2`(= 本步骤依赖列出的前序)
781
+ for (const s of docSteps) {
782
+ (s.detail || []).forEach((d, i) => {
783
+ const m = String(d).match(/^-?\s*依赖\s*[::]\s*(.+)$/);
784
+ if (!m) return;
785
+ if (!source) source = 'task-rows';
786
+ const rhs = expand(m[1], []);
787
+ if (!rhs.length) { noteUnresolved((s.detailPos || [])[i] || s.line, d); return; }
788
+ for (const b of rhs) addEdge(b, s.id);
789
+ });
790
+ }
791
+ if (!edges.size) return { edges: [], source, unresolved, unresolvedItems };
792
+
793
+ // 传递归约(仅在声明边集合内):去掉可被其他声明路径推出的边
794
+ const adj = new Map();
795
+ for (const e of edges.values()) {
796
+ if (!adj.has(e.from)) adj.set(e.from, []);
797
+ adj.get(e.from).push(e.to);
798
+ }
799
+ const reduced = [];
800
+ for (const e of edges.values()) {
801
+ const seen = new Set([e.from]);
802
+ const stack = [e.from];
803
+ let reachable = false;
804
+ while (stack.length && !reachable) {
805
+ const cur = stack.pop();
806
+ for (const nxt of adj.get(cur) || []) {
807
+ if (cur === e.from && nxt === e.to) continue; // 跳过被检边本身
808
+ if (nxt === e.to) { reachable = true; break; }
809
+ if (!seen.has(nxt)) { seen.add(nxt); stack.push(nxt); }
810
+ }
811
+ }
812
+ if (!reachable) reduced.push(e);
813
+ }
814
+ return { edges: reduced, source, unresolved, unresolvedItems };
815
+ }
816
+
817
+ // 验证证据(2026-11-10):本机台账 .agents/verifications.jsonl 优先,
818
+ // 仓库凭证 workflow/verify-evidence.md 兜底合并(ts+head 去重)。
819
+ // 时间窗 = 文档日期起——关联语义是「任务时间窗内的验证凭证」,非逐条绑定。
820
+ function collectEvidence(root, windowStart) {
821
+ const rows = [];
822
+ const seen = new Set();
823
+ const push = (r) => {
824
+ const key = r.ts + '|' + (r.headShort || '');
825
+ if (!r.ts || seen.has(key)) return;
826
+ seen.add(key);
827
+ rows.push(r);
828
+ };
829
+ try {
830
+ const p = path.join(root, '.agents', 'verifications.jsonl');
831
+ if (fs.existsSync(p)) {
832
+ for (const line of fs.readFileSync(p, 'utf8').split(/\r?\n/)) {
833
+ if (!line.trim()) continue;
834
+ try {
835
+ const j = JSON.parse(line);
836
+ push({
837
+ ts: j.ts, suite: j.suite || '', gates: j.gates ?? null, exitCode: j.exitCode ?? null,
838
+ headShort: j.headShort || '', skipped: (j.scope && j.scope.skipped) || [],
839
+ envSkips: j.envSkips || [], source: 'ledger',
840
+ });
841
+ } catch { /* 坏行跳过 */ }
842
+ }
843
+ }
844
+ } catch { /* 台账读不动不影响主模型 */ }
845
+ try {
846
+ const p = path.join(root, 'workflow', 'verify-evidence.md');
847
+ if (fs.existsSync(p)) {
848
+ for (const line of fs.readFileSync(p, 'utf8').split(/\r?\n/)) {
849
+ const m = line.match(/^\|\s*`([0-9a-f]+)`\s*\|\s*([\dT:.Z+-]+)\s*\|\s*(\d+)\s*\|/);
850
+ if (m) push({ ts: m[2], suite: 'all-gates', gates: +m[3], exitCode: 0, headShort: m[1], skipped: [], envSkips: [], source: 'evidence' });
851
+ }
852
+ }
853
+ } catch { /* 凭证读不动不影响主模型 */ }
854
+ const w0 = windowStart ? Date.parse(windowStart) : NaN;
855
+ const inWindow = isNaN(w0) ? rows : rows.filter((r) => Date.parse(r.ts) >= w0);
856
+ inWindow.sort((a, b) => Date.parse(b.ts) - Date.parse(a.ts));
857
+ return { windowStart: windowStart || '', rows: inWindow.slice(0, 10), totalInWindow: inWindow.length };
858
+ }
859
+
860
+ // ---------------- 组装模型 ----------------
861
+ // 测试缝合(2026-11-10):把注入 payload 归一成 collectGit 返回形状(files 条目缺省走 fileRecord)。
862
+ // 仅供 .agents/tests/dev-tour-model.test.mjs 注入确定性 git 面;buildTour 不传 opts.git 时
863
+ // 仍走原 collectGit 路径,行为不变。
864
+ function injectGitInfo(g = {}) {
865
+ const files = new Map();
866
+ const src = g.files instanceof Map ? g.files : Object.entries(g.files || {});
867
+ for (const [p, v] of src) files.set(normPath(p), fileRecord(p, v || {}));
868
+ return {
869
+ available: g.available !== false, base: g.base || 'HEAD',
870
+ files, basePaths: new Set(g.basePaths || []),
871
+ note: g.note || '', baseSha: g.baseSha || '',
872
+ };
873
+ }
874
+
875
+ export function buildTour(opts = {}) {
876
+ const root = path.resolve(opts.root || DEFAULT_ROOT);
877
+ const docAbs = path.isAbsolute(opts.docPath) ? opts.docPath : path.join(root, opts.docPath);
878
+ const docPath = rel(root, docAbs);
879
+ if (!fs.existsSync(docAbs)) throw new Error(`文档不存在:${docPath}`);
880
+ const text = fs.readFileSync(docAbs, 'utf8');
881
+ const fm = parseFrontmatter(text);
882
+ const sections = parseSections(text);
883
+ const title = (extractTitle(text) || path.basename(docPath, '.md'))
884
+ .replace(/^\d{4}-\d{2}-\d{2}[-—–\s]+/, '')
885
+ .trim() || path.basename(docPath, '.md');
886
+ const typeKey = docPath.split('/')[1] || '';
887
+ const type = TYPE_LABEL[typeKey] || 'DOC';
888
+
889
+ // 仓库跟踪索引提前建好(30s 缓存):步骤标题/正文/表格行的未反引号中段路径需按真实文件消解采集
890
+ const repo = trackedIndex(root);
891
+
892
+ // 1) 步骤:步骤 → 任务拆解 → h3 子标题 → 改动块 → 文件分组
893
+ let steps = [];
894
+ const secTasks = sectionByTitle(sections, ['任务拆解', '任务清单', 'TODO', '待办', '步骤', '实施步骤', '执行步骤']);
895
+ if (secTasks) steps = /-\s*\[[ xX]\]/.test(secTasks.body) ? stepsFromTaskSection(secTasks) : stepsFromNumberedSection(secTasks);
896
+
897
+ const changeSec = sectionByTitle(sections, ['改动方案', '改动面', '系统改动清单', '系统改动', '改动清单', '涉及文件', '改动文件', '文件清单']);
898
+ const changeChildren = changeSec ? childSections(sections, changeSec) : [];
899
+ let blocks = changeSec ? blocksFromChangeSection(changeSec, changeChildren.length === 0) : [];
900
+ // h3 批次子节的表格行:块解析只扫 `##` 节自身正文,够不到 `###` 子节——表格路径归入批次块,
901
+ // 与任务号/批次标签交集命中;无表格行的子节不产块(避免与标题步骤重复归集)
902
+ for (const csec of changeChildren) {
903
+ const rows = csec.body.split(/\r?\n/).map((l) => l.trim()).filter((l) => /^\|/.test(l) && !/^\|[\s:|-]+\|?$/.test(l));
904
+ if (!rows.length) continue;
905
+ const lb = labelOf(csec.title);
906
+ blocks.push({ id: lb.id, title: lb.title || csec.title, raw: csec.title, lines: rows, line: csec.start + 1 });
907
+ }
908
+ // h3 子标题当步骤(若尚未拿到):改动方案的 `### 批次` / 任务拆解的 `### T1 · 标题`
909
+ // (parseSections 把 h3 切成独立节、父节 body 为空,故从 childSections 取)
910
+ if (!steps.length) {
911
+ const h3Parent = secTasks && childSections(sections, secTasks).length ? secTasks : changeSec;
912
+ const h3 = h3Parent ? childSections(sections, h3Parent) : [];
913
+ if (h3.length) {
914
+ steps = h3.map((s, i) => {
915
+ const lb = labelOf(s.title);
916
+ return newStep({
917
+ id: lb.id || `S${i + 1}`, title: lb.title || s.title,
918
+ summary: firstLine(s.body.replace(/[`*_#>]/g, '')).slice(0, 160),
919
+ detail: s.body.split(/\r?\n/).map((x) => x.trim()).filter(Boolean).slice(0, 8),
920
+ source: 'headings',
921
+ });
922
+ });
923
+ }
924
+ }
925
+
926
+ // 步骤还没有文件:从改动方案块按标签合并
927
+ for (const st of steps) {
928
+ const matches = blocks.filter((b) => blockMatchesStep(b, st));
929
+ for (const b of matches) {
930
+ for (const line of b.lines) {
931
+ st.detail.push(line);
932
+ for (const p of extractPaths(line, repo)) if (!st.files.includes(p)) st.files.push(p);
933
+ }
934
+ }
935
+ }
936
+
937
+ // 未被步骤认领的块 → 新步骤;完全无步骤 → 按块/文件生成
938
+ const usedBlocks = new Set();
939
+ for (const st of steps) {
940
+ for (const b of blocks) {
941
+ if (blockMatchesStep(b, st)) usedBlocks.add(b);
942
+ }
943
+ }
944
+ const orphan = [];
945
+ const hadSteps = steps.length > 0;
946
+ for (const b of blocks) {
947
+ if (usedBlocks.has(b)) continue;
948
+ const files = uniq(b.lines.flatMap((l) => extractPaths(l, repo)));
949
+ if (!b.id && !files.length) continue;
950
+ if (hadSteps) { orphan.push(b); continue; }
951
+ const st = newStep({
952
+ id: b.id || `S${steps.length + 1}`,
953
+ title: b.title || (files.length === 1 ? files[0].split('/').pop() : `改动块 ${steps.length + 1}`),
954
+ summary: firstLine(b.lines.join('\n')).slice(0, 160),
955
+ detail: b.lines.slice(0, 12),
956
+ files,
957
+ source: 'blocks',
958
+ line: b.line || 0,
959
+ });
960
+ steps.push(st);
961
+ }
962
+ if (steps.length && orphan.length) {
963
+ const files = uniq(orphan.flatMap((b) => b.lines.flatMap((l) => extractPaths(l, repo))));
964
+ steps.push(newStep({
965
+ id: 'A', title: '改动面(未分步)',
966
+ summary: '任务拆解未覆盖的文件清单,按文档原样归集',
967
+ detail: orphan.flatMap((b) => b.lines).slice(0, 20),
968
+ files, source: 'blocks',
969
+ }));
970
+ }
971
+
972
+ if (!steps.length) {
973
+ // 最后兜底:按文件所属架构层分组,避免 100 个文件 100 个站
974
+ const all = extractPaths(text).filter((p) => !isDocPath(p) || p.startsWith(docPath.split('/')[0] + '/'));
975
+ const groups = new Map();
976
+ for (const p of all) {
977
+ const layer = inferLayer(p);
978
+ if (!groups.has(layer)) groups.set(layer, []);
979
+ groups.get(layer).push(p);
980
+ }
981
+ for (const [layer, files] of groups) {
982
+ steps.push(newStep({
983
+ id: `S${steps.length + 1}`,
984
+ title: LAYER_BY_ID.get(layer)?.label || '改动',
985
+ summary: `${files.length} 个文件`,
986
+ files,
987
+ source: 'layers',
988
+ }));
989
+ }
990
+ }
991
+
992
+ // 步骤无文件:从正文路径补;仍无则保留(时序图按文字推断层)
993
+ for (const st of steps) {
994
+ if (!st.files.length) {
995
+ const body = [st.title, st.summary, ...st.detail].join('\n');
996
+ st.files = extractPaths(body, repo);
997
+ }
998
+ }
999
+
1000
+ // 2) 文档引用路径(计划面)
1001
+ const allDocPaths = extractPaths(text);
1002
+ const changePaths = changeSec ? blocks.flatMap((b) => b.lines.flatMap((l) => extractPaths(l))) : [];
1003
+ const docPaths = uniq(allDocPaths.filter((p) => {
1004
+ if (p === docPath) return false;
1005
+ if (!CODE_EXT.test(p)) return false; // 目录/项目名只做步骤锚点,不进文件树
1006
+ if (inCodeScope(p)) return true;
1007
+ if (changePaths.includes(p)) return true;
1008
+ // 文档/台账只在显式出现在改动方案里时纳入,避免把检索命令、模板路径当改动
1009
+ return false;
1010
+ }));
1011
+
1012
+ // 3) git 实际面(测试缝合:opts.git 提供时跳过 collectGit,直接用注入的 collectGit 同形状 payload)
1013
+ const gitInfo = opts.git ? injectGitInfo(opts.git) : collectGit({
1014
+ root, base: opts.base || 'HEAD', includeAll: !!opts.includeAll,
1015
+ includeDocs: !!opts.includeDocs, withDiff: opts.withDiff !== false,
1016
+ maxDiffLines: opts.maxDiffLines || 160,
1017
+ });
1018
+ const docMeta = { title, module: fm['模块'] || '' };
1019
+
1020
+ // 验证计划里的测试文件补到「测试/验证」步骤:步骤正文常只写判据,测试路径在验证计划里
1021
+ const verifySecEarly = sectionByTitle(sections, ['验证计划', '测试计划']);
1022
+ if (verifySecEarly) {
1023
+ const testPaths = extractPaths(verifySecEarly.body).filter((p) => /(^|\/)tests?\//.test(p) || /\.(test|spec)\.[tj]sx?$/i.test(p));
1024
+ const testStep = steps.find((s) => /测试|验证|Test/i.test(s.title));
1025
+ if (testStep) {
1026
+ for (const p of testPaths) if (!testStep.files.includes(p)) testStep.files.push(p);
1027
+ if (testPaths.length && !testStep.summary) testStep.summary = `${testPaths.length} 个测试文件 / 验证命令`;
1028
+ }
1029
+ }
1030
+
1031
+ const universe = new Set([...docPaths, ...gitInfo.files.keys()]);
1032
+ const files = new Map();
1033
+ const docContext = contextTokens(docMeta);
1034
+ for (const p of docPaths) {
1035
+ const rp = resolvePath(p, universe, repo, docContext);
1036
+ if (!files.has(rp)) files.set(rp, fileRecord(rp, { planned: true, layer: inferLayer(rp) }));
1037
+ }
1038
+ for (const [p, rec] of gitInfo.files) {
1039
+ const rp = resolvePath(p, universe, repo, docContext);
1040
+ const prev = files.get(rp);
1041
+ if (prev) Object.assign(prev, rec, { planned: prev.planned, path: rp });
1042
+ else files.set(rp, fileRecord(rp, { ...rec, path: rp }));
1043
+ }
1044
+
1045
+ // 计划面文件若在 git 实际面里存在但状态 planned,补状态
1046
+ for (const rec of files.values()) {
1047
+ rec.test = rec.layer === 'test' || /(^|\/)tests?\//.test(rec.path) || /\.(test|spec)\.[tj]sx?$/i.test(rec.path);
1048
+ if (rec.actual) rec.badge = STATUS_LABEL[rec.status] || '修改';
1049
+ else if (rec.planned) rec.badge = '计划';
1050
+ }
1051
+
1052
+ // 4) 步骤 ↔ 文件双向绑定
1053
+ const stepFiles = new Map(steps.map((s) => [s.id, new Set()]));
1054
+ const resolvedAway = new Set();
1055
+ const guessed = []; // 路径猜测(多候选打分)→ parseHealth.pathGuesses;basename 唯一命中不算
1056
+ for (const st of steps) {
1057
+ const ctx = contextTokens(docMeta, st);
1058
+ const anchors = extractPaths([st.summary, ...st.detail].join('\n'))
1059
+ .filter((a) => !CODE_EXT.test(a));
1060
+ for (const raw of st.files) {
1061
+ const rawGuesses = [];
1062
+ let rp = resolvePath(raw, universe, repo, ctx, rawGuesses);
1063
+ // 裸文件名(MaterialCodePushService.cs)若仓库里只有一个同名文件,优先落到本步骤其他文件的共同目录
1064
+ if (!raw.includes('/') && (repo.byBase.get(raw) || []).length === 1) {
1065
+ const dirs = st.files.filter((x) => x !== raw).map((x) => x.split('/').slice(0, -1).join('/')).filter(Boolean);
1066
+ const freq = new Map();
1067
+ for (const d of dirs) freq.set(d, (freq.get(d) || 0) + 1);
1068
+ const dom = [...freq.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
1069
+ if (dom && !repo.set.has(dom + '/' + raw) && !files.has(dom + '/' + raw)) {
1070
+ rp = resolvePath(dom + '/' + raw, universe, repo, ctx);
1071
+ if (rp === dom + '/' + raw && CODE_EXT.test(raw)) rp = dom + '/' + raw;
1072
+ }
1073
+ }
1074
+ // 相对新文件(如 Guoyu/GuoyuOptions.cs)落在步骤正文声明的项目目录下:
1075
+ // 仅当拼接结果确实是仓库真实文件才生效;raw 本身已 tracked(rp===raw 即真实命中)不二次拼接,
1076
+ // 否则产生 wiki/.../wiki/.../00-preflight.sql 型翻倍伪路径与跨目录冒名拼接
1077
+ if (rp === raw && !repo.set.has(raw) && !raw.startsWith('.') && !CODE_ROOTS.some((r) => raw.startsWith(r))) {
1078
+ for (const a of anchors) {
1079
+ const cand = a.replace(/\/$/, '') + '/' + raw;
1080
+ if (repo.set.has(cand)) { rp = cand; break; }
1081
+ }
1082
+ }
1083
+ const rec = files.get(rp) || (gitInfo.files.has(rp) ? files.get(rp) : null);
1084
+ if (rec) {
1085
+ rec.steps.push(st.id);
1086
+ stepFiles.get(st.id).add(rp);
1087
+ // 同一文件先以相对路径进了计划面,这里解析到真实路径:删掉无主的相对重复项
1088
+ if (rp !== raw) {
1089
+ resolvedAway.add(raw);
1090
+ const old = files.get(raw);
1091
+ if (old && old.planned && !old.actual && !old.steps.length) files.delete(raw);
1092
+ }
1093
+ if (rawGuesses.length) {
1094
+ rec.pathGuess = true;
1095
+ guessed.push({ path: rp, step: st.id, candidates: rawGuesses[rawGuesses.length - 1].candidates });
1096
+ }
1097
+ } else if (CODE_EXT.test(rp) || repo.set.has(rp)) {
1098
+ const r = fileRecord(rp, { planned: true, layer: inferLayer(rp) });
1099
+ r.steps.push(st.id);
1100
+ files.set(rp, r);
1101
+ stepFiles.get(st.id).add(rp);
1102
+ if (rawGuesses.length) {
1103
+ r.pathGuess = true;
1104
+ guessed.push({ path: rp, step: st.id, candidates: rawGuesses[rawGuesses.length - 1].candidates });
1105
+ }
1106
+ }
1107
+ }
1108
+ st.files = [...stepFiles.get(st.id)];
1109
+ }
1110
+ // 相对路径已解析到真实文件:清理无主的原样重复项(否则会出现在「未分步文件」里)
1111
+ for (const raw of resolvedAway) {
1112
+ const old = files.get(raw);
1113
+ if (old && old.planned && !old.actual) {
1114
+ for (const set of stepFiles.values()) set.delete(raw);
1115
+ files.delete(raw);
1116
+ }
1117
+ }
1118
+ // 全局清理:文档里以相对路径出现、但步骤已解析到真实文件的重复项
1119
+ const resolvedSet = new Set([...stepFiles.values()].flatMap((set) => [...set]));
1120
+ for (const [p, rec] of [...files]) {
1121
+ if (rec.actual || rec.steps.length || resolvedSet.has(p) || !rec.planned) continue;
1122
+ const base = p.split('/').pop();
1123
+ const twin = [...resolvedSet].find((x) => x.endsWith('/' + p)) ||
1124
+ ([...resolvedSet].filter((x) => x.endsWith('/' + base)).length === 1 ? [...resolvedSet].find((x) => x.endsWith('/' + base)) : null);
1125
+ if (twin) files.delete(p);
1126
+ }
1127
+ // 测试文件优先归到「测试/验证」步骤(验证计划里的路径常只到测试项目目录)
1128
+ const testStep = steps.find((x) => /测试|验证|Test/i.test(x.title));
1129
+ if (testStep) {
1130
+ for (const rec of files.values()) {
1131
+ if (rec.test && !rec.steps.length) {
1132
+ rec.steps.push(testStep.id);
1133
+ stepFiles.get(testStep.id).add(rec.path);
1134
+ if (!testStep.files.includes(rec.path)) testStep.files.push(rec.path);
1135
+ }
1136
+ }
1137
+ }
1138
+ // 未绑定步骤的文件:按 basename / 层词 尝试匹配步骤正文。两臂都是启发式 → 打 pathGuess 标(猜测可见)。
1139
+ // basename 臂:正文提到文件名即绑第一个命中(既有语义不动)。
1140
+ // 层词臂:仅当恰一个步骤正文含该层 label 才绑;0 个或 ≥2 个都不绑,留「未分步」可见——
1141
+ // 静默挂到第一个命中步骤(旧行为)比不绑更难发现,故收紧为恰一。
1142
+ for (const rec of files.values()) {
1143
+ if (rec.steps.length) continue;
1144
+ const base = rec.path.split('/').pop();
1145
+ const byBase = steps.find((s) => [s.title, s.summary, ...s.detail].join(' ').includes(base));
1146
+ let hit = byBase;
1147
+ if (!hit && rec.layer !== 'other') {
1148
+ const label = LAYER_BY_ID.get(rec.layer)?.label || '';
1149
+ const hits = label ? steps.filter((s) => [s.title, s.summary, ...s.detail].join(' ').includes(label)) : [];
1150
+ if (hits.length === 1) hit = hits[0];
1151
+ }
1152
+ if (hit) {
1153
+ rec.steps.push(hit.id);
1154
+ stepFiles.get(hit.id).add(rec.path);
1155
+ rec.pathGuess = true;
1156
+ guessed.push({ path: rec.path, step: hit.id, candidates: 1 });
1157
+ }
1158
+ }
1159
+ // 仍未绑定:进入「未分步」步骤;有实际改动但文档没提:进入「文档外改动」
1160
+ const unassigned = [...files.values()].filter((f) => !f.steps.length);
1161
+ const undocumented = unassigned.filter((f) => f.actual);
1162
+ const rest = unassigned.filter((f) => !f.actual);
1163
+ if (undocumented.length) {
1164
+ const st = newStep({
1165
+ id: 'X', title: '文档外实际改动', summary: 'git 工作树里有、plan 改动面未登记——优先核对 scope',
1166
+ detail: ['这些文件不在文档改动清单内,可能是范围漂移,也可能是文档该回填。'],
1167
+ files: undocumented.map((f) => f.path), source: 'git', status: 'warn',
1168
+ });
1169
+ steps.push(st);
1170
+ for (const f of undocumented) { f.steps.push(st.id); stepFiles.set(st.id, (stepFiles.get(st.id) || new Set()).add(f.path)); }
1171
+ }
1172
+ if (rest.length) {
1173
+ const st = newStep({
1174
+ id: 'U', title: '未分步文件', summary: '文档提到了,但无法归入具体步骤',
1175
+ files: rest.map((f) => f.path), source: 'fallback',
1176
+ });
1177
+ steps.push(st);
1178
+ for (const f of rest) { f.steps.push(st.id); stepFiles.set(st.id, (stepFiles.get(st.id) || new Set()).add(f.path)); }
1179
+ }
1180
+
1181
+ for (const st of steps) st.files = uniq([...(stepFiles.get(st.id) || [])]);
1182
+ steps = steps.map((s, i) => ({ ...s, index: i + 1, files: uniq(s.files) }));
1183
+
1184
+ // 解析健康度(2026-11-09):直认(steps/tasks/headings = 标题约定命中)vs 兜底
1185
+ // (blocks/layers/fallback 归集)——文档标题跑偏时静默降级的可观测面;
1186
+ // gen-dev-tour --strict 与模板警告条消费本字段。git 步(文档外实际改动)是实际面
1187
+ // 对账产物,不计入文档解析分母。
1188
+ const DIRECT_SOURCES = new Set(['steps', 'tasks', 'headings']);
1189
+ const parseSrc = steps.filter((s) => s.source !== 'git');
1190
+ const bySource = {};
1191
+ for (const s of parseSrc) bySource[s.source] = (bySource[s.source] || 0) + 1;
1192
+ const guessSeen = new Set();
1193
+ const guessItems = [];
1194
+ for (const g of guessed) {
1195
+ const k = g.path + '>' + g.step;
1196
+ if (guessSeen.has(k)) continue;
1197
+ guessSeen.add(k);
1198
+ guessItems.push(g);
1199
+ }
1200
+ const parseHealth = {
1201
+ total: parseSrc.length,
1202
+ direct: parseSrc.filter((s) => DIRECT_SOURCES.has(s.source)).length,
1203
+ bySource,
1204
+ zeroFileSteps: parseSrc.filter((s) => !s.files.length).length,
1205
+ layersFallback: !parseSrc.some((s) => DIRECT_SOURCES.has(s.source)) && parseSrc.some((s) => s.source === 'layers'),
1206
+ hitRate: 0,
1207
+ pathGuesses: { total: guessItems.length, items: guessItems },
1208
+ };
1209
+ parseHealth.hitRate = parseHealth.total ? parseHealth.direct / parseHealth.total : 0;
1210
+
1211
+ // 5) 统计(产品面为主,文档/工具另计)
1212
+ const fileArr = [...files.values()].sort((a, b) => a.path.localeCompare(b.path));
1213
+ const isProduct = (f) => inCodeScope(f.path) || f.layer === 'test' || (!isDocPath(f.path) && ['frontend', 'api', 'app', 'domain', 'infra', 'db', 'test'].includes(f.layer));
1214
+ const product = fileArr.filter(isProduct);
1215
+ const stat = (arr, k) => arr.filter((f) => f.status === k).length;
1216
+ const stats = {
1217
+ files: product.length,
1218
+ new: stat(product, 'new'),
1219
+ modified: stat(product, 'modified'),
1220
+ deleted: stat(product, 'deleted'),
1221
+ renamed: stat(product, 'renamed'),
1222
+ planned: product.filter((f) => f.planned && !f.actual).length,
1223
+ tests: product.filter((f) => f.test).length,
1224
+ additions: product.reduce((n, f) => n + (f.additions || 0), 0),
1225
+ deletions: product.reduce((n, f) => n + (f.deletions || 0), 0),
1226
+ docs: fileArr.length - product.length,
1227
+ steps: steps.length,
1228
+ };
1229
+
1230
+ // 6) 时序图:每步文件层 → 箭头
1231
+ const participants = LAYERS.filter((l) => fileArr.some((f) => f.layer === l.id) || steps.some((s) => inferLayersFromText(s.title + ' ' + s.summary).includes(l.id)))
1232
+ .filter((l) => l.id !== 'doc' && l.id !== 'other' || fileArr.some((f) => f.layer === l.id));
1233
+ const messages = [];
1234
+ for (const st of steps) {
1235
+ let layers = uniq(st.files.map((p) => files.get(p)?.layer || inferLayer(p)));
1236
+ if (!layers.length) layers = inferLayersFromText(st.title + ' ' + st.summary + ' ' + st.detail.join(' '));
1237
+ layers = layers.filter((l) => LAYER_BY_ID.has(l) && l !== 'doc' && l !== 'other').sort((a, b) => LAYER_BY_ID.get(a).rank - LAYER_BY_ID.get(b).rank);
1238
+ if (!layers.length) layers = ['app'];
1239
+ if (layers.length === 1) {
1240
+ messages.push({ stepId: st.id, from: layers[0], to: layers[0], label: clamp(st.title, 26), self: true });
1241
+ } else {
1242
+ for (let i = 0; i < layers.length - 1; i++) {
1243
+ messages.push({ stepId: st.id, from: layers[i], to: layers[i + 1], label: clamp(st.title, 26) });
1244
+ }
1245
+ }
1246
+ if (layers.includes('test') && layers.length > 1 && layers[0] !== 'test') {
1247
+ messages.push({ stepId: st.id, from: 'test', to: layers[0], label: '验证 ' + clamp(st.title, 20), dashed: true });
1248
+ }
1249
+ }
1250
+
1251
+ // 6b) 数据流:数据对象 × 文件 × 架构层 × 步骤(从 diff 新增行 + 步骤正文静态派生)
1252
+ const dataflow = deriveDataflow(fileArr, steps);
1253
+
1254
+ // 7) 验证计划(流程图末端门)
1255
+ const verifySec = sectionByTitle(sections, ['验证计划', '测试计划']);
1256
+ const gates = verifySec ? verifySec.body.split(/\r?\n/).map((l) => l.trim()).filter((l) => /^-\s+/.test(l)).slice(0, 6).map((l) => l.replace(/^-\s+/, '')) : [];
1257
+
1258
+ // 7b) 声明依赖边(执行顺序「依赖:」子句 / 任务行内依赖;流程图旁路弧数据源)
1259
+ const deps = deriveDeps(sections, steps);
1260
+
1261
+ // 解析失约定位(2026-11-10):issues 从总数下钻到行——advisory,不参与 --strict 拒产判定。
1262
+ const issues = [];
1263
+ for (const st of parseSrc) {
1264
+ if (!st.files.length) issues.push({ line: st.line || 0, title: st.title, kind: 'step-no-files' });
1265
+ }
1266
+ for (const b of orphan) {
1267
+ issues.push({ line: b.line || 0, title: b.raw || b.title, kind: 'block-unmatched' });
1268
+ }
1269
+ for (const u of deps.unresolvedItems || []) {
1270
+ issues.push({ line: u.line, title: u.title, kind: 'dep-unresolved' });
1271
+ }
1272
+ if (parseHealth.layersFallback) {
1273
+ issues.push({ line: 1, title: '未发现结构化步骤标题(## 步骤 / ## 任务拆解 / ### 批次 N)', kind: 'layers-fallback' });
1274
+ }
1275
+ issues.sort((a, b) => a.line - b.line);
1276
+ parseHealth.issues = issues;
1277
+
1278
+ // 8) 关联文档
1279
+ const links = [];
1280
+ // 2026-11-11:`对应入口/spec` 允许中间空格;`../intents|specs/*.md` 是相对链接,
1281
+ // 不在 extractPaths 的仓库根白名单内,需单独兜底,否则「需求」tab 永远不出现。
1282
+ for (const m of text.matchAll(/对应\s*(?:入口|spec|规范|事故)[::]\s*(.+)/gi)) {
1283
+ const line = m[1].trim();
1284
+ for (const p of extractPaths(line)) {
1285
+ if (!links.some((l) => l.path === p)) links.push({ label: p.split('/').pop(), path: p });
1286
+ }
1287
+ const raw = normPath(line.replace(/^[`'"“”‘’((\s]+|[`'"“”‘’())\s]+$/g, '').split(/[\s,。;,;]+/)[0]);
1288
+ if (/\.md$/i.test(raw) && !links.some((l) => l.path === raw)) {
1289
+ links.push({ label: raw.split('/').pop(), path: raw });
1290
+ }
1291
+ }
1292
+
1293
+ const purpose = (() => {
1294
+ const entry = sectionByTitle(sections, ['入口']);
1295
+ const m = entry && entry.body.match(/目标[::]\s*(.+)/);
1296
+ if (m) return m[1].trim();
1297
+ const behavior = sectionByTitle(sections, ['功能行为']);
1298
+ if (behavior) return firstLine(behavior.body.replace(/^[#>*\-\s]+/, ''));
1299
+ return '';
1300
+ })();
1301
+
1302
+ // 9) 需求链路(2026-11-09):跟随「对应入口/spec」链接读上游文档(一层,不递归),
1303
+ // 提取目标与行为/验收要点——看板「需求」视图数据源,补齐 why / what 叙事。
1304
+ const UP_SEC = { INTENT: ['目标', '背景', '需求'], SPEC: ['功能行为', '验收标准', '验收'], INCIDENT: ['现象', '影响', '根因'] };
1305
+ const upstreamType = (p) => (/\/intents?\//.test(p) ? 'INTENT' : /\/specs?\//.test(p) ? 'SPEC' : /\/incidents?\//.test(p) ? 'INCIDENT' : type);
1306
+ const upstream = [];
1307
+ for (const lk of links.slice(0, 3)) {
1308
+ if (!/\.md$/i.test(lk.path)) continue;
1309
+ let abs = path.resolve(path.dirname(docAbs), lk.path);
1310
+ if (!fs.existsSync(abs)) abs = path.resolve(root, lk.path);
1311
+ if (!fs.existsSync(abs)) continue;
1312
+ try {
1313
+ const upText = fs.readFileSync(abs, 'utf8');
1314
+ const upSecs = parseSections(upText);
1315
+ const ut = upstreamType(lk.path);
1316
+ const upPurpose = (() => {
1317
+ const entry = sectionByTitle(upSecs, ['入口']);
1318
+ const m = entry && entry.body.match(/目标[::]\s*(.+)/);
1319
+ if (m) return m[1].trim();
1320
+ // SPEC 的「功能行为」正文常为空,真正的行为在 ### 子节里;用首个子节标题当一句话目标,
1321
+ // 避免 firstLine 落到「验收判据」表格头(| 判据 | 证据 | 状态 |)。
1322
+ if (ut === 'SPEC') {
1323
+ const behavior = sectionByTitle(upSecs, ['功能行为']);
1324
+ const kids = behavior ? childSections(upSecs, behavior) : [];
1325
+ if (kids.length) return clamp(kids[0].title, 120);
1326
+ }
1327
+ for (const name of UP_SEC[ut] || []) {
1328
+ const sec = sectionByTitle(upSecs, [name]);
1329
+ if (sec) {
1330
+ const line = firstLine(sec.body.replace(/^[#>*\-\s]+/, ''));
1331
+ if (line) return clamp(line, 120);
1332
+ }
1333
+ }
1334
+ return clamp(extractTitle(upText) || lk.label, 120);
1335
+ })();
1336
+ const points = [];
1337
+ if (ut === 'SPEC') {
1338
+ // 功能行为子节标题(S1/S2/…)比父节空正文更能说明“要做什么”
1339
+ const behavior = sectionByTitle(upSecs, ['功能行为']);
1340
+ const kids = behavior ? childSections(upSecs, behavior) : [];
1341
+ for (const k of kids) { if (points.length >= 5) break; points.push(k.title); }
1342
+ if (!points.length) {
1343
+ const acc = sectionByTitle(upSecs, ['验收判据', '验收标准', '验收']);
1344
+ for (const raw of String(acc && acc.body || '').split(/\r?\n/)) {
1345
+ const mm = raw.match(/^\|\s*([^|]+?)\s*\|/);
1346
+ const t = mm && mm[1].trim();
1347
+ if (t && !/^-+$/.test(t) && !/^(判据|证据|状态|锚)$/.test(t)) points.push(t);
1348
+ if (points.length >= 5) break;
1349
+ }
1350
+ }
1351
+ }
1352
+ if (!points.length) {
1353
+ for (const name of UP_SEC[ut] || ['功能行为', '目标']) {
1354
+ const sec = sectionByTitle(upSecs, [name]);
1355
+ if (!sec) continue;
1356
+ for (const raw of sec.body.split(/\r?\n/)) {
1357
+ const t = raw.trim();
1358
+ if (/^[-*\d]/.test(t) && t.length > 2) points.push(t.replace(/^[-*]\s*|^\d+[.、]\s*/, ''));
1359
+ if (points.length >= 5) break;
1360
+ }
1361
+ if (points.length) break;
1362
+ }
1363
+ }
1364
+ upstream.push({ path: rel(root, abs), type: ut, title: extractTitle(upText) || lk.label, purpose: upPurpose, points: points.filter((p) => !p.trim().startsWith(upPurpose.replace(/…$/, '').trim())).slice(0, 5) });
1365
+ } catch { /* 上游读不动不影响主模型 */ }
1366
+ }
1367
+
1368
+ return {
1369
+ schema: 2,
1370
+ generatedAt: new Date().toISOString(),
1371
+ rootLabel: path.basename(root),
1372
+ doc: {
1373
+ path: docPath, type, title, status: fm['状态'] || '', level: fm['级别'] || '',
1374
+ module: fm['模块'] || '', date: fm['日期'] || '', purpose,
1375
+ links,
1376
+ upstream,
1377
+ },
1378
+ evidence: collectEvidence(root, fm['日期'] || ''),
1379
+ base: { ref: opts.base || 'HEAD', sha: gitInfo.baseSha || '', available: gitInfo.available, note: gitInfo.note || '' },
1380
+ stats,
1381
+ parseHealth,
1382
+ steps: steps.map((s) => ({
1383
+ id: s.id, index: s.index, title: s.title, summary: s.summary,
1384
+ detail: s.detail.slice(0, 30), files: s.files,
1385
+ status: s.status || 'todo', source: s.source,
1386
+ })),
1387
+ files: fileArr,
1388
+ sequence: { participants: participants.map((p) => ({ id: p.id, label: p.label })), messages },
1389
+ dataflow,
1390
+ deps,
1391
+ gates,
1392
+ note: gitInfo.available ? '' : (gitInfo.note || 'git 数据不可用,仅展示文档计划面'),
1393
+ };
1394
+ }
1395
+
1396
+ export function renderModelJson(model) {
1397
+ return JSON.stringify(model, null, 2);
1398
+ }