freeharness 0.4.1

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 (375) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +108 -0
  3. package/README.md +134 -0
  4. package/canonical/HARVEST.yaml +858 -0
  5. package/canonical/PROVENANCE.yaml +740 -0
  6. package/canonical/agents/README.md +95 -0
  7. package/canonical/agents/agentic-architect.md +18 -0
  8. package/canonical/agents/database-reviewer.md +41 -0
  9. package/canonical/agents/explorer-ai-engineer-analyst.md +65 -0
  10. package/canonical/agents/pr-reviewer.md +96 -0
  11. package/canonical/agents/python-reviewer.md +55 -0
  12. package/canonical/agents/report-formatter.md +19 -0
  13. package/canonical/agents/security-reviewer.md +53 -0
  14. package/canonical/agents/test-writer.md +34 -0
  15. package/canonical/commands/fh-context.md +37 -0
  16. package/canonical/evals/promptfoo.redteam.yaml +108 -0
  17. package/canonical/hooks/cost-prices.json +14 -0
  18. package/canonical/hooks/freeharness-auto-memory.mjs +158 -0
  19. package/canonical/hooks/freeharness-config-guard.mjs +162 -0
  20. package/canonical/hooks/freeharness-context-monitor.mjs +198 -0
  21. package/canonical/hooks/freeharness-converge.mjs +227 -0
  22. package/canonical/hooks/freeharness-cost-tracker.mjs +1027 -0
  23. package/canonical/hooks/freeharness-memory-guard.mjs +57 -0
  24. package/canonical/hooks/freeharness-output-pruner.mjs +333 -0
  25. package/canonical/hooks/freeharness-precompact.mjs +323 -0
  26. package/canonical/hooks/freeharness-repeat-tool.mjs +208 -0
  27. package/canonical/hooks/freeharness-security-guidance.mjs +164 -0
  28. package/canonical/hooks/freeharness-session-start +583 -0
  29. package/canonical/hooks/hooks.auto-memory.claude-code.json +16 -0
  30. package/canonical/hooks/hooks.claude-code.json +17 -0
  31. package/canonical/hooks/hooks.config-guard.claude-code.json +16 -0
  32. package/canonical/hooks/hooks.context-monitor.claude-code.json +15 -0
  33. package/canonical/hooks/hooks.converge.claude-code.json +15 -0
  34. package/canonical/hooks/hooks.cost-tracker.claude-code.json +37 -0
  35. package/canonical/hooks/hooks.memory-guard.claude-code.json +16 -0
  36. package/canonical/hooks/hooks.output-pruner.claude-code.json +16 -0
  37. package/canonical/hooks/hooks.precompact.claude-code.json +15 -0
  38. package/canonical/hooks/hooks.repeat-tool.claude-code.json +25 -0
  39. package/canonical/hooks/hooks.security-guidance.claude-code.json +16 -0
  40. package/canonical/mcp/servers.json +20 -0
  41. package/canonical/openspec/config.base.yaml +89 -0
  42. package/canonical/openspec/schema.spec-driven.yaml +277 -0
  43. package/canonical/openspec/templates/data-contract.md +36 -0
  44. package/canonical/openspec/templates/design.md +84 -0
  45. package/canonical/openspec/templates/eval-plan.md +33 -0
  46. package/canonical/openspec/templates/follow-ups.md +2 -0
  47. package/canonical/openspec/templates/proposal.md +45 -0
  48. package/canonical/openspec/templates/spec.md +42 -0
  49. package/canonical/openspec/templates/tasks.md +38 -0
  50. package/canonical/profiles/local-llamacpp.LOCAL-MODELS.md +47 -0
  51. package/canonical/profiles/local-llamacpp.json +27 -0
  52. package/canonical/profiles/local-ollama.json +20 -0
  53. package/canonical/profiles/mcp.json +91 -0
  54. package/canonical/rules/CLAUDE.md +78 -0
  55. package/canonical/rules/project-CLAUDE.template.md +62 -0
  56. package/canonical/rules/routing.md +20 -0
  57. package/canonical/rules/safety.md +41 -0
  58. package/canonical/settings/settings.cache-hygiene.claude-code.json +5 -0
  59. package/canonical/skills/agent-evaluation/SKILL.md +102 -0
  60. package/canonical/skills/agent-evaluation/assets/evaluation_report_template.md +206 -0
  61. package/canonical/skills/agent-evaluation/evals/cases.yaml +106 -0
  62. package/canonical/skills/agent-evaluation/references/dataset-preparation.md +351 -0
  63. package/canonical/skills/agent-evaluation/references/scorers-constraints.md +165 -0
  64. package/canonical/skills/agent-evaluation/references/scorers.md +431 -0
  65. package/canonical/skills/agent-evaluation/references/setup-guide.md +252 -0
  66. package/canonical/skills/agent-evaluation/references/tracing-integration.md +164 -0
  67. package/canonical/skills/agent-evaluation/references/troubleshooting.md +514 -0
  68. package/canonical/skills/agent-evaluation/scripts/analyze_results.py +462 -0
  69. package/canonical/skills/agent-evaluation/scripts/create_dataset_template.py +382 -0
  70. package/canonical/skills/agent-evaluation/scripts/list_datasets.py +273 -0
  71. package/canonical/skills/agent-evaluation/scripts/run_evaluation_template.py +309 -0
  72. package/canonical/skills/agent-evaluation/scripts/setup_mlflow.py +353 -0
  73. package/canonical/skills/agent-evaluation/scripts/utils/__init__.py +27 -0
  74. package/canonical/skills/agent-evaluation/scripts/utils/env_validation.py +123 -0
  75. package/canonical/skills/agent-evaluation/scripts/utils/tracing_utils.py +108 -0
  76. package/canonical/skills/agent-evaluation/scripts/validate_agent_tracing.py +148 -0
  77. package/canonical/skills/agent-evaluation/scripts/validate_auth.py +240 -0
  78. package/canonical/skills/agent-evaluation/scripts/validate_environment.py +185 -0
  79. package/canonical/skills/agent-evaluation/scripts/validate_tracing_runtime.py +318 -0
  80. package/canonical/skills/agentic-architecture/SKILL.md +41 -0
  81. package/canonical/skills/agentic-architecture/evals/cases.yaml +94 -0
  82. package/canonical/skills/agentic-architecture/reference/context-engineering.md +75 -0
  83. package/canonical/skills/agentic-architecture/reference/patterns.md +76 -0
  84. package/canonical/skills/agentic-architecture/reference/reliability-observability.md +94 -0
  85. package/canonical/skills/agentic-architecture/reference/review-framework.md +59 -0
  86. package/canonical/skills/code-review/SKILL.md +52 -0
  87. package/canonical/skills/code-review/evals/cases.yaml +197 -0
  88. package/canonical/skills/git-workflow/SKILL.md +41 -0
  89. package/canonical/skills/git-workflow/evals/cases.yaml +92 -0
  90. package/canonical/skills/local-code/SKILL.md +24 -0
  91. package/canonical/skills/local-code/evals/cases.yaml +85 -0
  92. package/canonical/skills/local-code/scripts/query_llama.py +68 -0
  93. package/canonical/skills/report-formatting/SKILL.md +62 -0
  94. package/canonical/skills/report-formatting/evals/cases.yaml +83 -0
  95. package/canonical/skills/report-formatting/reference/ARCHITECTURE.md +135 -0
  96. package/canonical/skills/report-formatting/reference/HELPERS.md +120 -0
  97. package/canonical/skills/report-formatting/reference/PPTX.md +136 -0
  98. package/canonical/skills/report-formatting/reference/RELEASE.md +99 -0
  99. package/canonical/skills/report-formatting/reference/TEMPLATES.md +48 -0
  100. package/canonical/skills/report-formatting/reference/authoring.md +66 -0
  101. package/canonical/skills/report-formatting/scripts/architecture-template.md +271 -0
  102. package/canonical/skills/report-formatting/scripts/assets/claro-logo.png +0 -0
  103. package/canonical/skills/report-formatting/scripts/assets/claro-wordmark.svg +1 -0
  104. package/canonical/skills/report-formatting/scripts/build-architecture.js +57 -0
  105. package/canonical/skills/report-formatting/scripts/build-md-claro.js +224 -0
  106. package/canonical/skills/report-formatting/scripts/build-release.js +51 -0
  107. package/canonical/skills/report-formatting/scripts/check-env.js +58 -0
  108. package/canonical/skills/report-formatting/scripts/docmodel.js +283 -0
  109. package/canonical/skills/report-formatting/scripts/example-build.js +119 -0
  110. package/canonical/skills/report-formatting/scripts/example_build_ArchitectureReview_docx.js +195 -0
  111. package/canonical/skills/report-formatting/scripts/example_build_ExecutiveReport_docx.js +114 -0
  112. package/canonical/skills/report-formatting/scripts/package.json +17 -0
  113. package/canonical/skills/report-formatting/scripts/pptx/example_build_StatusReport_pptx.py +223 -0
  114. package/canonical/skills/report-formatting/scripts/pptx/example_build_WeeklyStatus_pptx.py +245 -0
  115. package/canonical/skills/report-formatting/scripts/pptx/pptx_office.py +144 -0
  116. package/canonical/skills/report-formatting/scripts/pptx/requirements.txt +7 -0
  117. package/canonical/skills/report-formatting/scripts/pptx/status_report_blocks.py +193 -0
  118. package/canonical/skills/report-formatting/scripts/pptx/theme_claro_pptx.py +473 -0
  119. package/canonical/skills/report-formatting/scripts/release-template.md +80 -0
  120. package/canonical/skills/report-formatting/scripts/render-mermaid.js +105 -0
  121. package/canonical/skills/report-formatting/scripts/theme-claro.js +403 -0
  122. package/canonical/skills/subagent-driven-development/SKILL.md +60 -0
  123. package/canonical/skills/subagent-driven-development/evals/cases.yaml +208 -0
  124. package/canonical/skills/subagent-driven-development/reference/brief.md +91 -0
  125. package/canonical/skills/subagent-driven-development/reference/handoff.md +118 -0
  126. package/canonical/skills/subagent-driven-development/scripts/validate-handoff.mjs +850 -0
  127. package/canonical/skills/systematic-debugging/SKILL.md +44 -0
  128. package/canonical/skills/systematic-debugging/evals/cases.yaml +90 -0
  129. package/canonical/skills/test-driven-development/SKILL.md +42 -0
  130. package/canonical/skills/test-driven-development/evals/cases.yaml +86 -0
  131. package/canonical/skills/test-driven-development/reference/writing-good-tests.md +52 -0
  132. package/canonical/skills/verification-before-completion/SKILL.md +53 -0
  133. package/canonical/skills/verification-before-completion/evals/cases.yaml +107 -0
  134. package/dist/adapters/content/language-lint.d.ts +46 -0
  135. package/dist/adapters/content/language-lint.js +191 -0
  136. package/dist/adapters/harness/claude-code.d.ts +57 -0
  137. package/dist/adapters/harness/claude-code.js +385 -0
  138. package/dist/adapters/harness/index.d.ts +3 -0
  139. package/dist/adapters/harness/index.js +17 -0
  140. package/dist/adapters/harness/opencode.d.ts +7 -0
  141. package/dist/adapters/harness/opencode.js +166 -0
  142. package/dist/adapters/harness/plugin.d.ts +22 -0
  143. package/dist/adapters/harness/plugin.js +257 -0
  144. package/dist/adapters/memory/doctor.d.ts +480 -0
  145. package/dist/adapters/memory/doctor.js +1217 -0
  146. package/dist/adapters/memory/vendored.d.ts +150 -0
  147. package/dist/adapters/memory/vendored.js +487 -0
  148. package/dist/adapters/package/node.d.ts +9 -0
  149. package/dist/adapters/package/node.js +20 -0
  150. package/dist/adapters/prompt/readline.d.ts +19 -0
  151. package/dist/adapters/prompt/readline.js +53 -0
  152. package/dist/adapters/sync/diff.d.ts +43 -0
  153. package/dist/adapters/sync/diff.js +380 -0
  154. package/dist/cli/cleanup.d.ts +86 -0
  155. package/dist/cli/cleanup.js +382 -0
  156. package/dist/cli/cost.d.ts +304 -0
  157. package/dist/cli/cost.js +501 -0
  158. package/dist/cli/doctor.d.ts +205 -0
  159. package/dist/cli/doctor.js +654 -0
  160. package/dist/cli/executor.d.ts +5 -0
  161. package/dist/cli/executor.js +48 -0
  162. package/dist/cli/plugin-validate.d.ts +37 -0
  163. package/dist/cli/plugin-validate.js +256 -0
  164. package/dist/cli/prefix.d.ts +68 -0
  165. package/dist/cli/prefix.js +218 -0
  166. package/dist/cli/profile.d.ts +170 -0
  167. package/dist/cli/profile.js +285 -0
  168. package/dist/cli/runner.d.ts +10 -0
  169. package/dist/cli/runner.js +27 -0
  170. package/dist/cli/uninstall.d.ts +65 -0
  171. package/dist/cli/uninstall.js +147 -0
  172. package/dist/index.d.ts +8 -0
  173. package/dist/index.js +989 -0
  174. package/dist/infra/dogfood.d.ts +31 -0
  175. package/dist/infra/dogfood.js +51 -0
  176. package/dist/infra/fsutil.d.ts +40 -0
  177. package/dist/infra/fsutil.js +143 -0
  178. package/dist/infra/manifest.d.ts +76 -0
  179. package/dist/infra/manifest.js +92 -0
  180. package/dist/infra/paths.d.ts +19 -0
  181. package/dist/infra/paths.js +74 -0
  182. package/dist/infra/routing.d.ts +4 -0
  183. package/dist/infra/routing.js +7 -0
  184. package/node_modules/@freeharness/core/dist/context.d.ts +30 -0
  185. package/node_modules/@freeharness/core/dist/context.js +1 -0
  186. package/node_modules/@freeharness/core/dist/index.d.ts +5 -0
  187. package/node_modules/@freeharness/core/dist/index.js +6 -0
  188. package/node_modules/@freeharness/core/dist/planner.d.ts +13 -0
  189. package/node_modules/@freeharness/core/dist/planner.js +18 -0
  190. package/node_modules/@freeharness/core/dist/ports.d.ts +90 -0
  191. package/node_modules/@freeharness/core/dist/ports.js +1 -0
  192. package/node_modules/@freeharness/core/dist/registry.d.ts +41 -0
  193. package/node_modules/@freeharness/core/dist/registry.js +124 -0
  194. package/node_modules/@freeharness/core/dist/tiers.d.ts +30 -0
  195. package/node_modules/@freeharness/core/dist/tiers.js +99 -0
  196. package/node_modules/@freeharness/core/package.json +34 -0
  197. package/node_modules/@freeharness/sdd/PROVENANCE.yaml +175 -0
  198. package/node_modules/@freeharness/sdd/dist/src/bin/openspec.d.ts +2 -0
  199. package/node_modules/@freeharness/sdd/dist/src/bin/openspec.js +166 -0
  200. package/node_modules/@freeharness/sdd/dist/src/index.d.ts +14 -0
  201. package/node_modules/@freeharness/sdd/dist/src/index.js +11 -0
  202. package/node_modules/@freeharness/sdd/dist/src/init.d.ts +39 -0
  203. package/node_modules/@freeharness/sdd/dist/src/init.js +190 -0
  204. package/node_modules/@freeharness/sdd/dist/src/root-resolver.d.ts +8 -0
  205. package/node_modules/@freeharness/sdd/dist/src/root-resolver.js +32 -0
  206. package/node_modules/@freeharness/sdd/dist/src/shim/archive.d.ts +81 -0
  207. package/node_modules/@freeharness/sdd/dist/src/shim/archive.js +483 -0
  208. package/node_modules/@freeharness/sdd/dist/src/shim/context.d.ts +4 -0
  209. package/node_modules/@freeharness/sdd/dist/src/shim/context.js +48 -0
  210. package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.d.ts +57 -0
  211. package/node_modules/@freeharness/sdd/dist/src/shim/follow-up.js +279 -0
  212. package/node_modules/@freeharness/sdd/dist/src/shim/instructions.d.ts +67 -0
  213. package/node_modules/@freeharness/sdd/dist/src/shim/instructions.js +460 -0
  214. package/node_modules/@freeharness/sdd/dist/src/shim/list.d.ts +5 -0
  215. package/node_modules/@freeharness/sdd/dist/src/shim/list.js +95 -0
  216. package/node_modules/@freeharness/sdd/dist/src/shim/new-change.d.ts +6 -0
  217. package/node_modules/@freeharness/sdd/dist/src/shim/new-change.js +150 -0
  218. package/node_modules/@freeharness/sdd/dist/src/shim/schemas.d.ts +4 -0
  219. package/node_modules/@freeharness/sdd/dist/src/shim/schemas.js +23 -0
  220. package/node_modules/@freeharness/sdd/dist/src/shim/shared.d.ts +56 -0
  221. package/node_modules/@freeharness/sdd/dist/src/shim/shared.js +140 -0
  222. package/node_modules/@freeharness/sdd/dist/src/shim/status.d.ts +8 -0
  223. package/node_modules/@freeharness/sdd/dist/src/shim/status.js +85 -0
  224. package/node_modules/@freeharness/sdd/dist/src/shim/validate.d.ts +6 -0
  225. package/node_modules/@freeharness/sdd/dist/src/shim/validate.js +229 -0
  226. package/node_modules/@freeharness/sdd/dist/src/validate-change.d.ts +40 -0
  227. package/node_modules/@freeharness/sdd/dist/src/validate-change.js +114 -0
  228. package/node_modules/@freeharness/sdd/dist/src/validate.d.ts +24 -0
  229. package/node_modules/@freeharness/sdd/dist/src/validate.js +72 -0
  230. package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.d.ts +130 -0
  231. package/node_modules/@freeharness/sdd/dist/vendor/openspec/apply/specs-apply.js +981 -0
  232. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-metadata.d.ts +46 -0
  233. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-metadata.js +128 -0
  234. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.d.ts +26 -0
  235. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/change-status-policy.js +32 -0
  236. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/file-system.d.ts +50 -0
  237. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/file-system.js +394 -0
  238. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/global-config.d.ts +15 -0
  239. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/global-config.js +49 -0
  240. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/graph.d.ts +71 -0
  241. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/graph.js +164 -0
  242. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.d.ts +11 -0
  243. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/index.js +21 -0
  244. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/instruction-loader.d.ts +102 -0
  245. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/instruction-loader.js +230 -0
  246. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.d.ts +18 -0
  247. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/outputs.js +138 -0
  248. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/resolver.d.ts +82 -0
  249. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/resolver.js +333 -0
  250. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/schema.d.ts +12 -0
  251. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/schema.js +110 -0
  252. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/state.d.ts +11 -0
  253. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/state.js +32 -0
  254. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.d.ts +33 -0
  255. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/task-progress.js +149 -0
  256. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.d.ts +39 -0
  257. package/node_modules/@freeharness/sdd/dist/vendor/openspec/engine/types.js +49 -0
  258. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/claude.d.ts +12 -0
  259. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/claude.js +31 -0
  260. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/index.d.ts +10 -0
  261. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/index.js +10 -0
  262. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/opencode.d.ts +12 -0
  263. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/adapters/opencode.js +26 -0
  264. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/generator.d.ts +28 -0
  265. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/generator.js +41 -0
  266. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/index.d.ts +23 -0
  267. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/index.js +25 -0
  268. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/invocation.d.ts +72 -0
  269. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/invocation.js +76 -0
  270. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/registry.d.ts +39 -0
  271. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/registry.js +53 -0
  272. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/types.d.ts +62 -0
  273. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/types.js +7 -0
  274. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/yaml.d.ts +35 -0
  275. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-generation/yaml.js +47 -0
  276. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-references.d.ts +33 -0
  277. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/command-references.js +55 -0
  278. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/shared/allowed-tools.d.ts +11 -0
  279. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/shared/allowed-tools.js +11 -0
  280. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/shared/skill-generation.d.ts +48 -0
  281. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/shared/skill-generation.js +99 -0
  282. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/skill-templates.d.ts +19 -0
  283. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/skill-templates.js +18 -0
  284. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/types.d.ts +18 -0
  285. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/types.js +4 -0
  286. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.d.ts +19 -0
  287. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/apply-change.js +207 -0
  288. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.d.ts +12 -0
  289. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/archive-change.js +409 -0
  290. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/bulk-archive-change.d.ts +9 -0
  291. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/bulk-archive-change.js +673 -0
  292. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/continue-change.d.ts +9 -0
  293. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/continue-change.js +230 -0
  294. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.d.ts +12 -0
  295. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/explore.js +550 -0
  296. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/feedback.d.ts +8 -0
  297. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/feedback.js +107 -0
  298. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/ff-change.d.ts +12 -0
  299. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/ff-change.js +228 -0
  300. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/new-change.d.ts +9 -0
  301. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/new-change.js +147 -0
  302. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/onboard.d.ts +9 -0
  303. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/onboard.js +571 -0
  304. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.d.ts +12 -0
  305. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/propose.js +322 -0
  306. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/store-selection.d.ts +7 -0
  307. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/store-selection.js +7 -0
  308. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.d.ts +9 -0
  309. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/sync-specs.js +518 -0
  310. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.d.ts +9 -0
  311. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/update-change.js +176 -0
  312. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/verify-change.d.ts +9 -0
  313. package/node_modules/@freeharness/sdd/dist/vendor/openspec/generation/templates/workflows/verify-change.js +344 -0
  314. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.d.ts +39 -0
  315. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/constants.js +54 -0
  316. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/delta-resolution.d.ts +35 -0
  317. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/delta-resolution.js +171 -0
  318. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.d.ts +27 -0
  319. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/change-parser.js +210 -0
  320. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/code-fence.d.ts +14 -0
  321. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/code-fence.js +48 -0
  322. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.d.ts +22 -0
  323. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/markdown-parser.js +186 -0
  324. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.d.ts +65 -0
  325. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-blocks.js +409 -0
  326. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.d.ts +55 -0
  327. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/requirement-text.js +98 -0
  328. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.d.ts +8 -0
  329. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/parsers/spec-structure.js +77 -0
  330. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.d.ts +44 -0
  331. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/purpose-placeholder.js +116 -0
  332. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/base.schema.d.ts +12 -0
  333. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/base.schema.js +17 -0
  334. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/change.schema.d.ts +72 -0
  335. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/change.schema.js +30 -0
  336. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/index.d.ts +3 -0
  337. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/index.js +3 -0
  338. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/spec.schema.d.ts +17 -0
  339. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/schemas/spec.schema.js +14 -0
  340. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.d.ts +39 -0
  341. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/spec-discovery.js +114 -0
  342. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.d.ts +45 -0
  343. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-lines.js +105 -0
  344. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-numbering.d.ts +15 -0
  345. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/task-numbering.js +56 -0
  346. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/types.d.ts +17 -0
  347. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/types.js +1 -0
  348. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.d.ts +94 -0
  349. package/node_modules/@freeharness/sdd/dist/vendor/openspec/validation/validator.js +756 -0
  350. package/node_modules/@freeharness/sdd/package.json +57 -0
  351. package/node_modules/@freeharness/sdd/vendor/openspec/LICENSE.upstream +22 -0
  352. package/node_modules/@freeharness/sdd/vendor/openspec/assets/schema.upstream.yaml +232 -0
  353. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/README.md +19 -0
  354. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-apply-change/SKILL.md +187 -0
  355. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-archive-change/SKILL.md +181 -0
  356. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-bulk-archive-change/SKILL.md +338 -0
  357. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-continue-change/SKILL.md +117 -0
  358. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-explore/SKILL.md +342 -0
  359. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-ff-change/SKILL.md +116 -0
  360. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-new-change/SKILL.md +76 -0
  361. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-onboard/SKILL.md +560 -0
  362. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-propose/SKILL.md +162 -0
  363. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-sync-specs/SKILL.md +261 -0
  364. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-update-change/SKILL.md +90 -0
  365. package/node_modules/@freeharness/sdd/vendor/openspec/assets/skills/openspec-verify-change/SKILL.md +174 -0
  366. package/package.json +70 -0
  367. package/provenance/canonical.PROVENANCE.yaml +740 -0
  368. package/provenance/claude-mem.LICENSE.upstream +202 -0
  369. package/provenance/memory.PROVENANCE.yaml +399 -0
  370. package/provenance/sdd.PROVENANCE.yaml +175 -0
  371. package/vendor-licenses/caveman.LICENSE +27 -0
  372. package/vendor-licenses/deepseek-harness.LICENSE +21 -0
  373. package/vendor-licenses/ecc.LICENSE +21 -0
  374. package/vendor-licenses/pi.LICENSE +21 -0
  375. package/vendor-licenses/superpowers.LICENSE +21 -0
