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,229 @@
1
+ // `openspec validate [<item>] [--specs] [--strict] [--json]` over the vendored
2
+ // change-validation slice.
3
+ //
4
+ // Reconstructed from src/commands/validate.ts, minus store/root-selection and
5
+ // the interactive selector. Uses the vendored Validator directly so the JSON
6
+ // shape matches upstream: { items: [{ id, type, valid, issues, durationMs }],
7
+ // summary: { totals, byType }, version }. Type is auto-detected (a change dir
8
+ // under openspec/changes wins; else a spec under openspec/specs). `--specs`
9
+ // (or a bare `validate`) runs the matching bulk set.
10
+ //
11
+ // Metadata depends_on/supersedes: the vendored Validator does not read
12
+ // .openspec.yaml, so a change item's issues are extended here with an ERROR for
13
+ // every unresolved id in either list, and a notice for a depends_on change that
14
+ // is in flight with open tasks. That notice is WARNING under the default
15
+ // (non-strict) verdict, where WARNING never affects validity; under --strict,
16
+ // where the vendored Validator's own strict semantics fail a change on any
17
+ // WARNING, it downgrades to INFO so a normal in-flight dependency never trips
18
+ // --strict on its own (an unresolved id stays ERROR either way).
19
+ import * as fs from 'node:fs';
20
+ import * as path from 'node:path';
21
+ import { Validator } from '../../vendor/openspec/validation/validator.js';
22
+ import { discoverSpecFiles } from '../../vendor/openspec/validation/spec-discovery.js';
23
+ import { parseAnnotatedTaskLines } from '../../vendor/openspec/validation/task-lines.js';
24
+ import { readChangeMetadata } from '../../vendor/openspec/engine/change-metadata.js';
25
+ import { changesDirFor, getAvailableChanges, printJson, resolveChangeId, taskLabel } from './shared.js';
26
+ const METADATA_FILENAME = '.openspec.yaml';
27
+ const TASKS_FILENAME = 'tasks.md';
28
+ const DISPOSITION_KINDS = new Set(['superseded', 'moved', 'external']);
29
+ /**
30
+ * Extra issues from a change's `tasks.md` disposition lines: an ERROR for a
31
+ * disposition under a checked task, a kind outside the three documented ones,
32
+ * a `superseded`/`moved` id that resolves to neither an in-flight nor an
33
+ * archived change, and an `external` line without a reference.
34
+ */
35
+ function dispositionIssues(projectRoot, changeDir) {
36
+ const tasksPath = path.join(changeDir, TASKS_FILENAME);
37
+ if (!fs.existsSync(tasksPath))
38
+ return [];
39
+ const tasks = parseAnnotatedTaskLines(fs.readFileSync(tasksPath, 'utf-8'));
40
+ const issues = [];
41
+ for (const task of tasks) {
42
+ if (!task.disposition)
43
+ continue;
44
+ const label = taskLabel(task);
45
+ if (task.done) {
46
+ issues.push({
47
+ level: 'ERROR',
48
+ path: TASKS_FILENAME,
49
+ line: task.line,
50
+ message: `task ${label}: a disposition belongs to an unchecked task`,
51
+ });
52
+ continue;
53
+ }
54
+ const { kind, target } = task.disposition;
55
+ if (!DISPOSITION_KINDS.has(kind)) {
56
+ issues.push({
57
+ level: 'ERROR',
58
+ path: TASKS_FILENAME,
59
+ line: task.line,
60
+ message: `task ${label}: disposition kind '${kind}' is none of superseded, moved, external`,
61
+ });
62
+ }
63
+ else if (kind === 'external') {
64
+ if (target.trim() === '') {
65
+ issues.push({
66
+ level: 'ERROR',
67
+ path: TASKS_FILENAME,
68
+ line: task.line,
69
+ message: `task ${label}: an external disposition needs a reference`,
70
+ });
71
+ }
72
+ }
73
+ else if (resolveChangeId(projectRoot, target) === null) {
74
+ issues.push({
75
+ level: 'ERROR',
76
+ path: TASKS_FILENAME,
77
+ line: task.line,
78
+ message: `task ${label}: ${kind} disposition names unresolved change '${target}'`,
79
+ });
80
+ }
81
+ }
82
+ return issues;
83
+ }
84
+ /**
85
+ * Extra issues from a change's `depends_on` / `supersedes` metadata: an ERROR
86
+ * for metadata that cannot be read, an ERROR for every id that resolves to
87
+ * neither an in-flight nor an archived change, and a notice for a `depends_on`
88
+ * id that is in flight with open tasks.
89
+ */
90
+ function dependencyIssues(projectRoot, changeDir, strict) {
91
+ let metadata;
92
+ try {
93
+ metadata = readChangeMetadata(changeDir, projectRoot);
94
+ }
95
+ catch (error) {
96
+ // Metadata that cannot be read leaves depends_on and supersedes unresolved, so the file
97
+ // itself is the finding: reporting nothing would pass a change whose dependency gate was
98
+ // never run.
99
+ const reason = error instanceof Error ? error.message : String(error);
100
+ return [{ level: 'ERROR', path: METADATA_FILENAME, message: `unreadable: ${reason}` }];
101
+ }
102
+ if (!metadata)
103
+ return [];
104
+ const issues = [];
105
+ for (const id of metadata.depends_on ?? []) {
106
+ const resolution = resolveChangeId(projectRoot, id);
107
+ if (resolution === null) {
108
+ issues.push({ level: 'ERROR', path: METADATA_FILENAME, message: `depends_on names unresolved change '${id}'` });
109
+ continue;
110
+ }
111
+ if (resolution === 'in-flight') {
112
+ const tasksPath = path.join(changesDirFor(projectRoot), id, 'tasks.md');
113
+ // A dispositioned task is resolved, never open, so it never inflates
114
+ // this notice (it is not this check's job to also validate the
115
+ // disposition; an invalid one is still reported on the dependency's
116
+ // own `validate --strict` run).
117
+ const open = fs.existsSync(tasksPath)
118
+ ? parseAnnotatedTaskLines(fs.readFileSync(tasksPath, 'utf-8')).filter((t) => !t.done && !t.disposition)
119
+ : [];
120
+ if (open.length > 0) {
121
+ const level = strict ? 'INFO' : 'WARNING';
122
+ issues.push({
123
+ level,
124
+ path: METADATA_FILENAME,
125
+ message: `depends_on '${id}' is in flight with ${open.length} open task(s)`,
126
+ });
127
+ }
128
+ }
129
+ }
130
+ for (const id of metadata.supersedes ?? []) {
131
+ if (resolveChangeId(projectRoot, id) === null) {
132
+ issues.push({ level: 'ERROR', path: METADATA_FILENAME, message: `supersedes names unresolved change '${id}'` });
133
+ }
134
+ }
135
+ return issues;
136
+ }
137
+ async function validateChangeItem(validator, projectRoot, id, strict) {
138
+ const start = Date.now();
139
+ const changeDir = path.join(changesDirFor(projectRoot), id);
140
+ const report = await validator.validateChangeDeltaSpecs(changeDir, {
141
+ mainSpecsDir: path.join(projectRoot, 'openspec', 'specs'),
142
+ projectRoot,
143
+ });
144
+ const issues = [
145
+ ...report.issues,
146
+ ...dependencyIssues(projectRoot, changeDir, strict),
147
+ ...dispositionIssues(projectRoot, changeDir),
148
+ ];
149
+ const errors = issues.filter((i) => i.level === 'ERROR').length;
150
+ const warnings = issues.filter((i) => i.level === 'WARNING').length;
151
+ const valid = strict ? errors === 0 && warnings === 0 : errors === 0;
152
+ return { id, type: 'change', valid, issues, durationMs: Date.now() - start };
153
+ }
154
+ async function validateSpecItem(validator, projectRoot, id, specFile) {
155
+ const start = Date.now();
156
+ const file = specFile ?? path.join(projectRoot, 'openspec', 'specs', id, 'spec.md');
157
+ const report = await validator.validateSpec(file);
158
+ return { id, type: 'spec', valid: report.valid, issues: report.issues, durationMs: Date.now() - start };
159
+ }
160
+ export async function validateHandler(projectRoot, item, options) {
161
+ const validator = new Validator(!!options.strict);
162
+ const items = [];
163
+ const changeExists = (id) => {
164
+ const p = path.join(changesDirFor(projectRoot), id);
165
+ return fs.existsSync(p) && fs.statSync(p).isDirectory();
166
+ };
167
+ const specExists = (id) => fs.existsSync(path.join(projectRoot, 'openspec', 'specs', id, 'spec.md'));
168
+ if (item) {
169
+ if (changeExists(item)) {
170
+ items.push(await validateChangeItem(validator, projectRoot, item, !!options.strict));
171
+ }
172
+ else if (specExists(item)) {
173
+ items.push(await validateSpecItem(validator, projectRoot, item));
174
+ }
175
+ else {
176
+ throw new Error(`'${item}' is neither a known change nor a known spec.`);
177
+ }
178
+ }
179
+ else if (options.specs) {
180
+ const discovered = await discoverSpecFiles(path.join(projectRoot, 'openspec', 'specs'));
181
+ for (const { id, specFile } of discovered) {
182
+ items.push(await validateSpecItem(validator, projectRoot, id, specFile));
183
+ }
184
+ }
185
+ else {
186
+ // Bare `validate`: run every active change.
187
+ for (const id of getAvailableChanges(projectRoot)) {
188
+ items.push(await validateChangeItem(validator, projectRoot, id, !!options.strict));
189
+ }
190
+ }
191
+ const passed = items.filter((i) => i.valid).length;
192
+ const failed = items.length - passed;
193
+ const byType = {};
194
+ for (const i of items) {
195
+ const bucket = (byType[i.type] ??= { items: 0, passed: 0, failed: 0 });
196
+ bucket.items++;
197
+ if (i.valid)
198
+ bucket.passed++;
199
+ else
200
+ bucket.failed++;
201
+ }
202
+ const allValid = failed === 0;
203
+ if (options.json) {
204
+ printJson({
205
+ items,
206
+ summary: { totals: { items: items.length, passed, failed }, byType },
207
+ version: '1.0',
208
+ });
209
+ return allValid ? 0 : 1;
210
+ }
211
+ if (items.length === 0) {
212
+ console.log('Nothing to validate.');
213
+ return 0;
214
+ }
215
+ for (const i of items) {
216
+ const label = i.type === 'change' ? 'Change' : 'Specification';
217
+ if (i.valid) {
218
+ console.log(`${label} '${i.id}' is valid`);
219
+ }
220
+ else {
221
+ console.error(`${label} '${i.id}' has issues:`);
222
+ for (const issue of i.issues) {
223
+ const loc = issue.line ? `${issue.path}:${issue.line}` : issue.path;
224
+ console.error(` [${issue.level}] ${loc}: ${issue.message}`);
225
+ }
226
+ }
227
+ }
228
+ return allValid ? 0 : 1;
229
+ }
@@ -0,0 +1,40 @@
1
+ export interface ValidateChangeOptions {
2
+ /**
3
+ * Schema name to resolve+validate before checking the change. Defaults to
4
+ * `spec-driven`. Set to an empty string / null to skip schema resolution.
5
+ */
6
+ schemaName?: string | null;
7
+ /**
8
+ * A directory that directly contains `<schemaName>/schema.yaml`, forwarded to
9
+ * the engine resolver so a schema can be validated with no global install and
10
+ * no XDG data dir. Mirrors the CLI's optional [userSchemasDir] argument.
11
+ */
12
+ userSchemasDir?: string;
13
+ /**
14
+ * Strict mode: warnings fail validation in addition to errors (matches
15
+ * `openspec validate --strict`).
16
+ */
17
+ strict?: boolean;
18
+ }
19
+ export interface ValidateChangeResult {
20
+ valid: boolean;
21
+ /** One human-readable line per issue: `[LEVEL] path[:line]: message`. */
22
+ issues: string[];
23
+ }
24
+ /**
25
+ * Validate an OpenSpec change directory using only the vendored slice.
26
+ *
27
+ * The verdict is the authoritative one `openspec validate <change>` uses: the
28
+ * delta-spec pass (`validateChangeDeltaSpecs`) plus schema resolution. In
29
+ * non-strict mode a change is valid when that pass reports no ERROR; in strict
30
+ * mode, no ERROR and no WARNING. A schema-resolution failure fails the change,
31
+ * since a change cannot be trusted against an unresolvable schema.
32
+ *
33
+ * The proposal document is additionally parsed and validated (Why / What
34
+ * Changes prose + the top-level ChangeSchema), but its findings are ADVISORY
35
+ * and never flip the verdict — mirroring upstream, where the proposal-level
36
+ * pass is archive's non-blocking warning source, not a gate. (This is what
37
+ * keeps a well-formed change with more than ten requirement-deltas from being
38
+ * failed by the advisory "Consider splitting" ChangeSchema rule.)
39
+ */
40
+ export declare function validateChange(changeDir: string, opts?: ValidateChangeOptions): Promise<ValidateChangeResult>;
@@ -0,0 +1,114 @@
1
+ // Store-free change validation entry point built on the vendored OpenSpec
2
+ // change-validation slice (parsers + schemas + Validator).
3
+ //
4
+ // Runs with NO global `openspec` on PATH. Given a change directory
5
+ // (`openspec/changes/<id>`), it optionally resolves+validates the governing
6
+ // schema, then parses and validates the change artifacts:
7
+ // - specs/**/*.md delta specs (ADDED/MODIFIED/REMOVED/RENAMED)
8
+ // - proposal.md Why / What Changes prose + the top-level ChangeSchema
9
+ // - tasks.md ambiguous task-numbering check
10
+ // and returns a machine-readable result.
11
+ import * as fs from 'node:fs';
12
+ import * as path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ import { Validator } from '../vendor/openspec/validation/validator.js';
15
+ import { validateSchema } from './validate.js';
16
+ const DEFAULT_SCHEMA_NAME = 'spec-driven';
17
+ /** Render one validation issue as a stable, greppable single line. */
18
+ function formatIssue(issue, advisory = false) {
19
+ const location = issue.line ? `${issue.path}:${issue.line}` : issue.path;
20
+ const prefix = advisory ? '[advisory] ' : '';
21
+ return `${prefix}[${issue.level}] ${location}: ${issue.message}`;
22
+ }
23
+ /**
24
+ * Validate an OpenSpec change directory using only the vendored slice.
25
+ *
26
+ * The verdict is the authoritative one `openspec validate <change>` uses: the
27
+ * delta-spec pass (`validateChangeDeltaSpecs`) plus schema resolution. In
28
+ * non-strict mode a change is valid when that pass reports no ERROR; in strict
29
+ * mode, no ERROR and no WARNING. A schema-resolution failure fails the change,
30
+ * since a change cannot be trusted against an unresolvable schema.
31
+ *
32
+ * The proposal document is additionally parsed and validated (Why / What
33
+ * Changes prose + the top-level ChangeSchema), but its findings are ADVISORY
34
+ * and never flip the verdict — mirroring upstream, where the proposal-level
35
+ * pass is archive's non-blocking warning source, not a gate. (This is what
36
+ * keeps a well-formed change with more than ten requirement-deltas from being
37
+ * failed by the advisory "Consider splitting" ChangeSchema rule.)
38
+ */
39
+ export async function validateChange(changeDir, opts = {}) {
40
+ const strict = opts.strict ?? false;
41
+ const lines = [];
42
+ let schemaOk = true;
43
+ // 1. Resolve + validate the governing schema (unless explicitly disabled).
44
+ const schemaName = opts.schemaName === undefined ? DEFAULT_SCHEMA_NAME : opts.schemaName;
45
+ if (schemaName) {
46
+ const schemaResult = await validateSchema(schemaName, {
47
+ ...(opts.userSchemasDir ? { userSchemasDir: opts.userSchemasDir } : {}),
48
+ });
49
+ if (!schemaResult.valid) {
50
+ schemaOk = false;
51
+ lines.push(formatIssue({
52
+ level: 'ERROR',
53
+ path: 'schema',
54
+ message: `Schema '${schemaName}' failed to resolve/validate: ${schemaResult.message ?? 'unknown error'}`,
55
+ }));
56
+ }
57
+ }
58
+ // Derive the sibling main specs dir and project root from the change layout
59
+ // `<root>/openspec/changes/<id>`, so the scenario-loss and task-numbering
60
+ // checks run. Absent siblings degrade gracefully inside the Validator.
61
+ const resolvedChangeDir = path.resolve(changeDir);
62
+ const mainSpecsDir = path.resolve(resolvedChangeDir, '..', '..', 'specs');
63
+ const projectRoot = path.resolve(resolvedChangeDir, '..', '..', '..');
64
+ const validator = new Validator(strict);
65
+ // 2. Authoritative change validation: the delta specs.
66
+ const deltaReport = await validator.validateChangeDeltaSpecs(resolvedChangeDir, { mainSpecsDir, projectRoot });
67
+ lines.push(...deltaReport.issues.map((i) => formatIssue(i)));
68
+ // 3. Advisory: proposal document (Why / What Changes prose + ChangeSchema).
69
+ const proposalPath = path.join(resolvedChangeDir, 'proposal.md');
70
+ if (fs.existsSync(proposalPath)) {
71
+ const proposalReport = await validator.validateChange(proposalPath);
72
+ lines.push(...proposalReport.issues.map((i) => formatIssue(i, true)));
73
+ }
74
+ const valid = schemaOk && deltaReport.valid;
75
+ return { valid, issues: lines };
76
+ }
77
+ /**
78
+ * CLI: `node dist/src/validate-change.js <changeDir> [userSchemasDir]`
79
+ * Prints the verdict and every issue, exits 0 when valid and 1 otherwise.
80
+ */
81
+ async function main() {
82
+ const [, , changeDir, userSchemasDir] = process.argv;
83
+ if (!changeDir) {
84
+ console.error('usage: node dist/src/validate-change.js <changeDir> [userSchemasDir]');
85
+ process.exit(1);
86
+ return;
87
+ }
88
+ const result = await validateChange(changeDir, {
89
+ ...(userSchemasDir ? { userSchemasDir } : {}),
90
+ });
91
+ for (const issue of result.issues) {
92
+ console.log(issue);
93
+ }
94
+ if (result.valid) {
95
+ console.log(`\nvalid (${result.issues.length} non-error issue(s))`);
96
+ process.exit(0);
97
+ }
98
+ else {
99
+ console.error(`\ninvalid: ${result.issues.length} issue(s) reported`);
100
+ process.exit(1);
101
+ }
102
+ }
103
+ // Run as a CLI only when executed directly (not when imported as a module).
104
+ const invokedPath = process.argv[1];
105
+ if (invokedPath) {
106
+ try {
107
+ if (fileURLToPath(import.meta.url) === fs.realpathSync(invokedPath)) {
108
+ void main();
109
+ }
110
+ }
111
+ catch {
112
+ // If the entry path can't be resolved, don't auto-run.
113
+ }
114
+ }
@@ -0,0 +1,24 @@
1
+ export interface ValidateSchemaOptions {
2
+ /**
3
+ * A directory that directly contains `<schemaName>/schema.yaml`. When set and
4
+ * the file exists, it is validated in preference to the default resolution
5
+ * order. This mirrors the CLI's optional [userSchemasDir] argument and lets
6
+ * callers validate a schema with no global install and no XDG data dir.
7
+ */
8
+ userSchemasDir?: string;
9
+ /**
10
+ * Optional project root; forwarded to the engine resolver so a project-local
11
+ * `<projectDir>/openspec/schemas/<name>/schema.yaml` takes precedence.
12
+ */
13
+ projectDir?: string;
14
+ }
15
+ export interface ValidateSchemaResult {
16
+ valid: boolean;
17
+ message?: string;
18
+ }
19
+ /**
20
+ * Validates a schema by resolving its directory, loading `schema.yaml`, and
21
+ * running the vendored `parseSchema` (structure + duplicate-id + requires-ref +
22
+ * cycle checks).
23
+ */
24
+ export declare function validateSchema(schemaName: string, opts?: ValidateSchemaOptions): Promise<ValidateSchemaResult>;
@@ -0,0 +1,72 @@
1
+ // Store-free schema validation entry point built on the vendored OpenSpec engine.
2
+ //
3
+ // Runs with NO global `openspec` on PATH: it resolves the schema directory and
4
+ // parses/validates schema.yaml using only the vendored engine slice.
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { loadSchema, SchemaValidationError } from '../vendor/openspec/engine/schema.js';
9
+ import { resolveSchema, SchemaLoadError } from '../vendor/openspec/engine/resolver.js';
10
+ /**
11
+ * Validates a schema by resolving its directory, loading `schema.yaml`, and
12
+ * running the vendored `parseSchema` (structure + duplicate-id + requires-ref +
13
+ * cycle checks).
14
+ */
15
+ export async function validateSchema(schemaName, opts = {}) {
16
+ try {
17
+ // 1. Explicit user schemas dir override: <userSchemasDir>/<name>/schema.yaml.
18
+ if (opts.userSchemasDir) {
19
+ const schemaPath = path.join(opts.userSchemasDir, schemaName, 'schema.yaml');
20
+ if (fs.existsSync(schemaPath)) {
21
+ loadSchema(schemaPath); // throws SchemaValidationError on invalid content
22
+ return { valid: true };
23
+ }
24
+ // Fall through to the engine resolver when the override has no such schema.
25
+ }
26
+ // 2. Default resolution (project-local -> user data dir -> package built-in)
27
+ // plus parse/validation, all via the vendored engine.
28
+ resolveSchema(schemaName, opts.projectDir);
29
+ return { valid: true };
30
+ }
31
+ catch (err) {
32
+ const message = err instanceof SchemaValidationError || err instanceof SchemaLoadError
33
+ ? err.message
34
+ : err instanceof Error
35
+ ? err.message
36
+ : String(err);
37
+ return { valid: false, message };
38
+ }
39
+ }
40
+ /**
41
+ * CLI: `node dist/validate.js <schemaName> [userSchemasDir]`
42
+ * Prints "valid" and exits 0 on success, or the error and exits 1 on failure.
43
+ */
44
+ async function main() {
45
+ const [, , schemaName, userSchemasDir] = process.argv;
46
+ if (!schemaName) {
47
+ console.error('usage: node dist/validate.js <schemaName> [userSchemasDir]');
48
+ process.exit(1);
49
+ return;
50
+ }
51
+ const result = await validateSchema(schemaName, userSchemasDir ? { userSchemasDir } : {});
52
+ if (result.valid) {
53
+ console.log('valid');
54
+ process.exit(0);
55
+ }
56
+ else {
57
+ console.error(result.message ?? 'invalid schema');
58
+ process.exit(1);
59
+ }
60
+ }
61
+ // Run as a CLI only when executed directly (not when imported as a module).
62
+ const invokedPath = process.argv[1];
63
+ if (invokedPath) {
64
+ try {
65
+ if (fileURLToPath(import.meta.url) === fs.realpathSync(invokedPath)) {
66
+ void main();
67
+ }
68
+ }
69
+ catch {
70
+ // If the entry path can't be resolved, don't auto-run.
71
+ }
72
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Spec Application Logic
3
+ *
4
+ * Extracted from ArchiveCommand to enable standalone spec application.
5
+ * Applies delta specs from a change to main specs without archiving.
6
+ */
7
+ export interface SpecUpdate {
8
+ /** Capability id relative to the specs root, forward-slash separated (e.g. "web" or "platform/session-layout"). */
9
+ id: string;
10
+ /** Allowed root for the delta source. */
11
+ sourceRoot: string;
12
+ source: string;
13
+ /** Allowed root for the main-spec target. */
14
+ targetRoot: string;
15
+ target: string;
16
+ exists: boolean;
17
+ }
18
+ /**
19
+ * Find all delta spec files that need to be applied from a change.
20
+ */
21
+ export declare function findSpecUpdates(changeDir: string, mainSpecsDir: string): Promise<SpecUpdate[]>;
22
+ /**
23
+ * Build an updated spec by applying delta operations.
24
+ * Returns the rebuilt content and counts of operations.
25
+ */
26
+ export declare function buildUpdatedSpec(update: SpecUpdate, changeName: string, options?: {
27
+ silent?: boolean;
28
+ /**
29
+ * FreeHarness addition (`openspec archive --force`): a MODIFIED or RENAMED
30
+ * whose header is not in the current spec is skipped with a warning instead
31
+ * of aborting the fold. Upstream behavior (throw) when unset.
32
+ */
33
+ skipUnresolved?: boolean;
34
+ }): Promise<{
35
+ rebuilt: string;
36
+ counts: {
37
+ added: number;
38
+ modified: number;
39
+ removed: number;
40
+ renamed: number;
41
+ };
42
+ warnings: string[];
43
+ /**
44
+ * Every canonical `### Requirement:` block the delta could act on is gone.
45
+ * This is only a *candidate* signal for retirement (#1302): the validator, not
46
+ * this count, decides whether `rebuilt` is actually unwritable - it recognises
47
+ * requirement shapes this parser sweeps into the preamble, so a spec can be
48
+ * blockless here and still validate. See `isRetirableSpec` in archive.ts.
49
+ */
50
+ noRequirementBlocks: boolean;
51
+ /**
52
+ * Every non-blank line of the spec this merge cannot name.
53
+ *
54
+ * Retirement deletes the whole file, so the only safe question is whether the
55
+ * merge can account for all of it. `extractRequirementsSection` splits a spec
56
+ * into five slices, and auditing a subset is how this guard kept failing: for
57
+ * seven rounds it looked for requirement-SHAPED text and was beaten by a new
58
+ * disguise each time, and when it started asking where content landed it
59
+ * still read only the preamble and the tail - so content simply moved into a
60
+ * slice nobody checked, and authored prose sitting inside a removed block's
61
+ * raw was deleted while the report said only "Purpose" was lost.
62
+ *
63
+ * So this accounts for the whole file: the title, the `## Purpose` section,
64
+ * the `## Requirements` header, and, inside each requirement block, the parts
65
+ * that make up a requirement - its header, its statement, and its scenarios'
66
+ * bullets. Every other non-blank line is reported and refuses the retirement.
67
+ *
68
+ * Fails safe in every direction: a line this cannot classify counts as
69
+ * unaccounted, which refuses rather than deletes.
70
+ */
71
+ unaccountedContent: string[];
72
+ }>;
73
+ /**
74
+ * Retire a capability whose last requirement a delta removed: delete its main
75
+ * spec and prune any directories the deletion leaves empty. Returns false when
76
+ * there was nothing to delete.
77
+ *
78
+ * Gated by the caller on the change's `retire_capabilities` marker, so the one
79
+ * archive action that removes a file from `openspec/specs/` is always something
80
+ * the author asked for rather than something inferred from a delta's shape. The
81
+ * file is recoverable from git, which the report names; applying REMOVED already
82
+ * deletes requirement content from a main spec, so deleting the spec once
83
+ * nothing is left is the same operation carried to its end rather than a new
84
+ * kind of act.
85
+ *
86
+ * Only the generated `spec.md` is removed - a directory holding anything else (a
87
+ * nested capability, a hand-kept note) is left in place.
88
+ *
89
+ * The target must resolve inside the selected specs root. A capability-directory
90
+ * symlink must not turn a retirement marker into authorization to delete an
91
+ * unrelated external file. A symlinked `spec.md` itself is safe: unlink removes
92
+ * the link and leaves its target alone.
93
+ *
94
+ * Directory pruning IS bounded, by REAL paths rather than string prefixes:
95
+ * `path.resolve` collapses `..` but does not resolve symlinks, and `readdir` and
96
+ * `rmdir` both follow them, so a symlinked capability directory would otherwise
97
+ * let the walk delete directories outside the specs root entirely.
98
+ */
99
+ export declare function retireSpec(update: SpecUpdate, mainSpecsDir: string, options?: {
100
+ silent?: boolean;
101
+ displayPath?: string;
102
+ beforeMutate?: () => Promise<void>;
103
+ verifyDisplaced?: (displacedPath: string) => Promise<void>;
104
+ deferDelete?: boolean;
105
+ }): Promise<{
106
+ retired: boolean;
107
+ resolvedPath?: string;
108
+ displacedPath?: string;
109
+ }>;
110
+ export declare function finalizeRetiredSpec(target: string, displacedPath: string, mainSpecsDir: string): Promise<void>;
111
+ /**
112
+ * Write an updated spec to disk.
113
+ */
114
+ export declare function writeUpdatedSpec(update: SpecUpdate, rebuilt: string, counts: {
115
+ added: number;
116
+ modified: number;
117
+ removed: number;
118
+ renamed: number;
119
+ }, options?: {
120
+ silent?: boolean;
121
+ displayPath?: string;
122
+ beforeMutate?: () => Promise<void>;
123
+ }): Promise<void>;
124
+ /**
125
+ * Build a skeleton spec for new capabilities. When the delta spec authored a
126
+ * `## Purpose`, carry it over instead of the TBD placeholder (#1413) - archive
127
+ * invents the Purpose for a brand-new main spec either way, and the author's
128
+ * own wording beats a placeholder they then have to hand-edit.
129
+ */
130
+ export declare function buildSpecSkeleton(specFolderName: string, changeName: string, purpose?: string): string;