@xulthekl/team-flow 0.22.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.agents/skills +1 -0
  3. package/.claude/always/phase-guard.md +3 -0
  4. package/.claude-plugin/marketplace.json +20 -0
  5. package/.claude-plugin/plugin.json +31 -0
  6. package/.codex-plugin/plugin.json +45 -0
  7. package/.codexignore +6 -0
  8. package/.cursor-plugin/marketplace.json +17 -0
  9. package/.cursor-plugin/plugin.json +31 -0
  10. package/.github/copilot-instructions.md +15 -0
  11. package/.github/dependabot.yml +10 -0
  12. package/.github/plugin/marketplace.json +42 -0
  13. package/.github/workflows/ci.yml +98 -0
  14. package/.github/workflows/hol-plugin-scanner.yml +17 -0
  15. package/.opencode/INSTALL.md +61 -0
  16. package/.opencode/plugins/team-flow.js +70 -0
  17. package/AGENTS.md +280 -0
  18. package/CHANGELOG.md +635 -0
  19. package/CONTRIBUTING.md +115 -0
  20. package/GEMINI.md +14 -0
  21. package/HANDOFF.md +219 -0
  22. package/INSTALL.md +810 -0
  23. package/LICENSE +21 -0
  24. package/README.md +185 -0
  25. package/SECURITY.md +37 -0
  26. package/agents/bug-investigator.md +180 -0
  27. package/agents/change-split-auditor.md +257 -0
  28. package/agents/code-reviewer.md +171 -0
  29. package/agents/cross-change-consistency-checker.md +215 -0
  30. package/agents/prd-completeness-reviewer.md +197 -0
  31. package/agents/prototype-builder.md +230 -0
  32. package/agents/prototype-env-scout.md +156 -0
  33. package/agents/prototype-reviewer.md +226 -0
  34. package/assets/icon.svg +6 -0
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.js +5 -0
  37. package/dist/parsing/change-parser.d.ts +12 -0
  38. package/dist/parsing/change-parser.js +54 -0
  39. package/dist/parsing/requirement-blocks.d.ts +31 -0
  40. package/dist/parsing/requirement-blocks.js +192 -0
  41. package/dist/schema/base.d.ts +7 -0
  42. package/dist/schema/base.js +1 -0
  43. package/dist/schema/change.d.ts +25 -0
  44. package/dist/schema/change.js +1 -0
  45. package/dist/schema/index.d.ts +3 -0
  46. package/dist/schema/index.js +1 -0
  47. package/dist/schema/spec.d.ts +11 -0
  48. package/dist/schema/spec.js +1 -0
  49. package/dist/validation/constants.d.ts +42 -0
  50. package/dist/validation/constants.js +42 -0
  51. package/dist/validation/tokenizer.d.ts +17 -0
  52. package/dist/validation/tokenizer.js +173 -0
  53. package/dist/validation/types.d.ts +40 -0
  54. package/dist/validation/types.js +1 -0
  55. package/dist/validation/validator.d.ts +21 -0
  56. package/dist/validation/validator.js +489 -0
  57. package/docs/README_en.md +358 -0
  58. package/docs/artifact-contract.md +96 -0
  59. package/docs/decision-points.md +88 -0
  60. package/docs/e2e-integration-design.md +207 -0
  61. package/docs/examples/README.md +25 -0
  62. package/docs/examples/add-dark-mode/README.md +13 -0
  63. package/docs/examples/add-dark-mode/design.md +49 -0
  64. package/docs/examples/add-dark-mode/execution-contract.md +56 -0
  65. package/docs/examples/add-dark-mode/proposal.md +46 -0
  66. package/docs/examples/add-dark-mode/specs/ui-theme/spec.md +35 -0
  67. package/docs/examples/add-dark-mode/tasks.md +26 -0
  68. package/docs/examples/refactor-auth-boundary/README.md +15 -0
  69. package/docs/examples/refactor-auth-boundary/design.md +41 -0
  70. package/docs/examples/refactor-auth-boundary/execution-contract.md +61 -0
  71. package/docs/examples/refactor-auth-boundary/proposal.md +47 -0
  72. package/docs/examples/refactor-auth-boundary/specs/auth-boundary/spec.md +45 -0
  73. package/docs/examples/refactor-auth-boundary/tasks.md +25 -0
  74. package/docs/plans/2026-07-07-fix-batch-design.md +68 -0
  75. package/docs/plans/2026-07-20-001-refactor-skill-collaboration-protocol-plan.md +128 -0
  76. package/docs/platform-matrix.md +45 -0
  77. package/docs/prototype-design-research.md +206 -0
  78. package/docs/release-checklist.md +96 -0
  79. package/docs/showcase.html +699 -0
  80. package/docs/skill-rename-v0.8.0.md +49 -0
  81. package/docs/state-machine.md +162 -0
  82. package/gemini-extension.json +6 -0
  83. package/hooks/hooks-cursor.json +8 -0
  84. package/hooks/hooks.json +26 -0
  85. package/hooks/pre-tool-use-guard +116 -0
  86. package/hooks/session-start +14 -0
  87. package/llms.txt +51 -0
  88. package/package.json +48 -0
  89. package/plugin.json +27 -0
  90. package/prd/v1/plan.md +443 -0
  91. package/prd/v1/prd.md +580 -0
  92. package/scripts/check-update.mjs +91 -0
  93. package/scripts/check-version-consistency.mjs +345 -0
  94. package/scripts/ensure-branch.mjs +71 -0
  95. package/scripts/get-config +31 -0
  96. package/scripts/guard/checks/artifacts-exist.mjs +36 -0
  97. package/scripts/guard/checks/contract-current.mjs +33 -0
  98. package/scripts/guard/checks/contract-fresh.mjs +17 -0
  99. package/scripts/guard/checks/dp-gate-passed.mjs +35 -0
  100. package/scripts/guard/checks/dp3-approved.mjs +22 -0
  101. package/scripts/guard/checks/execution-plan-ready.mjs +40 -0
  102. package/scripts/guard/checks/execution-reviews-passed.mjs +36 -0
  103. package/scripts/guard/checks/schema-valid.mjs +63 -0
  104. package/scripts/guard/checks/specs-merged.mjs +51 -0
  105. package/scripts/guard/checks/tasks-complete.mjs +31 -0
  106. package/scripts/guard/checks/tests-passing.mjs +31 -0
  107. package/scripts/guard/design-token-guard.mjs +237 -0
  108. package/scripts/guard/guard.mjs +205 -0
  109. package/scripts/infer-workflow.mjs +136 -0
  110. package/scripts/install-amazon-q.mjs +9 -0
  111. package/scripts/install-cline.mjs +9 -0
  112. package/scripts/install-continue.mjs +9 -0
  113. package/scripts/install-cursor.mjs +265 -0
  114. package/scripts/install-git-hooks.mjs +82 -0
  115. package/scripts/install-kiro.mjs +9 -0
  116. package/scripts/install-pi.mjs +9 -0
  117. package/scripts/install-qoder.mjs +9 -0
  118. package/scripts/install-qwen.mjs +9 -0
  119. package/scripts/install-roocode.mjs +9 -0
  120. package/scripts/install-windsurf.mjs +9 -0
  121. package/scripts/install-zcode.mjs +265 -0
  122. package/scripts/lib/cmd-audit.mjs +127 -0
  123. package/scripts/lib/cmd-checkpoint.mjs +118 -0
  124. package/scripts/lib/cmd-config.mjs +88 -0
  125. package/scripts/lib/cmd-doctor.mjs +267 -0
  126. package/scripts/lib/cmd-execution.mjs +206 -0
  127. package/scripts/lib/cmd-handoff.mjs +94 -0
  128. package/scripts/lib/cmd-inject.mjs +310 -0
  129. package/scripts/lib/cmd-install-amazon-q.mjs +11 -0
  130. package/scripts/lib/cmd-install-cline.mjs +11 -0
  131. package/scripts/lib/cmd-install-continue.mjs +11 -0
  132. package/scripts/lib/cmd-install-cursor.mjs +14 -0
  133. package/scripts/lib/cmd-install-kiro.mjs +11 -0
  134. package/scripts/lib/cmd-install-pi.mjs +11 -0
  135. package/scripts/lib/cmd-install-qoder.mjs +11 -0
  136. package/scripts/lib/cmd-install-qwen.mjs +11 -0
  137. package/scripts/lib/cmd-install-roocode.mjs +11 -0
  138. package/scripts/lib/cmd-install-windsurf.mjs +11 -0
  139. package/scripts/lib/cmd-install-workbuddy.mjs +359 -0
  140. package/scripts/lib/cmd-install-zcode.mjs +14 -0
  141. package/scripts/lib/cmd-isolate.mjs +37 -0
  142. package/scripts/lib/cmd-list.mjs +71 -0
  143. package/scripts/lib/cmd-runtime.mjs +85 -0
  144. package/scripts/lib/cmd-solutions.mjs +71 -0
  145. package/scripts/lib/cmd-state.mjs +231 -0
  146. package/scripts/lib/cmd-sync.mjs +102 -0
  147. package/scripts/lib/cmd-validate.mjs +105 -0
  148. package/scripts/lib/cmd-version.mjs +148 -0
  149. package/scripts/lib/config-loader.mjs +107 -0
  150. package/scripts/lib/execution-plan.mjs +492 -0
  151. package/scripts/lib/execution-recommendation.mjs +221 -0
  152. package/scripts/lib/hash.mjs +74 -0
  153. package/scripts/lib/install.mjs +267 -0
  154. package/scripts/lib/platform-runtime-inventory.mjs +27 -0
  155. package/scripts/lib/platforms.mjs +165 -0
  156. package/scripts/lib/prototype-sync.mjs +192 -0
  157. package/scripts/lib/sdd-overlay.mjs +262 -0
  158. package/scripts/lib/shell-quote.mjs +4 -0
  159. package/scripts/lib/solutions-capture.mjs +102 -0
  160. package/scripts/lib/solutions-index-gen.mjs +112 -0
  161. package/scripts/lib/solutions-inject.mjs +77 -0
  162. package/scripts/lib/solutions-promote.mjs +145 -0
  163. package/scripts/lib/spec-paths.mjs +85 -0
  164. package/scripts/lib/state-loader.mjs +154 -0
  165. package/scripts/lint/lint-skills.mjs +149 -0
  166. package/scripts/lint/rules/behavior-consistency.mjs +56 -0
  167. package/scripts/lint/rules/dp-trigger-points.mjs +60 -0
  168. package/scripts/lint/rules/exception-handling.mjs +59 -0
  169. package/scripts/lint/rules/no-contradictory-instructions.mjs +67 -0
  170. package/scripts/lint/rules/no-redundant-checks.mjs +50 -0
  171. package/scripts/lint/rules/token-rules.mjs +122 -0
  172. package/scripts/review-package +44 -0
  173. package/scripts/task-brief +40 -0
  174. package/scripts/team-flow.mjs +164 -0
  175. package/scripts/token-baseline.mjs +158 -0
  176. package/scripts/validate-artifacts +96 -0
  177. package/scripts/verify-marketplace-release.mjs +59 -0
  178. package/skills/architecture-design/SKILL.md +140 -0
  179. package/skills/architecture-design/chapters/ch01-4a-domains.md +27 -0
  180. package/skills/architecture-design/chapters/ch02-change-cascade.md +27 -0
  181. package/skills/architecture-design/chapters/ch03-architecture-outputs.md +25 -0
  182. package/skills/architecture-design/chapters/ch04-entity-to-aggregate.md +27 -0
  183. package/skills/architecture-design/chapters/ch05-cqrs.md +32 -0
  184. package/skills/architecture-design/chapters/ch06-integration.md +54 -0
  185. package/skills/architecture-design/cheatsheet.md +51 -0
  186. package/skills/architecture-design/glossary.md +23 -0
  187. package/skills/architecture-design/patterns.md +32 -0
  188. package/skills/bug-investigator/SKILL.md +77 -0
  189. package/skills/build-executor/SKILL.md +163 -0
  190. package/skills/build-executor/implementer-prompt.md +162 -0
  191. package/skills/build-executor/references/execution-modes.md +86 -0
  192. package/skills/build-executor/task-reviewer-prompt.md +195 -0
  193. package/skills/ce-brainstorm/SKILL.md +415 -0
  194. package/skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
  195. package/skills/ce-brainstorm/references/blindspot-pass.md +70 -0
  196. package/skills/ce-brainstorm/references/brainstorm-sections.md +285 -0
  197. package/skills/ce-brainstorm/references/handoff.md +168 -0
  198. package/skills/ce-brainstorm/references/html-rendering.md +634 -0
  199. package/skills/ce-brainstorm/references/markdown-rendering.md +241 -0
  200. package/skills/ce-brainstorm/references/model-tiers.md +9 -0
  201. package/skills/ce-brainstorm/references/prd-mapping.md +71 -0
  202. package/skills/ce-brainstorm/references/product-pressure-test.md +42 -0
  203. package/skills/ce-brainstorm/references/reasoning-elevation.md +63 -0
  204. package/skills/ce-brainstorm/references/settled-decisions.md +45 -0
  205. package/skills/ce-brainstorm/references/synthesis-summary.md +282 -0
  206. package/skills/ce-brainstorm/references/universal-brainstorming.md +73 -0
  207. package/skills/ce-brainstorm/references/verdict-routing.md +26 -0
  208. package/skills/ce-brainstorm/references/visual-probes.md +158 -0
  209. package/skills/ce-brainstorm/scripts/visual-probe-server.js +418 -0
  210. package/skills/ce-compound/SKILL.md +114 -0
  211. package/skills/ce-compound/assets/resolution-template.md +94 -0
  212. package/skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
  213. package/skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
  214. package/skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
  215. package/skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
  216. package/skills/ce-compound/references/agents/performance-oracle.md +108 -0
  217. package/skills/ce-compound/references/agents/security-sentinel.md +91 -0
  218. package/skills/ce-compound/references/agents/session-historian.md +83 -0
  219. package/skills/ce-compound/references/concepts-vocabulary.md +78 -0
  220. package/skills/ce-compound/references/full-mode-workflow.md +413 -0
  221. package/skills/ce-compound/references/grounding-validation.md +84 -0
  222. package/skills/ce-compound/references/lightweight-mode.md +53 -0
  223. package/skills/ce-compound/references/output-standards.md +182 -0
  224. package/skills/ce-compound/references/promotion-rules.md +38 -0
  225. package/skills/ce-compound/references/schema.yaml +231 -0
  226. package/skills/ce-compound/references/three-tier-index.md +55 -0
  227. package/skills/ce-compound/references/v0.5-upgrade.md +32 -0
  228. package/skills/ce-compound/references/write-flow.md +36 -0
  229. package/skills/ce-compound/references/yaml-schema.md +118 -0
  230. package/skills/ce-compound/scripts/session-history/discover-sessions.sh +130 -0
  231. package/skills/ce-compound/scripts/session-history/extract-errors.py +254 -0
  232. package/skills/ce-compound/scripts/session-history/extract-metadata.py +456 -0
  233. package/skills/ce-compound/scripts/session-history/extract-skeleton.py +575 -0
  234. package/skills/ce-compound/scripts/validate-doc-claims.py +346 -0
  235. package/skills/ce-compound/scripts/validate-frontmatter.py +137 -0
  236. package/skills/ce-ideate/SKILL.md +402 -0
  237. package/skills/ce-ideate/references/agents/issue-intelligence-analyst.md +200 -0
  238. package/skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
  239. package/skills/ce-ideate/references/agents/slack-researcher.md +127 -0
  240. package/skills/ce-ideate/references/agents/web-researcher.md +121 -0
  241. package/skills/ce-ideate/references/divergent-ideation.md +89 -0
  242. package/skills/ce-ideate/references/html-rendering.md +634 -0
  243. package/skills/ce-ideate/references/ideation-sections.md +191 -0
  244. package/skills/ce-ideate/references/markdown-rendering.md +238 -0
  245. package/skills/ce-ideate/references/post-ideation-workflow.md +167 -0
  246. package/skills/ce-ideate/references/universal-ideation.md +107 -0
  247. package/skills/ce-ideate/references/web-research-cache.md +56 -0
  248. package/skills/ce-plan/SKILL.md +137 -0
  249. package/skills/ce-plan/references/agents/agent-native-planning-strategist.md +62 -0
  250. package/skills/ce-plan/references/agents/architecture-strategist.md +46 -0
  251. package/skills/ce-plan/references/agents/best-practices-researcher.md +114 -0
  252. package/skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
  253. package/skills/ce-plan/references/agents/data-migration-reviewer.md +103 -0
  254. package/skills/ce-plan/references/agents/deployment-verification-agent.md +157 -0
  255. package/skills/ce-plan/references/agents/framework-docs-researcher.md +93 -0
  256. package/skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
  257. package/skills/ce-plan/references/agents/learnings-researcher.md +247 -0
  258. package/skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
  259. package/skills/ce-plan/references/agents/performance-oracle.md +108 -0
  260. package/skills/ce-plan/references/agents/repo-research-analyst.md +258 -0
  261. package/skills/ce-plan/references/agents/security-sentinel.md +91 -0
  262. package/skills/ce-plan/references/agents/slack-researcher.md +127 -0
  263. package/skills/ce-plan/references/agents/spec-flow-analyzer.md +80 -0
  264. package/skills/ce-plan/references/agents/web-researcher.md +121 -0
  265. package/skills/ce-plan/references/approach-altitude.md +55 -0
  266. package/skills/ce-plan/references/change-splitting.md +65 -0
  267. package/skills/ce-plan/references/completion-contract.md +33 -0
  268. package/skills/ce-plan/references/core-principles.md +40 -0
  269. package/skills/ce-plan/references/deepening-workflow.md +263 -0
  270. package/skills/ce-plan/references/dependency-graph.md +36 -0
  271. package/skills/ce-plan/references/html-rendering.md +634 -0
  272. package/skills/ce-plan/references/intake-routing.md +189 -0
  273. package/skills/ce-plan/references/markdown-rendering.md +238 -0
  274. package/skills/ce-plan/references/plan-handoff.md +150 -0
  275. package/skills/ce-plan/references/plan-sections.md +427 -0
  276. package/skills/ce-plan/references/plan-structure.md +120 -0
  277. package/skills/ce-plan/references/planning-modes.md +69 -0
  278. package/skills/ce-plan/references/reasoning-elevation.md +63 -0
  279. package/skills/ce-plan/references/research-workflow.md +165 -0
  280. package/skills/ce-plan/references/review-and-write.md +127 -0
  281. package/skills/ce-plan/references/settled-decisions.md +45 -0
  282. package/skills/ce-plan/references/synthesis-summary.md +418 -0
  283. package/skills/ce-plan/references/universal-planning.md +168 -0
  284. package/skills/ce-proof/SKILL.md +346 -0
  285. package/skills/ce-strategy/SKILL.md +97 -0
  286. package/skills/ce-strategy/references/interview.md +143 -0
  287. package/skills/ce-strategy/references/strategy-template.md +89 -0
  288. package/skills/code-reviewer/SKILL.md +84 -0
  289. package/skills/code-reviewer/code-reviewer-prompt.md +195 -0
  290. package/skills/contract-builder/SKILL.md +72 -0
  291. package/skills/design-system/SKILL.md +75 -0
  292. package/skills/design-system/references/agents/design-system-architect.md +138 -0
  293. package/skills/design-system/references/creation-flow.md +170 -0
  294. package/skills/design-system/references/preview-template.html +319 -0
  295. package/skills/design-system/references/token-derivation.md +115 -0
  296. package/skills/design-system/references/variant-schema.md +60 -0
  297. package/skills/e2e/SKILL.md +56 -0
  298. package/skills/e2e/references/test-verifier.md +40 -0
  299. package/skills/need-explorer/SKILL.md +79 -0
  300. package/skills/prototype/SKILL.md +98 -0
  301. package/skills/prototype/references/agents/design-system-architect.md +122 -0
  302. package/skills/prototype/references/checklist.md +82 -0
  303. package/skills/prototype/references/craft/accessibility-baseline.md +71 -0
  304. package/skills/prototype/references/craft/anti-ai-slop.md +87 -0
  305. package/skills/prototype/references/craft/laws-of-ux.md +60 -0
  306. package/skills/prototype/references/craft/state-coverage.md +81 -0
  307. package/skills/prototype/references/craft/typography-hierarchy.md +50 -0
  308. package/skills/prototype/references/layouts.md +223 -0
  309. package/skills/prototype/references/orchestration-flow.md +101 -0
  310. package/skills/prototype/references/prototype-scaffold/assets/design-tokens.css +19 -0
  311. package/skills/prototype/references/prototype-scaffold/components/button.html +3 -0
  312. package/skills/prototype/references/prototype-scaffold/design-system.md +7 -0
  313. package/skills/prototype/references/prototype-scaffold/flow.md +11 -0
  314. package/skills/prototype/references/prototype-scaffold/index.html +20 -0
  315. package/skills/prototype/references/prototype-scaffold/pages/index.html +11 -0
  316. package/skills/prototype/references/template.html +396 -0
  317. package/skills/release-archivist/SKILL.md +166 -0
  318. package/skills/release-archivist/references/closing-procedures.md +81 -0
  319. package/skills/session-handoff/SKILL.md +110 -0
  320. package/skills/session-handoff/references/context-extraction.md +70 -0
  321. package/skills/session-handoff/references/handoff-template.md +90 -0
  322. package/skills/session-handoff/references/skill-recommendation.md +43 -0
  323. package/skills/spec-merger/SKILL.md +66 -0
  324. package/skills/spec-writer/SKILL.md +149 -0
  325. package/skills/workflow-bootstrap/SKILL.md +148 -0
  326. package/skills/workflow-bootstrap/references/agents/codebase-recon-analyst.md +121 -0
  327. package/skills/workflow-bootstrap/references/b1-reconnaissance.md +80 -0
  328. package/skills/workflow-bootstrap/scripts/recon-probe.sh +328 -0
  329. package/skills/workflow-feedback/SKILL.md +120 -0
  330. package/skills/workflow-feedback/references/category-guide.md +121 -0
  331. package/skills/workflow-feedback/references/feedback-template.md +73 -0
  332. package/skills/workflow-feedback/references/review-mode.md +92 -0
  333. package/skills/workflow-orchestrator/SKILL.md +94 -0
  334. package/skills/workflow-orchestrator/references/feedback-loops.md +56 -0
  335. package/skills/workflow-orchestrator/references/s1-path-router.md +81 -0
  336. package/skills/workflow-orchestrator/references/s2-prd-prototype-loop.md +96 -0
  337. package/skills/workflow-orchestrator/references/s3-plan-pipeline.md +50 -0
  338. package/skills/workflow-orchestrator/references/s4-split-validate.md +108 -0
  339. package/skills/workflow-orchestrator/references/s5-monitoring.md +61 -0
  340. package/skills/workflow-orchestrator/references/state-model.md +185 -0
  341. package/skills/workflow-start/SKILL.md +147 -0
  342. package/skills/workflow-start/references/routing-rules.md +155 -0
  343. package/src/index.ts +16 -0
  344. package/src/parsing/change-parser.ts +79 -0
  345. package/src/parsing/requirement-blocks.ts +244 -0
  346. package/src/schema/base.ts +8 -0
  347. package/src/schema/change.ts +29 -0
  348. package/src/schema/index.ts +3 -0
  349. package/src/schema/spec.ts +12 -0
  350. package/src/validation/constants.ts +53 -0
  351. package/src/validation/tokenizer.ts +180 -0
  352. package/src/validation/types.ts +47 -0
  353. package/src/validation/validator.ts +552 -0
  354. package/templates/abandonment-summary.md +29 -0
  355. package/templates/design.md +37 -0
  356. package/templates/execution-contract.md +99 -0
  357. package/templates/plan.md +86 -0
  358. package/templates/prd.md +468 -0
  359. package/templates/proposal.md +37 -0
  360. package/templates/spec.md +31 -0
  361. package/templates/tasks.md +47 -0
  362. package/tests/e2e.test.mjs +266 -0
  363. package/tests/lib/cmd-audit.test.mjs +202 -0
  364. package/tests/lib/cmd-checkpoint.test.mjs +98 -0
  365. package/tests/lib/cmd-config.test.mjs +101 -0
  366. package/tests/lib/cmd-doctor.test.mjs +450 -0
  367. package/tests/lib/cmd-execution.test.mjs +647 -0
  368. package/tests/lib/cmd-handoff.test.mjs +137 -0
  369. package/tests/lib/cmd-inject.test.mjs +218 -0
  370. package/tests/lib/cmd-install-workbuddy.test.mjs +113 -0
  371. package/tests/lib/cmd-install-zcode.test.mjs +42 -0
  372. package/tests/lib/cmd-isolate.test.mjs +68 -0
  373. package/tests/lib/cmd-list.test.mjs +127 -0
  374. package/tests/lib/cmd-runtime.test.mjs +83 -0
  375. package/tests/lib/cmd-state-missing.test.mjs +65 -0
  376. package/tests/lib/cmd-state.test.mjs +365 -0
  377. package/tests/lib/cmd-sync-paths.test.mjs +84 -0
  378. package/tests/lib/cmd-validate-paths.test.mjs +90 -0
  379. package/tests/lib/config-loader.test.mjs +175 -0
  380. package/tests/lib/ensure-branch.test.mjs +59 -0
  381. package/tests/lib/execution-control-plane.test.mjs +168 -0
  382. package/tests/lib/execution-plan.test.mjs +474 -0
  383. package/tests/lib/guard-specs-merged.test.mjs +108 -0
  384. package/tests/lib/guard-tests-passing.test.mjs +112 -0
  385. package/tests/lib/guard-transitions.test.mjs +262 -0
  386. package/tests/lib/guard.test.mjs +611 -0
  387. package/tests/lib/hash.test.mjs +138 -0
  388. package/tests/lib/infer-workflow.test.mjs +161 -0
  389. package/tests/lib/install-git-hooks.test.mjs +20 -0
  390. package/tests/lib/marketplace-release-docs.test.mjs +31 -0
  391. package/tests/lib/minimality-discipline.test.mjs +37 -0
  392. package/tests/lib/model-profiles-docs.test.mjs +33 -0
  393. package/tests/lib/node20-compatibility.test.mjs +73 -0
  394. package/tests/lib/node20-test-entry.test.mjs +17 -0
  395. package/tests/lib/platform-runtime-distribution.test.mjs +150 -0
  396. package/tests/lib/raw-mode-smoke.test.mjs +42 -0
  397. package/tests/lib/sdd-overlay.test.mjs +118 -0
  398. package/tests/lib/spec-paths.test.mjs +80 -0
  399. package/tests/lib/state-loader.test.mjs +251 -0
  400. package/tests/lib/token-baseline.test.mjs +72 -0
  401. package/tests/lib/token-rules.test.mjs +94 -0
  402. package/tests/lib/verify-marketplace-release.test.mjs +128 -0
  403. package/tests/tsconfig.json +12 -0
  404. package/token-baseline.json +95 -0
  405. package/tsconfig.json +14 -0
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * solutions-capture — 写入一条经验到 INDEX.md + 对应目录
4
+ *
5
+ * v0.5 复利贯穿机制脚本
6
+ * 用法:tf solutions capture --phase <p> --domain <d> --type <t> --severity <s> --summary "<text>"
7
+ *
8
+ * 功能:
9
+ * 1. 在对应阶段目录下创建经验文件
10
+ * 2. 在 INDEX.md 中追加一行摘要
11
+ * 3. INDEX.md 超过 150 行时触发 index-gen 重建
12
+ */
13
+
14
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from 'node:fs';
15
+ import { join } from 'node:path';
16
+
17
+ const PHASES = ['prd', 'plan', 'prototype', 'spec', 'build', 'review', 'cross-phase'];
18
+ const MAX_INDEX_LINES = 150;
19
+
20
+ function slugify(text) {
21
+ return text
22
+ .toLowerCase()
23
+ .replace(/[^a-z0-9一-鿿]+/g, '-')
24
+ .replace(/^-|-$/g, '')
25
+ .slice(0, 40);
26
+ }
27
+
28
+ export function run(args = {}) {
29
+ const phase = args.phase || 'cross-phase';
30
+ const domain = args.domain || 'general';
31
+ const type = args.type || 'insight';
32
+ const severity = args.severity || 'medium';
33
+ const summary = args.summary || '(no summary)';
34
+ const source = args.source || '';
35
+ const dir = args.dir || 'docs/solutions';
36
+
37
+ if (!PHASES.includes(phase)) {
38
+ console.error(`Invalid phase: ${phase}. Valid: ${PHASES.join(', ')}`);
39
+ process.exit(1);
40
+ }
41
+
42
+ // 确保目录存在
43
+ const phaseDir = join(dir, phase);
44
+ if (!existsSync(phaseDir)) mkdirSync(phaseDir, { recursive: true });
45
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
46
+
47
+ // 生成文件名
48
+ const date = new Date().toISOString().slice(0, 10);
49
+ const slug = slugify(summary);
50
+ const fileName = `${date}-${slug}.md`;
51
+ const filePath = join(phaseDir, fileName);
52
+
53
+ // 写经验文件
54
+ const content = `---
55
+ phase: ${phase}
56
+ domain: ${domain}
57
+ type: ${type}
58
+ severity: ${severity}
59
+ date: ${date}
60
+ source: ${source}
61
+ ---
62
+
63
+ ## 问题描述
64
+ ${summary}
65
+
66
+ ## 根因/模式
67
+ (待补充)
68
+
69
+ ## 预防措施/应用方式
70
+ (待补充)
71
+ `;
72
+ writeFileSync(filePath, content, 'utf-8');
73
+
74
+ // 追加 INDEX.md
75
+ const indexPath = join(dir, 'INDEX.md');
76
+ if (!existsSync(indexPath)) {
77
+ writeFileSync(indexPath, '# Solutions Index\n<!-- 每条一行,按 severity 降序,≤150 行硬上限 -->\n| date | phase | domain | type | severity | summary | file |\n|------|-------|--------|------|----------|---------|------|\n', 'utf-8');
78
+ }
79
+ appendFileSync(indexPath, `| ${date} | ${phase} | ${domain} | ${type} | ${severity} | ${summary.slice(0, 80)} | ${phase}/${fileName} |\n`, 'utf-8');
80
+
81
+ // 检查是否超过 150 行
82
+ const indexContent = readFileSync(indexPath, 'utf-8');
83
+ const dataLines = indexContent.split('\n').filter(l => l.startsWith('|') && !l.startsWith('| date') && !l.startsWith('|--'));
84
+ if (dataLines.length > MAX_INDEX_LINES) {
85
+ console.log(`INDEX.md exceeded ${MAX_INDEX_LINES} lines, consider running: tf solutions index-gen`);
86
+ }
87
+
88
+ console.log(`Captured: ${phase}/${fileName} (${severity}/${type})`);
89
+ return { file: `${phase}/${fileName}`, phase, domain, type, severity };
90
+ }
91
+
92
+ if (process.argv[1]?.includes('solutions-capture')) {
93
+ const args = {};
94
+ for (let i = 2; i < process.argv.length; i++) {
95
+ if (process.argv[i].startsWith('--')) {
96
+ const key = process.argv[i].replace('--', '');
97
+ const val = process.argv[i + 1] && !process.argv[i + 1].startsWith('--') ? process.argv[++i] : true;
98
+ args[key] = val;
99
+ }
100
+ }
101
+ run(args);
102
+ }
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * solutions-index-gen — 扫描 docs/solutions/<phase>/ 各阶段目录,重建 INDEX.md
4
+ *
5
+ * v0.5 复利贯穿机制脚本
6
+ * 用法:tf solutions index-gen [--dir <solutions-dir>]
7
+ *
8
+ * 功能:
9
+ * 1. 扫描 docs/solutions/ 下所有子目录中的 .md 文件
10
+ * 2. 解析每个文件的 frontmatter(phase/domain/type/severity/date/summary)
11
+ * 3. 按 severity 降序、date 降序排列
12
+ * 4. 重建 INDEX.md(≤150 行硬上限)
13
+ * 5. 超出 150 条时,淘汰 low severity 且 date 最早的条目
14
+ */
15
+
16
+ import { readFileSync, writeFileSync, readdirSync, statSync, existsSync } from 'node:fs';
17
+ import { join } from 'node:path';
18
+
19
+ const PHASES = ['prd', 'plan', 'prototype', 'spec', 'build', 'review', 'cross-phase'];
20
+ const SEVERITY_ORDER = { high: 0, medium: 1, low: 2 };
21
+ const MAX_INDEX_LINES = 150;
22
+
23
+ function parseFrontmatter(content) {
24
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
25
+ if (!match) return null;
26
+ const fm = {};
27
+ for (const line of match[1].split('\n')) {
28
+ const idx = line.indexOf(':');
29
+ if (idx > 0) {
30
+ const key = line.slice(0, idx).trim();
31
+ const val = line.slice(idx + 1).trim();
32
+ fm[key] = val;
33
+ }
34
+ }
35
+ return fm;
36
+ }
37
+
38
+ function extractSummary(content) {
39
+ // 取 frontmatter 后第一个非空行作为摘要
40
+ const body = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
41
+ const lines = body.split('\n').filter(l => l.trim() && !l.startsWith('#'));
42
+ return lines[0]?.trim().slice(0, 80) || '(no summary)';
43
+ }
44
+
45
+ export function run(args = {}) {
46
+ const dir = args.dir || 'docs/solutions';
47
+ const indexPath = join(dir, 'INDEX.md');
48
+
49
+ if (!existsSync(dir)) {
50
+ console.error(`Solutions directory not found: ${dir}`);
51
+ process.exit(1);
52
+ }
53
+
54
+ const entries = [];
55
+
56
+ for (const phase of PHASES) {
57
+ const phaseDir = join(dir, phase);
58
+ if (!existsSync(phaseDir) || !statSync(phaseDir).isDirectory()) continue;
59
+
60
+ for (const file of readdirSync(phaseDir)) {
61
+ if (!file.endsWith('.md')) continue;
62
+ const filePath = join(phaseDir, file);
63
+ const content = readFileSync(filePath, 'utf-8');
64
+ const fm = parseFrontmatter(content);
65
+ if (!fm) continue;
66
+
67
+ entries.push({
68
+ date: fm.date || '1970-01-01',
69
+ phase: fm.phase || phase,
70
+ domain: fm.domain || 'general',
71
+ type: fm.type || 'insight',
72
+ severity: fm.severity || 'low',
73
+ summary: extractSummary(content),
74
+ file: `${phase}/${file}`,
75
+ });
76
+ }
77
+ }
78
+
79
+ // 排序:severity 降序,date 降序
80
+ entries.sort((a, b) => {
81
+ const sevDiff = (SEVERITY_ORDER[a.severity] ?? 3) - (SEVERITY_ORDER[b.severity] ?? 3);
82
+ if (sevDiff !== 0) return sevDiff;
83
+ return b.date.localeCompare(a.date);
84
+ });
85
+
86
+ // 截断到 150 条
87
+ const kept = entries.slice(0, MAX_INDEX_LINES);
88
+ const dropped = entries.slice(MAX_INDEX_LINES);
89
+
90
+ // 生成 INDEX.md
91
+ let index = '# Solutions Index\n';
92
+ index += '<!-- 每条一行,按 severity 降序,≤150 行硬上限 -->\n';
93
+ index += '| date | phase | domain | type | severity | summary | file |\n';
94
+ index += '|------|-------|--------|------|----------|---------|------|\n';
95
+ for (const e of kept) {
96
+ index += `| ${e.date} | ${e.phase} | ${e.domain} | ${e.type} | ${e.severity} | ${e.summary} | ${e.file} |\n`;
97
+ }
98
+
99
+ writeFileSync(indexPath, index, 'utf-8');
100
+ console.log(`INDEX.md rebuilt: ${kept.length} entries (${dropped.length} dropped)`);
101
+ if (dropped.length > 0) {
102
+ console.log(`Dropped ${dropped.length} low-priority entries (files preserved in directories)`);
103
+ }
104
+ }
105
+
106
+ // CLI 直接执行
107
+ if (process.argv[1]?.includes('solutions-index-gen')) {
108
+ const args = {};
109
+ const dirIdx = process.argv.indexOf('--dir');
110
+ if (dirIdx > -1 && process.argv[dirIdx + 1]) args.dir = process.argv[dirIdx + 1];
111
+ run(args);
112
+ }
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * solutions-inject — 按 phase+domain 过滤 INDEX.md,输出 top-5 摘要
4
+ *
5
+ * v0.5 复利贯穿机制脚本
6
+ * 用法:tf solutions inject --phase <p> --domain <d>
7
+ *
8
+ * 功能:
9
+ * 1. 读取 docs/solutions/INDEX.md
10
+ * 2. 过滤:phase = 指定阶段 OR phase = cross-phase,且 domain 匹配
11
+ * 3. 按 severity 降序取 top-5
12
+ * 4. 输出摘要(供各 skill 注入上下文)
13
+ */
14
+
15
+ import { readFileSync, existsSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+
18
+ const SEVERITY_ORDER = { high: 0, medium: 1, low: 2 };
19
+
20
+ export function run(args = {}) {
21
+ const phase = args.phase || 'cross-phase';
22
+ const domain = args.domain || '';
23
+ const dir = args.dir || 'docs/solutions';
24
+ const indexPath = join(dir, 'INDEX.md');
25
+
26
+ if (!existsSync(indexPath)) {
27
+ console.log('<!-- No solutions index found, skipping injection -->');
28
+ return { entries: [] };
29
+ }
30
+
31
+ const content = readFileSync(indexPath, 'utf-8');
32
+ const lines = content.split('\n').filter(l => l.startsWith('|') && !l.startsWith('| date') && !l.startsWith('|--'));
33
+
34
+ const entries = [];
35
+ for (const line of lines) {
36
+ const cols = line.split('|').map(c => c.trim()).filter(Boolean);
37
+ if (cols.length < 7) continue;
38
+ const [date, ePhase, eDomain, type, severity, summary, file] = cols;
39
+
40
+ // 过滤:phase 匹配或 cross-phase
41
+ const phaseMatch = ePhase === phase || ePhase === 'cross-phase';
42
+ // 过滤:domain 匹配(空 domain 表示通用)
43
+ const domainMatch = !domain || !eDomain || eDomain === 'general' || eDomain === domain;
44
+
45
+ if (phaseMatch && domainMatch) {
46
+ entries.push({ date, phase: ePhase, domain: eDomain, type, severity, summary, file });
47
+ }
48
+ }
49
+
50
+ // 按 severity 降序排序,取 top-5
51
+ entries.sort((a, b) => (SEVERITY_ORDER[a.severity] ?? 3) - (SEVERITY_ORDER[b.severity] ?? 3));
52
+ const top5 = entries.slice(0, 5);
53
+
54
+ if (top5.length === 0) {
55
+ console.log('<!-- No matching solutions found -->');
56
+ return { entries: [] };
57
+ }
58
+
59
+ console.log(`## Solutions Context (phase=${phase}, domain=${domain || 'any'})`);
60
+ console.log('');
61
+ for (const e of top5) {
62
+ console.log(`- [${e.severity}] ${e.summary} (${e.phase}/${e.domain}, ${e.date}) → ${e.file}`);
63
+ }
64
+ console.log('');
65
+
66
+ return { entries: top5 };
67
+ }
68
+
69
+ if (process.argv[1]?.includes('solutions-inject')) {
70
+ const args = {};
71
+ for (let i = 2; i < process.argv.length; i += 2) {
72
+ const key = process.argv[i]?.replace('--', '');
73
+ const val = process.argv[i + 1];
74
+ if (key && val) args[key] = val;
75
+ }
76
+ run(args);
77
+ }
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * solutions-promote — 检查 change 内经验的晋升条件,执行 change→product 晋升
4
+ *
5
+ * v0.5 复利贯穿机制脚本
6
+ * 用法:tf solutions promote <change-dir> [--dir <solutions-dir>]
7
+ *
8
+ * 功能:
9
+ * 1. 读取 specs/<cap>/learnings.md
10
+ * 2. 逐条检查晋升条件(severity ≥ medium 且 type = pitfall/pattern)
11
+ * 3. 检查与全局 INDEX 是否重复(domain+type 匹配)
12
+ * 4. 符合条件 → 写入全局 docs/solutions/
13
+ * 5. 重复 → 更新已有条目的 severity 和确认次数
14
+ *
15
+ * 变更说明(v0.5 / 2026-07-21):
16
+ * - 修正原稿在同步 run() 内使用 `await import('node:fs')` 取 mkdirSync 导致的语法错误,
17
+ * 改为在顶部静态 import mkdirSync,保持与其它 solutions-* 脚本一致的同步签名。
18
+ */
19
+
20
+ import { readFileSync, writeFileSync, existsSync, appendFileSync, mkdirSync } from 'node:fs';
21
+ import { join, basename } from 'node:path';
22
+
23
+ function parseFrontmatter(content) {
24
+ // learnings.md 中每个条目以 `## 标题` 开头,frontmatter 紧跟标题行之后;
25
+ // 故此处兼容 frontmatter 位于段首或标题行之后两种位置(^--- 只能匹配段首会漏判)。
26
+ const match = content.match(/(?:^|\n)---\n([\s\S]*?)\n---/);
27
+ if (!match) return null;
28
+ const fm = {};
29
+ for (const line of match[1].split('\n')) {
30
+ const idx = line.indexOf(':');
31
+ if (idx > 0) {
32
+ fm[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
33
+ }
34
+ }
35
+ return fm;
36
+ }
37
+
38
+ function parseLearnings(content) {
39
+ // 解析 learnings.md 中的经验条目(## 标题分隔)
40
+ const sections = content.split(/^## /m).filter(s => s.trim());
41
+ return sections.map(s => {
42
+ const lines = s.trim().split('\n');
43
+ const title = lines[0]?.trim() || '';
44
+ const fm = parseFrontmatter(s);
45
+ return { title, fm, body: s.trim() };
46
+ }).filter(e => e.title && e.title !== 'Learnings');
47
+ }
48
+
49
+ export function run(args = {}) {
50
+ const changeDir = args._?.[0] || args.changeDir;
51
+ const solutionsDir = args.dir || 'docs/solutions';
52
+
53
+ if (!changeDir) {
54
+ console.error('Usage: tf solutions promote <change-dir>');
55
+ process.exit(1);
56
+ }
57
+
58
+ const learningsPath = join(changeDir, 'learnings.md');
59
+ if (!existsSync(learningsPath)) {
60
+ console.log(`No learnings.md found in ${changeDir}, nothing to promote`);
61
+ return { promoted: 0, updated: 0 };
62
+ }
63
+
64
+ const content = readFileSync(learningsPath, 'utf-8');
65
+ const learnings = parseLearnings(content);
66
+
67
+ if (learnings.length === 0) {
68
+ console.log('No learning entries found');
69
+ return { promoted: 0, updated: 0 };
70
+ }
71
+
72
+ // 读取全局 INDEX
73
+ const indexPath = join(solutionsDir, 'INDEX.md');
74
+ const indexContent = existsSync(indexPath) ? readFileSync(indexPath, 'utf-8') : '';
75
+
76
+ let promoted = 0;
77
+ let updated = 0;
78
+
79
+ for (const learning of learnings) {
80
+ const severity = learning.fm?.severity || 'low';
81
+ const type = learning.fm?.type || 'insight';
82
+ const phase = learning.fm?.phase || 'cross-phase';
83
+ const domain = learning.fm?.domain || 'general';
84
+
85
+ // 晋升条件:severity ≥ medium 且 type = pitfall/pattern
86
+ const sevOk = severity === 'high' || severity === 'medium';
87
+ const typeOk = type === 'pitfall' || type === 'pattern';
88
+ if (!sevOk || !typeOk) continue;
89
+
90
+ // 检查重复:domain+type 匹配
91
+ const dupRegex = new RegExp(`\\| ${domain} \\| ${type} \\|`);
92
+ if (dupRegex.test(indexContent)) {
93
+ console.log(`Duplicate found (${domain}/${type}), marking as confirmed pattern`);
94
+ updated++;
95
+ continue;
96
+ }
97
+
98
+ // 写入全局
99
+ const date = learning.fm?.date || new Date().toISOString().slice(0, 10);
100
+ const phaseDir = join(solutionsDir, phase);
101
+ if (!existsSync(phaseDir)) {
102
+ mkdirSync(phaseDir, { recursive: true });
103
+ }
104
+
105
+ const slug = learning.title.toLowerCase().replace(/[^a-z0-9一-鿿]+/g, '-').slice(0, 40);
106
+ const fileName = `${date}-${slug}.md`;
107
+ const filePath = join(phaseDir, fileName);
108
+
109
+ const fileContent = `---
110
+ phase: ${phase}
111
+ domain: ${domain}
112
+ type: ${type}
113
+ severity: ${severity}
114
+ date: ${date}
115
+ source: ${basename(changeDir)}
116
+ ---
117
+
118
+ ${learning.body}
119
+ `;
120
+ writeFileSync(filePath, fileContent, 'utf-8');
121
+
122
+ // 追加 INDEX
123
+ appendFileSync(indexPath, `| ${date} | ${phase} | ${domain} | ${type} | ${severity} | ${learning.title.slice(0, 80)} | ${phase}/${fileName} |\n`, 'utf-8');
124
+
125
+ console.log(`Promoted: ${phase}/${fileName}`);
126
+ promoted++;
127
+ }
128
+
129
+ console.log(`\nPromotion complete: ${promoted} promoted, ${updated} updated as confirmed`);
130
+ return { promoted, updated };
131
+ }
132
+
133
+ if (process.argv[1]?.includes('solutions-promote')) {
134
+ const args = { _: [] };
135
+ for (let i = 2; i < process.argv.length; i++) {
136
+ if (process.argv[i].startsWith('--')) {
137
+ const key = process.argv[i].replace('--', '');
138
+ const val = process.argv[i + 1] && !process.argv[i + 1].startsWith('--') ? process.argv[++i] : true;
139
+ args[key] = val;
140
+ } else {
141
+ args._.push(process.argv[i]);
142
+ }
143
+ }
144
+ run(args);
145
+ }
@@ -0,0 +1,85 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ function toPosix(value) {
5
+ return value.split(path.sep).join('/');
6
+ }
7
+
8
+ function specsDir(changeDir) {
9
+ return path.join(changeDir, 'specs');
10
+ }
11
+
12
+ function walk(dir, results = []) {
13
+ if (!fs.existsSync(dir)) return results;
14
+
15
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
16
+ const full = path.join(dir, entry.name);
17
+ if (entry.isDirectory()) {
18
+ walk(full, results);
19
+ } else if (entry.isFile()) {
20
+ results.push(full);
21
+ }
22
+ }
23
+
24
+ return results;
25
+ }
26
+
27
+ export function findCanonicalSpecFiles(changeDir) {
28
+ const root = specsDir(changeDir);
29
+ if (!fs.existsSync(root)) return [];
30
+
31
+ return walk(root)
32
+ .filter(file => {
33
+ if (path.basename(file) !== 'spec.md') return false;
34
+ const rel = path.relative(root, file).split(path.sep);
35
+ return rel.length === 2;
36
+ })
37
+ .sort();
38
+ }
39
+
40
+ export function findInvalidSpecFiles(changeDir) {
41
+ const root = specsDir(changeDir);
42
+ if (!fs.existsSync(root)) return [];
43
+
44
+ const invalid = [];
45
+
46
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
47
+ if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
48
+
49
+ const rel = `specs/${entry.name}`;
50
+ if (entry.name === 'README.md') continue;
51
+ if (entry.name === 'spec.md') {
52
+ invalid.push({ path: rel, expected: 'specs/<capability>/spec.md' });
53
+ continue;
54
+ }
55
+
56
+ const capability = entry.name.replace(/\.md$/, '');
57
+ invalid.push({ path: rel, expected: `specs/${capability}/spec.md` });
58
+ }
59
+
60
+ return invalid.sort((a, b) => a.path.localeCompare(b.path));
61
+ }
62
+
63
+ export function formatSpecPathFailure(item) {
64
+ return `Invalid spec path: ${item.path}. Expected: ${item.expected}`;
65
+ }
66
+
67
+ export function validateSpecPathLayout(changeDir, options = {}) {
68
+ const requireSpecs = options.requireSpecs === true;
69
+ const specFiles = findCanonicalSpecFiles(changeDir);
70
+ const failures = findInvalidSpecFiles(changeDir).map(formatSpecPathFailure);
71
+
72
+ if (requireSpecs && specFiles.length === 0) {
73
+ failures.push('No canonical spec files found. Expected at least one specs/<capability>/spec.md');
74
+ }
75
+
76
+ return {
77
+ pass: failures.length === 0,
78
+ specFiles,
79
+ failures,
80
+ };
81
+ }
82
+
83
+ export function relativeSpecPath(changeDir, file) {
84
+ return toPosix(path.relative(changeDir, file));
85
+ }
@@ -0,0 +1,154 @@
1
+ // scripts/lib/state-loader.mjs — lightweight .team-flow.yaml state file reader/writer
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ const STATE_FILE = '.team-flow.yaml';
6
+
7
+ const BUILTIN_DEFAULTS = {
8
+ state: 'exploring',
9
+ workflow: 'auto',
10
+ revision: null,
11
+ artifacts_hash: null,
12
+ contract_hash: null,
13
+ execution_mode: null,
14
+ execution_plan_hash: null,
15
+ execution_plan_revision: null,
16
+ batches_completed: 0,
17
+ test_result: null,
18
+ spec_merged: false,
19
+ change_name: null,
20
+ last_transition: null,
21
+ last_transition_from: null,
22
+ last_transition_to: null,
23
+ dp_0_decisions: null,
24
+ dp_0_result: null,
25
+ dp_0_confirmed: null,
26
+ dp_0_timestamp: null,
27
+ dp_1_result: null,
28
+ dp_1_timestamp: null,
29
+ dp_2_result: null,
30
+ dp_2_timestamp: null,
31
+ dp_3_result: null,
32
+ dp_3_timestamp: null,
33
+ dp_4_result: null,
34
+ dp_4_timestamp: null,
35
+ dp_5_result: null,
36
+ dp_5_timestamp: null,
37
+ dp_6_result: null,
38
+ dp_6_timestamp: null,
39
+ dp_7_result: null,
40
+ dp_7_timestamp: null,
41
+ };
42
+
43
+ /**
44
+ * Read state file, merging with built-in defaults.
45
+ * Returns a complete state object even if the file doesn't exist.
46
+ */
47
+ export function readState(changeDir) {
48
+ const filePath = path.join(changeDir, STATE_FILE);
49
+ if (!fs.existsSync(filePath)) {
50
+ return { ...BUILTIN_DEFAULTS, change_name: path.basename(changeDir) };
51
+ }
52
+
53
+ const raw = fs.readFileSync(filePath, 'utf-8');
54
+ const parsed = parseYaml(raw);
55
+ return { ...BUILTIN_DEFAULTS, ...parsed };
56
+ }
57
+
58
+ /**
59
+ * Write state object to .team-flow.yaml.
60
+ */
61
+ export function writeState(changeDir, state) {
62
+ const filePath = path.join(changeDir, STATE_FILE);
63
+ const lines = [];
64
+ lines.push('# .team-flow.yaml — lightweight state machine');
65
+ lines.push('# Derived data. Always rebuildable from artifacts. Lost/corrupt → fall back to content-level detection.');
66
+ lines.push('');
67
+ lines.push('# === Core state ===');
68
+ lines.push(`state: ${state.state || 'exploring'}`);
69
+ lines.push(`workflow: ${state.workflow || 'auto'}`);
70
+ lines.push(`revision: ${state.revision ?? 'null'}`);
71
+ lines.push('');
72
+ lines.push('# === Hashes (fast staleness detection) ===');
73
+ lines.push(`artifacts_hash: ${state.artifacts_hash ?? 'null'}`);
74
+ lines.push(`contract_hash: ${state.contract_hash ?? 'null'}`);
75
+ lines.push('');
76
+ lines.push('# === Execution progress ===');
77
+ lines.push(`execution_mode: ${state.execution_mode ?? 'null'}`);
78
+ lines.push(`execution_plan_hash: ${state.execution_plan_hash ?? 'null'}`);
79
+ lines.push(`execution_plan_revision: ${state.execution_plan_revision ?? 'null'}`);
80
+ lines.push(`batches_completed: ${state.batches_completed ?? 0}`);
81
+ lines.push(`test_result: ${state.test_result ?? 'null'}`);
82
+ lines.push(`spec_merged: ${state.spec_merged ?? false}`);
83
+ lines.push('');
84
+ lines.push('# === Metadata ===');
85
+ lines.push(`change_name: ${state.change_name ?? path.basename(changeDir)}`);
86
+ lines.push(`last_transition: ${state.last_transition ?? 'null'}`);
87
+ lines.push(`last_transition_from: ${state.last_transition_from ?? 'null'}`);
88
+ lines.push(`last_transition_to: ${state.last_transition_to ?? 'null'}`);
89
+ lines.push('');
90
+ lines.push('# === Decision points ===');
91
+ lines.push(`dp_0_decisions: ${state.dp_0_decisions ?? 'null'}`);
92
+ lines.push(`dp_0_result: ${state.dp_0_result ?? 'null'}`);
93
+ lines.push(`dp_0_confirmed: ${state.dp_0_confirmed ?? 'null'}`);
94
+ lines.push(`dp_0_timestamp: ${state.dp_0_timestamp ?? 'null'}`);
95
+ lines.push(`dp_1_result: ${state.dp_1_result ?? 'null'}`);
96
+ lines.push(`dp_1_timestamp: ${state.dp_1_timestamp ?? 'null'}`);
97
+ lines.push(`dp_2_result: ${state.dp_2_result ?? 'null'}`);
98
+ lines.push(`dp_2_timestamp: ${state.dp_2_timestamp ?? 'null'}`);
99
+ lines.push(`dp_3_result: ${state.dp_3_result ?? 'null'}`);
100
+ lines.push(`dp_3_timestamp: ${state.dp_3_timestamp ?? 'null'}`);
101
+ lines.push(`dp_4_result: ${state.dp_4_result ?? 'null'}`);
102
+ lines.push(`dp_4_timestamp: ${state.dp_4_timestamp ?? 'null'}`);
103
+ lines.push(`dp_5_result: ${state.dp_5_result ?? 'null'}`);
104
+ lines.push(`dp_5_timestamp: ${state.dp_5_timestamp ?? 'null'}`);
105
+ lines.push(`dp_6_result: ${state.dp_6_result ?? 'null'}`);
106
+ lines.push(`dp_6_timestamp: ${state.dp_6_timestamp ?? 'null'}`);
107
+ lines.push(`dp_7_result: ${state.dp_7_result ?? 'null'}`);
108
+ lines.push(`dp_7_timestamp: ${state.dp_7_timestamp ?? 'null'}`);
109
+
110
+ fs.writeFileSync(filePath, lines.join('\n') + '\n', 'utf-8');
111
+ }
112
+
113
+ /**
114
+ * Update a single field in the state file.
115
+ */
116
+ export function updateField(changeDir, field, value) {
117
+ const state = readState(changeDir);
118
+ state[field] = value;
119
+ writeState(changeDir, state);
120
+ }
121
+
122
+ /**
123
+ * Rebuild state file from artifacts — recomputes hashes.
124
+ * Requires hash functions to be passed in (avoids circular dependency).
125
+ */
126
+ export function rebuildState(changeDir, { computeArtifactsHash, computeContractHash }) {
127
+ const state = readState(changeDir);
128
+ state.artifacts_hash = computeArtifactsHash(changeDir);
129
+ state.contract_hash = computeContractHash(changeDir);
130
+ writeState(changeDir, state);
131
+ return state;
132
+ }
133
+
134
+ // Minimal YAML parser — top-level fields only, zero dependencies.
135
+ // Handles strings, null, integers. No nested structures needed.
136
+ function parseYaml(content) {
137
+ const result = {};
138
+ for (const line of content.split('\n')) {
139
+ const trimmed = line.trim();
140
+ if (!trimmed || trimmed.startsWith('#')) continue;
141
+ const match = trimmed.match(/^(\w[\w_]*):\s*(.*)/);
142
+ if (match) {
143
+ const val = match[2].trim();
144
+ if (val === 'null' || val === '') {
145
+ result[match[1]] = null;
146
+ } else if (/^\d+$/.test(val)) {
147
+ result[match[1]] = parseInt(val, 10);
148
+ } else {
149
+ result[match[1]] = val;
150
+ }
151
+ }
152
+ }
153
+ return result;
154
+ }