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,150 @@
1
+ // `openspec new change <id> [--schema <name>] [--json]` — store-free.
2
+ //
3
+ // Reconstructed from src/commands/workflow/new-change.ts + src/utils/change-utils.ts,
4
+ // minus store/root-selection and the project-config schema fallback. Schema
5
+ // resolution is: explicit --schema -> 'spec-driven'. Creating a change also
6
+ // scaffolds the surrounding root (specs/, changes/archive/, config.yaml) exactly
7
+ // like upstream, so a fresh openspec/ is left healthy. JSON shape matches
8
+ // upstream: { change: { id, path, metadataPath, schema } }.
9
+ import * as fs from 'node:fs';
10
+ import * as path from 'node:path';
11
+ import { writeChangeMetadata, METADATA_FILENAME, countTasksFromContent, } from '../../vendor/openspec/engine/index.js';
12
+ import { getAvailableChanges, planningHomeStub, printableId, printJson, validateSchemaExists } from './shared.js';
13
+ const DEFAULT_WIP_CAP = 1;
14
+ const WIP_CAP_FILE = '.freeharness/sdd.json';
15
+ /**
16
+ * Reads `wip_cap` from `.freeharness/sdd.json` at the project root: absent file
17
+ * means the default silently; an unreadable or unparseable file, or a `wip_cap`
18
+ * that is not a positive integer, means the default plus one INFO line naming
19
+ * the file. Read synchronously with a try/catch, never `require` or `import`.
20
+ */
21
+ function readWipCap(projectRoot) {
22
+ let raw;
23
+ try {
24
+ raw = fs.readFileSync(path.join(projectRoot, WIP_CAP_FILE), 'utf-8');
25
+ }
26
+ catch (error) {
27
+ const code = error.code;
28
+ if (code === 'ENOENT' || code === 'ENOTDIR')
29
+ return { cap: DEFAULT_WIP_CAP, info: null };
30
+ return { cap: DEFAULT_WIP_CAP, info: `INFO ${WIP_CAP_FILE}: wip_cap invalid, using ${DEFAULT_WIP_CAP}` };
31
+ }
32
+ try {
33
+ const parsed = JSON.parse(raw);
34
+ const cap = parsed.wip_cap;
35
+ if (typeof cap === 'number' && Number.isInteger(cap) && cap > 0) {
36
+ return { cap, info: null };
37
+ }
38
+ }
39
+ catch {
40
+ // falls through to the malformed-default below
41
+ }
42
+ return { cap: DEFAULT_WIP_CAP, info: `INFO ${WIP_CAP_FILE}: wip_cap invalid, using ${DEFAULT_WIP_CAP}` };
43
+ }
44
+ /**
45
+ * In-flight change ids (excluding archive) whose tasks.md has an open task
46
+ * (unchecked, no disposition) or has no tasks.md at all.
47
+ */
48
+ function inFlightChangeIds(projectRoot) {
49
+ const changesDir = path.join(projectRoot, 'openspec', 'changes');
50
+ const ids = [];
51
+ for (const id of getAvailableChanges(projectRoot)) {
52
+ const tasksPath = path.join(changesDir, id, 'tasks.md');
53
+ let hasOpenTasks = true;
54
+ if (fs.existsSync(tasksPath)) {
55
+ hasOpenTasks = countTasksFromContent(fs.readFileSync(tasksPath, 'utf-8')).open > 0;
56
+ }
57
+ if (hasOpenTasks)
58
+ ids.push(id);
59
+ }
60
+ return ids;
61
+ }
62
+ const DEFAULT_SCHEMA = 'spec-driven';
63
+ const KEBAB_ID_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
64
+ function validateChangeName(name) {
65
+ if (!name)
66
+ throw new Error('Change name cannot be empty');
67
+ if (name.length > 200)
68
+ throw new Error('Change name is too long (200 characters max)');
69
+ if (!KEBAB_ID_REGEX.test(name)) {
70
+ if (/[A-Z]/.test(name))
71
+ throw new Error('Change name must be lowercase (use kebab-case)');
72
+ if (/\s/.test(name))
73
+ throw new Error('Change name cannot contain spaces (use hyphens instead)');
74
+ if (/_/.test(name))
75
+ throw new Error('Change name cannot contain underscores (use hyphens instead)');
76
+ if (name.startsWith('-'))
77
+ throw new Error('Change name cannot start with a hyphen');
78
+ if (name.endsWith('-'))
79
+ throw new Error('Change name cannot end with a hyphen');
80
+ if (/--/.test(name))
81
+ throw new Error('Change name cannot contain consecutive hyphens');
82
+ throw new Error('Change name must follow kebab-case convention (e.g., add-auth, refactor-db)');
83
+ }
84
+ }
85
+ function formatLocalDate(date = new Date()) {
86
+ const year = date.getFullYear();
87
+ const month = String(date.getMonth() + 1).padStart(2, '0');
88
+ const day = String(date.getDate()).padStart(2, '0');
89
+ return `${year}-${month}-${day}`;
90
+ }
91
+ export function newChangeHandler(projectRoot, name, options) {
92
+ if (!name)
93
+ throw new Error('Missing required argument <name>');
94
+ validateChangeName(name);
95
+ const schemaName = options.schema ?? DEFAULT_SCHEMA;
96
+ validateSchemaExists(schemaName, projectRoot);
97
+ const openspecDir = path.join(projectRoot, 'openspec');
98
+ const changeDir = path.join(openspecDir, 'changes', name);
99
+ if (fs.existsSync(changeDir)) {
100
+ throw new Error(`Change '${name}' already exists at ${changeDir}`);
101
+ }
102
+ // WIP advisory: read before the new change directory exists, so it is never
103
+ // counted among the in-flight changes it might be advised against joining.
104
+ // A count of zero prints nothing, not even the malformed-config INFO line: the advisory has
105
+ // nothing to say.
106
+ const inFlight = inFlightChangeIds(projectRoot);
107
+ const wipCap = inFlight.length > 0 ? readWipCap(projectRoot) : { cap: DEFAULT_WIP_CAP, info: null };
108
+ fs.mkdirSync(changeDir, { recursive: true });
109
+ fs.mkdirSync(path.join(openspecDir, 'specs'), { recursive: true });
110
+ fs.mkdirSync(path.join(openspecDir, 'changes', 'archive'), { recursive: true });
111
+ const configPath = path.join(openspecDir, 'config.yaml');
112
+ const configYmlPath = path.join(openspecDir, 'config.yml');
113
+ if (!fs.existsSync(configPath) && !fs.existsSync(configYmlPath)) {
114
+ fs.writeFileSync(configPath, `schema: ${DEFAULT_SCHEMA}\n`, 'utf-8');
115
+ }
116
+ writeChangeMetadata(changeDir, { schema: schemaName, created: formatLocalDate() }, projectRoot);
117
+ if (options.description) {
118
+ fs.writeFileSync(path.join(changeDir, 'README.md'), `# ${name}\n\n${options.description}\n`, 'utf-8');
119
+ }
120
+ const wipAdvisory = inFlight.length >= wipCap.cap
121
+ ? { count: inFlight.length, cap: wipCap.cap, changes: inFlight.map(printableId) }
122
+ : null;
123
+ const notices = wipCap.info ? [wipCap.info] : [];
124
+ const payload = {
125
+ change: {
126
+ id: name,
127
+ path: changeDir,
128
+ metadataPath: path.join(changeDir, METADATA_FILENAME),
129
+ schema: schemaName,
130
+ },
131
+ planningHome: planningHomeStub(projectRoot),
132
+ changeRoot: changeDir,
133
+ wip_advisory: wipAdvisory,
134
+ notices,
135
+ };
136
+ if (options.json) {
137
+ printJson(payload);
138
+ return 0;
139
+ }
140
+ console.log(`Created change '${name}' at ${changeDir}/`);
141
+ console.log(`Schema: ${schemaName}`);
142
+ console.log(`Next: openspec status --change ${name}`);
143
+ for (const notice of notices)
144
+ console.log(notice);
145
+ if (wipAdvisory) {
146
+ console.log(`WIP advisory: ${wipAdvisory.count} change(s) in flight (cap ${wipAdvisory.cap}): ${wipAdvisory.changes.join(', ')}; ` +
147
+ 'finish or archive before opening another');
148
+ }
149
+ return 0;
150
+ }
@@ -0,0 +1,4 @@
1
+ export interface SchemasOptions {
2
+ json?: boolean;
3
+ }
4
+ export declare function schemasHandler(projectRoot: string, options: SchemasOptions): number;
@@ -0,0 +1,23 @@
1
+ // `openspec schemas --json` over the vendored engine.
2
+ //
3
+ // Reconstructed from src/commands/workflow/schemas.ts, minus store/root-selection
4
+ // and chalk. JSON output is the SchemaInfo[] array upstream emits verbatim.
5
+ import { listSchemasWithInfo } from '../../vendor/openspec/engine/index.js';
6
+ import { printJson } from './shared.js';
7
+ export function schemasHandler(projectRoot, options) {
8
+ const schemas = listSchemasWithInfo(projectRoot);
9
+ if (options.json) {
10
+ printJson(schemas);
11
+ return 0;
12
+ }
13
+ console.log('Available schemas:');
14
+ console.log();
15
+ for (const schema of schemas) {
16
+ const sourceLabel = schema.source === 'project' ? ' (project)' : schema.source === 'user' ? ' (user override)' : '';
17
+ console.log(` ${schema.name}${sourceLabel}`);
18
+ console.log(` ${schema.description}`);
19
+ console.log(` Artifacts: ${schema.artifacts.join(' -> ')}`);
20
+ console.log();
21
+ }
22
+ return 0;
23
+ }
@@ -0,0 +1,56 @@
1
+ /** Emits a value as pretty JSON on stdout (the single-document `--json` contract). */
2
+ export declare function printJson(payload: unknown): void;
3
+ /**
4
+ * A task's numbering label for a message (the leading `N.N` of its
5
+ * description), falling back to its line number when the description carries
6
+ * none. Shared by every reader that names a task in an issue or a blocker.
7
+ */
8
+ export declare function taskLabel(task: {
9
+ description: string;
10
+ line: number;
11
+ }): string;
12
+ /** The changes directory for a project root. */
13
+ export declare function changesDirFor(projectRoot: string): string;
14
+ /**
15
+ * Store-free stand-in for upstream's `planningHome` (#1703, OpenSpec 1.12).
16
+ * Upstream resolves the planning home against a registered store; this slice
17
+ * has no store, so the planning home is always the local repository root. The
18
+ * /opsx skills compose main-spec paths from `planningHome.root` and archive
19
+ * paths from `planningHome.changesDir`, so both are carried.
20
+ */
21
+ export interface PlanningHomeStub {
22
+ kind: 'local';
23
+ root: string;
24
+ changesDir: string;
25
+ defaultSchema: string;
26
+ }
27
+ /** Builds the planningHome stub for a resolved project root. */
28
+ export declare function planningHomeStub(projectRoot: string): PlanningHomeStub;
29
+ /**
30
+ * Lists active change directory names under openspec/changes/, excluding the
31
+ * archive directory and hidden entries.
32
+ */
33
+ export declare function getAvailableChanges(projectRoot: string): string[];
34
+ /**
35
+ * A change id as a command prints it: a directory name is data, so a control character in it
36
+ * can never break the printed line in two or reach the agent reading the output as a line of
37
+ * its own.
38
+ */
39
+ export declare function printableId(id: string): string;
40
+ /**
41
+ * Resolves a `depends_on` / `supersedes` change id against the project: `"in-flight"`
42
+ * when it names a directory under `openspec/changes/`, `"archived"` when it names one
43
+ * under `openspec/changes/archive/`, else `null`. An id with a path separator, a `..`
44
+ * segment or the reserved `archive` name never resolves, so a list entry cannot escape
45
+ * either tree or alias the archive directory itself. The entry is stat'd without following
46
+ * links, so a symlink planted in the changes tree is not a change and a writer such as
47
+ * `follow-up add` never lands at its target.
48
+ */
49
+ export declare function resolveChangeId(projectRoot: string, id: string): 'in-flight' | 'archived' | null;
50
+ /**
51
+ * Validates that a change exists; throws with the available-changes list when
52
+ * it does not. Mirrors upstream `validateChangeExists`.
53
+ */
54
+ export declare function validateChangeExists(changeName: string | undefined, projectRoot: string): string;
55
+ /** Validates that a schema resolves; throws with the available list otherwise. */
56
+ export declare function validateSchemaExists(schemaName: string, projectRoot?: string): string;
@@ -0,0 +1,140 @@
1
+ // Shared helpers for the store-free `openspec` CLI shim.
2
+ //
3
+ // These reimplement, over the vendored engine, the small validation/discovery
4
+ // helpers the upstream `src/commands/workflow/shared.ts` provided — minus the
5
+ // store/root-selection coupling (root resolution is the shim's job, see
6
+ // src/root-resolver.ts).
7
+ import * as fs from 'node:fs';
8
+ import * as path from 'node:path';
9
+ import { getSchemaDir, listSchemas } from '../../vendor/openspec/engine/index.js';
10
+ /** Emits a value as pretty JSON on stdout (the single-document `--json` contract). */
11
+ export function printJson(payload) {
12
+ console.log(JSON.stringify(payload, null, 2));
13
+ }
14
+ const TASK_ID = /^(\d+(?:\.\d+)+(?:[A-Za-z]+)?)(?=\s|$)/;
15
+ /**
16
+ * A task's numbering label for a message (the leading `N.N` of its
17
+ * description), falling back to its line number when the description carries
18
+ * none. Shared by every reader that names a task in an issue or a blocker.
19
+ */
20
+ export function taskLabel(task) {
21
+ return task.description.match(TASK_ID)?.[1] ?? `line ${task.line}`;
22
+ }
23
+ /** The changes directory for a project root. */
24
+ export function changesDirFor(projectRoot) {
25
+ return path.join(projectRoot, 'openspec', 'changes');
26
+ }
27
+ /** Builds the planningHome stub for a resolved project root. */
28
+ export function planningHomeStub(projectRoot) {
29
+ return {
30
+ kind: 'local',
31
+ root: projectRoot,
32
+ changesDir: changesDirFor(projectRoot),
33
+ defaultSchema: 'spec-driven',
34
+ };
35
+ }
36
+ /**
37
+ * Lists active change directory names under openspec/changes/, excluding the
38
+ * archive directory and hidden entries.
39
+ */
40
+ export function getAvailableChanges(projectRoot) {
41
+ const dir = changesDirFor(projectRoot);
42
+ try {
43
+ return fs
44
+ .readdirSync(dir, { withFileTypes: true })
45
+ .filter((e) => e.isDirectory() && e.name !== 'archive' && !e.name.startsWith('.'))
46
+ .map((e) => e.name);
47
+ }
48
+ catch (error) {
49
+ if (error.code === 'ENOENT')
50
+ return [];
51
+ throw error;
52
+ }
53
+ }
54
+ /**
55
+ * A change id as a command prints it: a directory name is data, so a control character in it
56
+ * can never break the printed line in two or reach the agent reading the output as a line of
57
+ * its own.
58
+ */
59
+ export function printableId(id) {
60
+ return id.replace(/[\x00-\x1f\x7f]/g, '');
61
+ }
62
+ /**
63
+ * Resolves a `depends_on` / `supersedes` change id against the project: `"in-flight"`
64
+ * when it names a directory under `openspec/changes/`, `"archived"` when it names one
65
+ * under `openspec/changes/archive/`, else `null`. An id with a path separator, a `..`
66
+ * segment or the reserved `archive` name never resolves, so a list entry cannot escape
67
+ * either tree or alias the archive directory itself. The entry is stat'd without following
68
+ * links, so a symlink planted in the changes tree is not a change and a writer such as
69
+ * `follow-up add` never lands at its target.
70
+ */
71
+ export function resolveChangeId(projectRoot, id) {
72
+ if (!id || id === '.' || id === '..' || id === 'archive' || id.includes('/') || id.includes('\\') || id.includes('\0')) {
73
+ return null;
74
+ }
75
+ const changesDir = changesDirFor(projectRoot);
76
+ if (isRealDirectory(path.join(changesDir, id)))
77
+ return 'in-flight';
78
+ if (isRealDirectory(path.join(changesDir, 'archive', id)))
79
+ return 'archived';
80
+ return null;
81
+ }
82
+ /** True for a directory that is not a symlink; false for anything else, including an absent path. */
83
+ function isRealDirectory(entry) {
84
+ try {
85
+ return fs.lstatSync(entry).isDirectory();
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ }
91
+ /** Rejects change names that would escape the changes directory. */
92
+ function validateChangeLookupName(changeName) {
93
+ if (changeName === '.' || changeName === '..')
94
+ return 'Change name cannot be a relative path segment';
95
+ if (changeName.includes('/') || changeName.includes('\\'))
96
+ return 'Change name cannot contain path separators';
97
+ if (changeName.includes('\0'))
98
+ return 'Change name cannot contain null characters';
99
+ if (changeName.startsWith('.'))
100
+ return 'Change name cannot start with a dot';
101
+ if (changeName === 'archive')
102
+ return "'archive' is reserved for archived changes";
103
+ return undefined;
104
+ }
105
+ /**
106
+ * Validates that a change exists; throws with the available-changes list when
107
+ * it does not. Mirrors upstream `validateChangeExists`.
108
+ */
109
+ export function validateChangeExists(changeName, projectRoot) {
110
+ if (!changeName) {
111
+ const available = getAvailableChanges(projectRoot);
112
+ if (available.length === 0) {
113
+ throw new Error('No changes found. Create one with: openspec new change <name>');
114
+ }
115
+ throw new Error(`Missing required option --change. Available changes:\n ${available.join('\n ')}`);
116
+ }
117
+ const lookupError = validateChangeLookupName(changeName);
118
+ if (lookupError) {
119
+ throw new Error(`Invalid change name '${changeName}': ${lookupError}`);
120
+ }
121
+ const changePath = path.join(changesDirFor(projectRoot), changeName);
122
+ const exists = fs.existsSync(changePath) && fs.statSync(changePath).isDirectory();
123
+ if (!exists) {
124
+ const available = getAvailableChanges(projectRoot);
125
+ if (available.length === 0) {
126
+ throw new Error(`Change '${changeName}' not found. No changes exist. Create one with: openspec new change <name>`);
127
+ }
128
+ throw new Error(`Change '${changeName}' not found. Available changes:\n ${available.join('\n ')}`);
129
+ }
130
+ return changeName;
131
+ }
132
+ /** Validates that a schema resolves; throws with the available list otherwise. */
133
+ export function validateSchemaExists(schemaName, projectRoot) {
134
+ const schemaDir = getSchemaDir(schemaName, projectRoot);
135
+ if (!schemaDir) {
136
+ const available = listSchemas(projectRoot);
137
+ throw new Error(`Schema '${schemaName}' not found. Available schemas:\n ${available.join('\n ')}`);
138
+ }
139
+ return schemaName;
140
+ }
@@ -0,0 +1,8 @@
1
+ import { type ChangeStatus } from '../../vendor/openspec/engine/index.js';
2
+ export interface StatusOptions {
3
+ change?: string;
4
+ schema?: string;
5
+ json?: boolean;
6
+ }
7
+ export declare function statusHandler(projectRoot: string, options: StatusOptions): Promise<number>;
8
+ export declare function printStatusText(status: ChangeStatus): void;
@@ -0,0 +1,85 @@
1
+ // `openspec status --change <id> [--json]` over the vendored engine.
2
+ //
3
+ // Reconstructed from src/commands/workflow/status.ts, minus store/root-selection
4
+ // and the ora spinner. The JSON shape preserves the fields the /opsx skills read
5
+ // (artifacts[].status, artifacts[].requires, applyRequires, isPlanningComplete /
6
+ // isComplete). The store-only `root` field is omitted; `planningHome` is the
7
+ // store-free stub from shared.ts (V4-P4, #1703).
8
+ import { loadChangeContext, formatChangeStatus, getTaskProgressForChange, } from '../../vendor/openspec/engine/index.js';
9
+ import { changesDirFor, getAvailableChanges, planningHomeStub, printJson, validateChangeExists, validateSchemaExists, } from './shared.js';
10
+ export async function statusHandler(projectRoot, options) {
11
+ if (!options.change) {
12
+ const available = getAvailableChanges(projectRoot);
13
+ if (available.length === 0) {
14
+ if (options.json) {
15
+ printJson({ changes: [], message: 'No active changes.' });
16
+ return 0;
17
+ }
18
+ console.log('No active changes. Create one with: openspec new change <name>');
19
+ return 0;
20
+ }
21
+ throw new Error(`Missing required option --change. Available changes:\n ${available.join('\n ')}`);
22
+ }
23
+ const changeName = validateChangeExists(options.change, projectRoot);
24
+ if (options.schema) {
25
+ validateSchemaExists(options.schema, projectRoot);
26
+ }
27
+ const context = loadChangeContext(projectRoot, changeName, options.schema);
28
+ const status = formatChangeStatus(context);
29
+ // Task-level counts (done/open/dispositioned) are orthogonal to the planning
30
+ // artifacts' status above: a dispositioned task is resolved without being
31
+ // checked, so the tasks artifact is complete for a reader's purposes exactly
32
+ // when `open` reaches zero.
33
+ const taskProgress = await getTaskProgressForChange(changesDirFor(projectRoot), changeName, projectRoot);
34
+ if (options.json) {
35
+ printJson({
36
+ ...status,
37
+ done: taskProgress.done,
38
+ open: taskProgress.open,
39
+ dispositioned: taskProgress.dispositioned,
40
+ planningHome: planningHomeStub(projectRoot),
41
+ });
42
+ return 0;
43
+ }
44
+ printStatusText(status);
45
+ return 0;
46
+ }
47
+ function indicator(status) {
48
+ switch (status) {
49
+ case 'done':
50
+ return '[x]';
51
+ case 'skipped':
52
+ return '[~]';
53
+ case 'ready':
54
+ return '[ ]';
55
+ case 'blocked':
56
+ return '[-]';
57
+ }
58
+ }
59
+ export function printStatusText(status) {
60
+ const doneCount = status.artifacts.filter((a) => a.status === 'done').length;
61
+ const skippedCount = status.artifacts.filter((a) => a.status === 'skipped').length;
62
+ const total = status.artifacts.length - skippedCount;
63
+ console.log(`Change: ${status.changeName}`);
64
+ console.log(`Schema: ${status.schemaName}`);
65
+ if (status.changeRoot) {
66
+ console.log(`Change root: ${status.changeRoot}`);
67
+ }
68
+ const skippedSuffix = skippedCount > 0 ? ` (${skippedCount} skipped)` : '';
69
+ console.log(`Progress: ${doneCount}/${total} artifacts complete${skippedSuffix}`);
70
+ console.log();
71
+ for (const artifact of status.artifacts) {
72
+ let line = `${indicator(artifact.status)} ${artifact.id}`;
73
+ if (artifact.status === 'skipped') {
74
+ line += ' (skipped: change declares skip_specs)';
75
+ }
76
+ if (artifact.status === 'blocked' && artifact.missingDeps && artifact.missingDeps.length > 0) {
77
+ line += ` (blocked by: ${artifact.missingDeps.join(', ')})`;
78
+ }
79
+ console.log(line);
80
+ }
81
+ if (status.isPlanningComplete) {
82
+ console.log();
83
+ console.log('All planning artifacts complete!');
84
+ }
85
+ }
@@ -0,0 +1,6 @@
1
+ export interface ValidateOptions {
2
+ specs?: boolean;
3
+ strict?: boolean;
4
+ json?: boolean;
5
+ }
6
+ export declare function validateHandler(projectRoot: string, item: string | undefined, options: ValidateOptions): Promise<number>;