@@ -0,0 +1,77 @@
1
+ import { buildCodeFenceMask } from './code-fence.js';
2
+ const REQUIREMENTS_SECTION_HEADER = /^##\s+Requirements\s*$/i;
3
+ const TOP_LEVEL_SECTION_HEADER = /^##\s+/;
4
+ const DELTA_HEADER = /^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements\s*$/i;
5
+ const REQUIREMENT_HEADER = /^###\s+Requirement:\s*(.+)\s*$/i;
6
+ export function findMainSpecStructureIssues(content) {
7
+ const normalized = content.replace(/\r\n?/g, '\n');
8
+ const stripped = stripFencedCodeBlocksPreservingLines(normalized);
9
+ const lines = stripped.split('\n');
10
+ const issues = [];
11
+ const requirementLines = new Map();
12
+ const requirementsHeaderIndex = lines.findIndex(line => REQUIREMENTS_SECTION_HEADER.test(line));
13
+ let requirementsEndIndex = lines.length;
14
+ if (requirementsHeaderIndex !== -1) {
15
+ for (let i = requirementsHeaderIndex + 1; i < lines.length; i++) {
16
+ if (TOP_LEVEL_SECTION_HEADER.test(lines[i])) {
17
+ requirementsEndIndex = i;
18
+ break;
19
+ }
20
+ }
21
+ }
22
+ for (let i = 0; i < lines.length; i++) {
23
+ const line = lines[i];
24
+ const trimmed = line.trim();
25
+ if (!trimmed) {
26
+ continue;
27
+ }
28
+ if (DELTA_HEADER.test(line)) {
29
+ issues.push({
30
+ kind: 'delta-header',
31
+ line: i + 1,
32
+ header: trimmed,
33
+ message: `Main spec contains delta header "${trimmed}". ` +
34
+ 'Delta headers are only valid inside openspec/changes/<name>/specs/<capability-path>/spec.md ' +
35
+ 'and truncate the parsed ## Requirements section.',
36
+ });
37
+ continue;
38
+ }
39
+ const requirementMatch = line.match(REQUIREMENT_HEADER);
40
+ if (!requirementMatch) {
41
+ continue;
42
+ }
43
+ const insideRequirements = requirementsHeaderIndex !== -1 &&
44
+ i > requirementsHeaderIndex &&
45
+ i < requirementsEndIndex;
46
+ if (!insideRequirements) {
47
+ issues.push({
48
+ kind: 'requirement-outside-requirements',
49
+ line: i + 1,
50
+ header: trimmed,
51
+ message: `Requirement header "${trimmed}" appears outside the main ## Requirements section. ` +
52
+ 'Main specs only parse requirements inside that section, so this requirement is currently invisible to validate, list, and archive.',
53
+ });
54
+ continue;
55
+ }
56
+ const requirementName = requirementMatch[1].trim();
57
+ const previousLine = requirementLines.get(requirementName);
58
+ if (previousLine !== undefined) {
59
+ issues.push({
60
+ kind: 'duplicate-requirement',
61
+ line: i + 1,
62
+ header: trimmed,
63
+ message: `Requirement header "${trimmed}" duplicates the requirement declared on line ${previousLine}. ` +
64
+ 'Requirement names must be unique so spec updates cannot discard one block while updating another.',
65
+ });
66
+ }
67
+ else {
68
+ requirementLines.set(requirementName, i + 1);
69
+ }
70
+ }
71
+ return issues;
72
+ }
73
+ export function stripFencedCodeBlocksPreservingLines(content) {
74
+ const lines = content.split('\n');
75
+ const mask = buildCodeFenceMask(lines);
76
+ return lines.map((line, i) => (mask[i] ? '' : line)).join('\n');
77
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Detects a `## Purpose` that is still a placeholder, rather than one somebody
3
+ * wrote.
4
+ *
5
+ * When a delta introduces a capability with no usable `## Purpose`, archive
6
+ * stamps the placeholder into the new main spec. That text is over
7
+ * `MIN_PURPOSE_LENGTH`, so the brevity check cannot reach it: the one rule that
8
+ * exists to catch a Purpose nobody wrote is satisfied by the exact string
9
+ * meaning nobody wrote one. Nothing else reads it afterwards, so the capability
10
+ * keeps a to-do in it while every command reports success.
11
+ *
12
+ * Two things count, and deliberately nothing else:
13
+ *
14
+ * - the placeholder this tool generates, recognised through the same constants
15
+ * the writer composes it from, wherever it sits in the Purpose - nobody types
16
+ * that sentence by accident;
17
+ * - a `TBD` or `TODO` **opening** the Purpose, which is the marker left behind
18
+ * when someone is told to leave "a brief TBD placeholder" and never comes
19
+ * back. Which of the two words got typed says nothing about whether the
20
+ * Purpose was written, so both are read the same way.
21
+ *
22
+ * A marker inside a sentence is left alone. "The retry budget is TBD pending
23
+ * benchmarks" is a real Purpose with an open question in it, and reporting it
24
+ * would teach people to ignore the warning - which costs more than the findings
25
+ * it would add.
26
+ *
27
+ * Fenced code inside the Purpose is quoted material rather than the Purpose
28
+ * speaking, so it is read out first. Without that, a Purpose documenting the
29
+ * sentence archive writes is reported as being that sentence: a document about
30
+ * the placeholder, failing for carrying one.
31
+ */
32
+ export interface PurposePlaceholderIssue {
33
+ /** 1-based line of the placeholder text, when it can be located. */
34
+ line?: number;
35
+ }
36
+ /**
37
+ * Reports the Purpose of a main spec as an unwritten placeholder, or null when
38
+ * it reads as authored content.
39
+ *
40
+ * An empty Purpose is not reported here - `SPEC_PURPOSE_EMPTY` already covers
41
+ * it, and reporting both would put two findings on one line. That falls out of
42
+ * the two rules rather than needing a case of its own.
43
+ */
44
+ export declare function findPurposePlaceholderIssue(overview: string, content?: string): PurposePlaceholderIssue | null;
@@ -0,0 +1,116 @@
1
+ import { buildCodeFenceMask } from './parsers/code-fence.js';
2
+ import { PURPOSE_PLACEHOLDER_PREFIX, PURPOSE_PLACEHOLDER_SUFFIX } from './constants.js';
3
+ /**
4
+ * A `TBD` or `TODO` opening the Purpose. The lookahead keeps it off a longer
5
+ * word that merely begins with those letters, like "TBDs" or "TODOs", while
6
+ * still allowing the punctuation a marker is usually written with: `TODO:`,
7
+ * `TBD -`. It rejects any letter, digit or combining mark rather than only the
8
+ * ASCII ones `\b` knows about, because a Purpose is prose and prose is not
9
+ * always written in Latin script - `TBD` followed by an Arabic-Indic digit is
10
+ * as much a longer word as `TBDs` is.
11
+ */
12
+ const LEADING_MARKER = /^(?:TBD|TODO)(?![\p{L}\p{N}\p{M}_])/iu;
13
+ const PURPOSE_HEADER = /^ {0,3}##(?!#)[ \t]+Purpose[ \t]*$/i;
14
+ const TOP_LEVEL_HEADER = /^ {0,3}#{1,2}(?!#)[ \t]+/;
15
+ /**
16
+ * The lines of `text` that sit outside a fenced code block, with line endings
17
+ * normalised first.
18
+ *
19
+ * `buildCodeFenceMask` is the masker the requirement and structure parsers
20
+ * already share, and its own reason for existing is that a second, private
21
+ * notion of what a fence is drifts from the first. This check reads Markdown
22
+ * for the same purpose they do, so it reads fences the same way they do.
23
+ */
24
+ function unfencedLines(text) {
25
+ const lines = text.replace(/\r\n?/g, '\n').split('\n');
26
+ const fenced = buildCodeFenceMask(lines);
27
+ return lines.filter((_, index) => !fenced[index]);
28
+ }
29
+ /**
30
+ * True when the text carries the sentence archive writes. Matched as its two
31
+ * fixed halves in order, because the change name between them varies - so the
32
+ * check follows the writer's own definition instead of a second copy of it.
33
+ */
34
+ function generatedPlaceholderPrefixIndex(text) {
35
+ let suffixAt = text.indexOf(PURPOSE_PLACEHOLDER_SUFFIX);
36
+ while (suffixAt !== -1) {
37
+ // Use the closest prefix before this suffix. An authored explanation can
38
+ // mention the prefix above the real placeholder; choosing the first prefix
39
+ // would then point the diagnostic at the explanation instead of the text
40
+ // the user needs to replace.
41
+ const prefixAt = text.lastIndexOf(PURPOSE_PLACEHOLDER_PREFIX, suffixAt);
42
+ if (prefixAt !== -1)
43
+ return prefixAt;
44
+ suffixAt = text.indexOf(PURPOSE_PLACEHOLDER_SUFFIX, suffixAt + 1);
45
+ }
46
+ return undefined;
47
+ }
48
+ /**
49
+ * Reports the Purpose of a main spec as an unwritten placeholder, or null when
50
+ * it reads as authored content.
51
+ *
52
+ * An empty Purpose is not reported here - `SPEC_PURPOSE_EMPTY` already covers
53
+ * it, and reporting both would put two findings on one line. That falls out of
54
+ * the two rules rather than needing a case of its own.
55
+ */
56
+ export function findPurposePlaceholderIssue(overview, content) {
57
+ // An empty Purpose needs no branch of its own: neither rule matches empty
58
+ // text, so it falls through to null on the line below. An early return for it
59
+ // would be a guard no test could hold, which is worse than none. A Purpose
60
+ // that is nothing but a fenced block reduces to the same empty text here, and
61
+ // is left to the brevity and empty-Purpose rules for the same reason.
62
+ const prose = unfencedLines(overview).join('\n').trim();
63
+ const leading = LEADING_MARKER.test(prose);
64
+ if (!leading && generatedPlaceholderPrefixIndex(prose) === undefined)
65
+ return null;
66
+ // Which rule matched decides where the placeholder is, so the locator is told.
67
+ // When both match the leading marker wins: it sits at or above the generated
68
+ // sentence, and the earliest marker is the one a reader scanning down meets.
69
+ return { line: content === undefined ? undefined : findPlaceholderLine(content, leading) };
70
+ }
71
+ /**
72
+ * The line inside the `## Purpose` section carrying the placeholder, so the
73
+ * warning points at the text to replace rather than at the file.
74
+ *
75
+ * Which line that is depends on the rule that matched. A leading marker is the
76
+ * section's first non-blank line by definition. The generated sentence is not:
77
+ * it can follow prose somebody wrote, and naming the first non-blank line then
78
+ * points at that prose - a line the reader can see is fine, which reads as the
79
+ * check being wrong rather than the Purpose being unwritten.
80
+ *
81
+ * Fenced lines are skipped on the way, for the reason detection skips them, and
82
+ * so a `## Requirements` quoted inside a fence cannot end the section early.
83
+ *
84
+ * Undefined when the placeholder cannot be located - no section header, or a
85
+ * generated sentence no single line carries. The caller then reports the finding
86
+ * without a line rather than with a guessed one, since a wrong line number is
87
+ * worse than none.
88
+ *
89
+ * Line endings are normalised first, so the same spec reports the same line
90
+ * whether it was saved on Windows or on macOS/Linux.
91
+ */
92
+ function findPlaceholderLine(content, leading) {
93
+ const lines = content.replace(/\r\n?/g, '\n').split('\n');
94
+ const fenced = buildCodeFenceMask(lines);
95
+ const headerIndex = lines.findIndex((line, index) => !fenced[index] && PURPOSE_HEADER.test(line));
96
+ if (headerIndex === -1)
97
+ return undefined;
98
+ const purposeLines = [];
99
+ for (let i = headerIndex + 1; i < lines.length; i++) {
100
+ if (fenced[i])
101
+ continue;
102
+ if (TOP_LEVEL_HEADER.test(lines[i]))
103
+ break;
104
+ if (leading && lines[i].trim())
105
+ return i + 1;
106
+ purposeLines.push({ line: i + 1, text: lines[i] });
107
+ }
108
+ if (leading)
109
+ return undefined;
110
+ const purpose = purposeLines.map(({ text }) => text).join('\n');
111
+ const prefixAt = generatedPlaceholderPrefixIndex(purpose);
112
+ if (prefixAt === undefined)
113
+ return undefined;
114
+ const lineOffset = purpose.slice(0, prefixAt).split('\n').length - 1;
115
+ return purposeLines[lineOffset]?.line;
116
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ export declare const ScenarioSchema: z.ZodObject<{
3
+ rawText: z.ZodString;
4
+ }, z.core.$strip>;
5
+ export declare const RequirementSchema: z.ZodObject<{
6
+ text: z.ZodString;
7
+ scenarios: z.ZodArray<z.ZodObject<{
8
+ rawText: z.ZodString;
9
+ }, z.core.$strip>>;
10
+ }, z.core.$strip>;
11
+ export type Scenario = z.infer<typeof ScenarioSchema>;
12
+ export type Requirement = z.infer<typeof RequirementSchema>;
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ import { VALIDATION_MESSAGES } from '../constants.js';
3
+ export const ScenarioSchema = z.object({
4
+ rawText: z.string().min(1, VALIDATION_MESSAGES.SCENARIO_EMPTY),
5
+ });
6
+ export const RequirementSchema = z.object({
7
+ // SHALL/MUST body-keyword enforcement lives in the imperative validator
8
+ // (Validator.applySpecRules), not here: the parser collapses the requirement
9
+ // header into `text`, so a Zod refine on `text` cannot tell "keyword in header
10
+ // only" from "keyword in body" and emits a misleading generic error. The
11
+ // validator recovers the header and emits the targeted hint for both the
12
+ // main-spec and change-delta paths (#1156).
13
+ text: z.string()
14
+ .min(1, VALIDATION_MESSAGES.REQUIREMENT_EMPTY),
15
+ scenarios: z.array(ScenarioSchema)
16
+ .min(1, VALIDATION_MESSAGES.REQUIREMENT_NO_SCENARIOS),
17
+ });
@@ -0,0 +1,72 @@
1
+ import { z } from 'zod';
2
+ export declare const DeltaOperationType: z.ZodEnum<{
3
+ ADDED: "ADDED";
4
+ MODIFIED: "MODIFIED";
5
+ REMOVED: "REMOVED";
6
+ RENAMED: "RENAMED";
7
+ }>;
8
+ export declare const DeltaSchema: z.ZodObject<{
9
+ spec: z.ZodString;
10
+ operation: z.ZodEnum<{
11
+ ADDED: "ADDED";
12
+ MODIFIED: "MODIFIED";
13
+ REMOVED: "REMOVED";
14
+ RENAMED: "RENAMED";
15
+ }>;
16
+ description: z.ZodString;
17
+ requirement: z.ZodOptional<z.ZodObject<{
18
+ text: z.ZodString;
19
+ scenarios: z.ZodArray<z.ZodObject<{
20
+ rawText: z.ZodString;
21
+ }, z.core.$strip>>;
22
+ }, z.core.$strip>>;
23
+ requirements: z.ZodOptional<z.ZodArray<z.ZodObject<{
24
+ text: z.ZodString;
25
+ scenarios: z.ZodArray<z.ZodObject<{
26
+ rawText: z.ZodString;
27
+ }, z.core.$strip>>;
28
+ }, z.core.$strip>>>;
29
+ rename: z.ZodOptional<z.ZodObject<{
30
+ from: z.ZodString;
31
+ to: z.ZodString;
32
+ }, z.core.$strip>>;
33
+ }, z.core.$strip>;
34
+ export declare const ChangeSchema: z.ZodObject<{
35
+ name: z.ZodString;
36
+ why: z.ZodString;
37
+ whatChanges: z.ZodString;
38
+ deltas: z.ZodArray<z.ZodObject<{
39
+ spec: z.ZodString;
40
+ operation: z.ZodEnum<{
41
+ ADDED: "ADDED";
42
+ MODIFIED: "MODIFIED";
43
+ REMOVED: "REMOVED";
44
+ RENAMED: "RENAMED";
45
+ }>;
46
+ description: z.ZodString;
47
+ requirement: z.ZodOptional<z.ZodObject<{
48
+ text: z.ZodString;
49
+ scenarios: z.ZodArray<z.ZodObject<{
50
+ rawText: z.ZodString;
51
+ }, z.core.$strip>>;
52
+ }, z.core.$strip>>;
53
+ requirements: z.ZodOptional<z.ZodArray<z.ZodObject<{
54
+ text: z.ZodString;
55
+ scenarios: z.ZodArray<z.ZodObject<{
56
+ rawText: z.ZodString;
57
+ }, z.core.$strip>>;
58
+ }, z.core.$strip>>>;
59
+ rename: z.ZodOptional<z.ZodObject<{
60
+ from: z.ZodString;
61
+ to: z.ZodString;
62
+ }, z.core.$strip>>;
63
+ }, z.core.$strip>>;
64
+ metadata: z.ZodOptional<z.ZodObject<{
65
+ version: z.ZodDefault<z.ZodString>;
66
+ format: z.ZodLiteral<"openspec-change">;
67
+ sourcePath: z.ZodOptional<z.ZodString>;
68
+ }, z.core.$strip>>;
69
+ }, z.core.$strip>;
70
+ export type DeltaOperation = z.infer<typeof DeltaOperationType>;
71
+ export type Delta = z.infer<typeof DeltaSchema>;
72
+ export type Change = z.infer<typeof ChangeSchema>;
@@ -0,0 +1,30 @@
1
+ import { z } from 'zod';
2
+ import { RequirementSchema } from './base.schema.js';
3
+ import { MIN_WHY_SECTION_LENGTH, MAX_WHY_SECTION_LENGTH, MAX_DELTAS_PER_CHANGE, VALIDATION_MESSAGES } from '../constants.js';
4
+ export const DeltaOperationType = z.enum(['ADDED', 'MODIFIED', 'REMOVED', 'RENAMED']);
5
+ export const DeltaSchema = z.object({
6
+ spec: z.string().min(1, VALIDATION_MESSAGES.DELTA_SPEC_EMPTY),
7
+ operation: DeltaOperationType,
8
+ description: z.string().min(1, VALIDATION_MESSAGES.DELTA_DESCRIPTION_EMPTY),
9
+ requirement: RequirementSchema.optional(),
10
+ requirements: z.array(RequirementSchema).optional(),
11
+ rename: z.object({
12
+ from: z.string(),
13
+ to: z.string(),
14
+ }).optional(),
15
+ });
16
+ export const ChangeSchema = z.object({
17
+ name: z.string().min(1, VALIDATION_MESSAGES.CHANGE_NAME_EMPTY),
18
+ why: z.string()
19
+ .min(MIN_WHY_SECTION_LENGTH, VALIDATION_MESSAGES.CHANGE_WHY_TOO_SHORT)
20
+ .max(MAX_WHY_SECTION_LENGTH, VALIDATION_MESSAGES.CHANGE_WHY_TOO_LONG),
21
+ whatChanges: z.string().min(1, VALIDATION_MESSAGES.CHANGE_WHAT_EMPTY),
22
+ deltas: z.array(DeltaSchema)
23
+ .min(1, VALIDATION_MESSAGES.CHANGE_NO_DELTAS)
24
+ .max(MAX_DELTAS_PER_CHANGE, VALIDATION_MESSAGES.CHANGE_TOO_MANY_DELTAS),
25
+ metadata: z.object({
26
+ version: z.string().default('1.0.0'),
27
+ format: z.literal('openspec-change'),
28
+ sourcePath: z.string().optional(),
29
+ }).optional(),
30
+ });
@@ -0,0 +1,3 @@
1
+ export { ScenarioSchema, RequirementSchema, type Scenario, type Requirement, } from './base.schema.js';
2
+ export { SpecSchema, type Spec, } from './spec.schema.js';
3
+ export { DeltaOperationType, DeltaSchema, ChangeSchema, type DeltaOperation, type Delta, type Change, } from './change.schema.js';
@@ -0,0 +1,3 @@
1
+ export { ScenarioSchema, RequirementSchema, } from './base.schema.js';
2
+ export { SpecSchema, } from './spec.schema.js';
3
+ export { DeltaOperationType, DeltaSchema, ChangeSchema, } from './change.schema.js';
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ export declare const SpecSchema: z.ZodObject<{
3
+ name: z.ZodString;
4
+ overview: z.ZodString;
5
+ requirements: z.ZodArray<z.ZodObject<{
6
+ text: z.ZodString;
7
+ scenarios: z.ZodArray<z.ZodObject<{
8
+ rawText: z.ZodString;
9
+ }, z.core.$strip>>;
10
+ }, z.core.$strip>>;
11
+ metadata: z.ZodOptional<z.ZodObject<{
12
+ version: z.ZodDefault<z.ZodString>;
13
+ format: z.ZodLiteral<"openspec">;
14
+ sourcePath: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$strip>>;
16
+ }, z.core.$strip>;
17
+ export type Spec = z.infer<typeof SpecSchema>;
@@ -0,0 +1,14 @@
1
+ import { z } from 'zod';
2
+ import { RequirementSchema } from './base.schema.js';
3
+ import { VALIDATION_MESSAGES } from '../constants.js';
4
+ export const SpecSchema = z.object({
5
+ name: z.string().min(1, VALIDATION_MESSAGES.SPEC_NAME_EMPTY),
6
+ overview: z.string().min(1, VALIDATION_MESSAGES.SPEC_PURPOSE_EMPTY),
7
+ requirements: z.array(RequirementSchema)
8
+ .min(1, VALIDATION_MESSAGES.SPEC_NO_REQUIREMENTS),
9
+ metadata: z.object({
10
+ version: z.string().default('1.0.0'),
11
+ format: z.literal('openspec'),
12
+ sourcePath: z.string().optional(),
13
+ }).optional(),
14
+ });
@@ -0,0 +1,39 @@
1
+ export interface DiscoveredSpec {
2
+ /** Spec id relative to the specs root, forward-slash separated on every platform (e.g. "web" or "platform/session-layout"). */
3
+ id: string;
4
+ /** Path to the spec.md file (absolute if the specs root is absolute). */
5
+ specFile: string;
6
+ }
7
+ /**
8
+ * Recursively discover every `spec.md` under a specs root, so both the flat
9
+ * `specs/<id>/spec.md` layout and nested `specs/<area>/<id>/spec.md` layouts
10
+ * are found (#1353). A `spec.md` sitting directly in the root is ignored,
11
+ * matching the historical requirement that specs live in a capability folder.
12
+ * Dot-directories are skipped and symlinked directories are not followed.
13
+ * An in-capability symlinked `spec.md` IS resolved: `hasAnyFileUnder` and the
14
+ * artifact graph's globs both count it as content, so dropping it here would
15
+ * silently lose the delta on archive. A link outside its capability is
16
+ * rejected and a dangling link is skipped. Results are sorted by id.
17
+ *
18
+ * A missing root (ENOENT) yields an empty list, but any other read failure
19
+ * (EACCES, EIO, ...) is thrown rather than swallowed: since this feeds the
20
+ * archive/apply merge path, silently dropping an unreadable capability would
21
+ * recreate the exact data-loss class #1353 is closing.
22
+ */
23
+ export declare function discoverSpecFiles(specsRoot: string): Promise<DiscoveredSpec[]>;
24
+ /**
25
+ * True when any regular non-dot file exists anywhere under the given
26
+ * directory. Used by validate/archive to detect content under a change's
27
+ * specs/ that contradicts a declared skip_specs marker - including files that
28
+ * discoverSpecFiles ignores (a root spec.md, stray non-spec.md notes), since
29
+ * anything there would be silently dropped or misread while the change claims
30
+ * to have nothing. Dot entries (.DS_Store, .gitkeep, dot-directories) are
31
+ * skipped to match discoverSpecFiles - they are invisible to every other
32
+ * code path, so they must not count as spec content. Symlinks DO count
33
+ * (without being followed): the artifact graph's globs follow them, so a
34
+ * symlinked spec would read as existing content while the change claims to
35
+ * have none - it contradicts the marker like any regular file. A missing
36
+ * directory returns false; other read failures are thrown for the caller to
37
+ * decide.
38
+ */
39
+ export declare function hasAnyFileUnder(dirPath: string): Promise<boolean>;
@@ -0,0 +1,114 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { FileSystemUtils } from '../engine/file-system.js';
4
+ function assertDiscoveredSpecPath(specsRoot, capabilityDir, specFile) {
5
+ try {
6
+ FileSystemUtils.assertPathWithin(specsRoot, specFile);
7
+ }
8
+ catch {
9
+ // Direct capability directories may intentionally be external monorepo
10
+ // links. In that case, confine the file to the capability itself.
11
+ FileSystemUtils.assertPathWithin(capabilityDir, specFile);
12
+ }
13
+ }
14
+ /**
15
+ * Recursively discover every `spec.md` under a specs root, so both the flat
16
+ * `specs/<id>/spec.md` layout and nested `specs/<area>/<id>/spec.md` layouts
17
+ * are found (#1353). A `spec.md` sitting directly in the root is ignored,
18
+ * matching the historical requirement that specs live in a capability folder.
19
+ * Dot-directories are skipped and symlinked directories are not followed.
20
+ * An in-capability symlinked `spec.md` IS resolved: `hasAnyFileUnder` and the
21
+ * artifact graph's globs both count it as content, so dropping it here would
22
+ * silently lose the delta on archive. A link outside its capability is
23
+ * rejected and a dangling link is skipped. Results are sorted by id.
24
+ *
25
+ * A missing root (ENOENT) yields an empty list, but any other read failure
26
+ * (EACCES, EIO, ...) is thrown rather than swallowed: since this feeds the
27
+ * archive/apply merge path, silently dropping an unreadable capability would
28
+ * recreate the exact data-loss class #1353 is closing.
29
+ */
30
+ export async function discoverSpecFiles(specsRoot) {
31
+ const results = [];
32
+ const walk = async (dir, segments) => {
33
+ let entries;
34
+ try {
35
+ entries = await fs.readdir(dir, { withFileTypes: true });
36
+ }
37
+ catch (err) {
38
+ if (err?.code === 'ENOENT')
39
+ return;
40
+ throw err;
41
+ }
42
+ for (const entry of entries) {
43
+ if (entry.name.startsWith('.'))
44
+ continue;
45
+ if (entry.isDirectory()) {
46
+ await walk(path.join(dir, entry.name), [...segments, entry.name]);
47
+ }
48
+ else if (entry.name === 'spec.md' && segments.length > 0) {
49
+ const specFile = path.join(dir, entry.name);
50
+ if (entry.isFile()) {
51
+ assertDiscoveredSpecPath(specsRoot, dir, specFile);
52
+ results.push({ id: segments.join('/'), specFile });
53
+ }
54
+ else if (entry.isSymbolicLink()) {
55
+ try {
56
+ if ((await fs.stat(specFile)).isFile()) {
57
+ assertDiscoveredSpecPath(specsRoot, dir, specFile);
58
+ results.push({ id: segments.join('/'), specFile });
59
+ }
60
+ }
61
+ catch (err) {
62
+ // A dangling link is not content; anything else fails loudly.
63
+ if (err?.code !== 'ENOENT')
64
+ throw err;
65
+ }
66
+ }
67
+ }
68
+ }
69
+ };
70
+ await walk(specsRoot, []);
71
+ // Plain code-point comparison, not localeCompare: the latter follows the
72
+ // process's ICU locale, so ordering could vary by OS/CI. Code-point ordering
73
+ // guarantees the deterministic output the docstring promises.
74
+ return results.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
75
+ }
76
+ /**
77
+ * True when any regular non-dot file exists anywhere under the given
78
+ * directory. Used by validate/archive to detect content under a change's
79
+ * specs/ that contradicts a declared skip_specs marker - including files that
80
+ * discoverSpecFiles ignores (a root spec.md, stray non-spec.md notes), since
81
+ * anything there would be silently dropped or misread while the change claims
82
+ * to have nothing. Dot entries (.DS_Store, .gitkeep, dot-directories) are
83
+ * skipped to match discoverSpecFiles - they are invisible to every other
84
+ * code path, so they must not count as spec content. Symlinks DO count
85
+ * (without being followed): the artifact graph's globs follow them, so a
86
+ * symlinked spec would read as existing content while the change claims to
87
+ * have none - it contradicts the marker like any regular file. A missing
88
+ * directory returns false; other read failures are thrown for the caller to
89
+ * decide.
90
+ */
91
+ export async function hasAnyFileUnder(dirPath) {
92
+ let entries;
93
+ try {
94
+ entries = await fs.readdir(dirPath, { withFileTypes: true });
95
+ }
96
+ catch (err) {
97
+ if (err?.code === 'ENOENT') {
98
+ return false;
99
+ }
100
+ throw err;
101
+ }
102
+ for (const entry of entries) {
103
+ if (entry.name.startsWith('.')) {
104
+ continue;
105
+ }
106
+ if (entry.isFile() || entry.isSymbolicLink()) {
107
+ return true;
108
+ }
109
+ if (entry.isDirectory() && (await hasAnyFileUnder(path.join(dirPath, entry.name)))) {
110
+ return true;
111
+ }
112
+ }
113
+ return false;
114
+ }
@@ -0,0 +1,45 @@
1
+ export interface ParsedTask {
2
+ /** Checkbox state: `[x]`/`[X]` is done, anything else is not. */
3
+ done: boolean;
4
+ /** Task text after the checkbox, trimmed (may be empty). */
5
+ description: string;
6
+ }
7
+ /**
8
+ * Parses every task line in a tasks file, in document order. Every line matching
9
+ * the pattern counts, wherever it sits (inside a code fence, HTML comment, or an
10
+ * indented block), matching upstream.
11
+ */
12
+ export declare function parseTaskLines(content: string): ParsedTask[];
13
+ /** The annotation keys the tasks template documents. */
14
+ export declare const TASK_ANNOTATION_KEYS: readonly ["Verify", "Interfaces", "Boundary", "Depends", "Disposition"];
15
+ export type TaskAnnotationKey = (typeof TASK_ANNOTATION_KEYS)[number];
16
+ /**
17
+ * A task resolved outside this change: `superseded`/`moved` name another
18
+ * change id, `external` carries free-form reference text. `kind` is the raw
19
+ * first token of the `Disposition:` value, so a value outside the three
20
+ * documented kinds still parses (a reader such as `validate --strict` reports
21
+ * it, rather than the parser silently dropping the line).
22
+ */
23
+ export interface TaskDisposition {
24
+ kind: string;
25
+ target: string;
26
+ }
27
+ export interface AnnotatedTask extends ParsedTask {
28
+ /** 1-based line of the checkbox in the tasks file. */
29
+ line: number;
30
+ /** Annotation lines that followed the checkbox, keyed by their label. */
31
+ annotations: Partial<Record<TaskAnnotationKey, string>>;
32
+ /** Parsed `Disposition:` line, or null when the task carries none. */
33
+ disposition: TaskDisposition | null;
34
+ }
35
+ /**
36
+ * True when the line is one of the documented annotation lines. Such a line is
37
+ * never a task, whatever precedes it.
38
+ */
39
+ export declare function isTaskAnnotationLine(line: string): boolean;
40
+ /**
41
+ * Parses task lines and attaches the annotation lines that directly follow each
42
+ * task (blank lines and non-annotation prose end the run). The task count is
43
+ * exactly `parseTaskLines(content).length`.
44
+ */
45
+ export declare function parseAnnotatedTaskLines(content: string): AnnotatedTask[];