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,31 @@
1
+ /** Project-relative SDD workspace: `<change>/<task>.brief.md` and `<task>.review.md`. */
2
+ export declare const SDD_DIR: string;
3
+ /** Gitignore line for the transient SDD workspace (briefs, review packages). */
4
+ export declare const SDD_GITIGNORE_LINE = ".freeharness/sdd/";
5
+ /** Gitignore line for the project-scope agent-memory tree (`memory: project` agents). */
6
+ export declare const AGENT_MEMORY_GITIGNORE_LINE = ".claude/agent-memory/";
7
+ /** Gitignore line for the project-scope local agent-memory tree (`memory: local` agents). */
8
+ export declare const AGENT_MEMORY_LOCAL_GITIGNORE_LINE = ".claude/agent-memory-local/";
9
+ /**
10
+ * Ensures `.gitignore` contains `line` (exact line match, comments ignored). Creates the file
11
+ * when absent; appends otherwise; never removes or reorders anything.
12
+ * @param projectRoot Project root.
13
+ * @param line Line to guarantee.
14
+ * @param dryRun Report without writing.
15
+ * @returns Absolute `.gitignore` path and whether the line was added.
16
+ */
17
+ export declare function ensureGitignoreLine(projectRoot: string, line: string, dryRun: boolean): {
18
+ path: string;
19
+ added: boolean;
20
+ };
21
+ /**
22
+ * Creates the SDD workspace directory when absent. Never recorded in the install manifest:
23
+ * briefs and review packages are the project's transient evidence, not ours to uninstall.
24
+ * @param projectRoot Project root.
25
+ * @param dryRun Report without writing.
26
+ * @returns Absolute path and whether the directory was created.
27
+ */
28
+ export declare function ensureSddDir(projectRoot: string, dryRun: boolean): {
29
+ path: string;
30
+ created: boolean;
31
+ };
@@ -0,0 +1,51 @@
1
+ // Project-scope evidence helpers. `init --scope project` creates `.freeharness/` and keeps
2
+ // `.freeharness/sdd/` (the transient subagent-driven-development workspace: briefs and review
3
+ // packages) out of git. The durable evidence is `.freeharness/telemetry.jsonl`, written by the
4
+ // cost-tracker hook at session end; it is never seeded or read here, only documented in the
5
+ // adapter's notes.
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { ensureDir } from "./fsutil.js";
9
+ /** Project-relative SDD workspace: `<change>/<task>.brief.md` and `<task>.review.md`. */
10
+ export const SDD_DIR = path.join(".freeharness", "sdd");
11
+ /** Gitignore line for the transient SDD workspace (briefs, review packages). */
12
+ export const SDD_GITIGNORE_LINE = ".freeharness/sdd/";
13
+ /** Gitignore line for the project-scope agent-memory tree (`memory: project` agents). */
14
+ export const AGENT_MEMORY_GITIGNORE_LINE = ".claude/agent-memory/";
15
+ /** Gitignore line for the project-scope local agent-memory tree (`memory: local` agents). */
16
+ export const AGENT_MEMORY_LOCAL_GITIGNORE_LINE = ".claude/agent-memory-local/";
17
+ /**
18
+ * Ensures `.gitignore` contains `line` (exact line match, comments ignored). Creates the file
19
+ * when absent; appends otherwise; never removes or reorders anything.
20
+ * @param projectRoot Project root.
21
+ * @param line Line to guarantee.
22
+ * @param dryRun Report without writing.
23
+ * @returns Absolute `.gitignore` path and whether the line was added.
24
+ */
25
+ export function ensureGitignoreLine(projectRoot, line, dryRun) {
26
+ const file = path.join(projectRoot, ".gitignore");
27
+ const current = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
28
+ const present = current.split(/\r?\n/).some((l) => l.trim() === line || l.trim() === line.replace(/\/$/, ""));
29
+ if (present)
30
+ return { path: file, added: false };
31
+ if (!dryRun) {
32
+ ensureDir(projectRoot, false);
33
+ const sep = current === "" || current.endsWith("\n") ? "" : "\n";
34
+ fs.writeFileSync(file, `${current}${sep}${line}\n`, "utf8");
35
+ }
36
+ return { path: file, added: true };
37
+ }
38
+ /**
39
+ * Creates the SDD workspace directory when absent. Never recorded in the install manifest:
40
+ * briefs and review packages are the project's transient evidence, not ours to uninstall.
41
+ * @param projectRoot Project root.
42
+ * @param dryRun Report without writing.
43
+ * @returns Absolute path and whether the directory was created.
44
+ */
45
+ export function ensureSddDir(projectRoot, dryRun) {
46
+ const dir = path.join(projectRoot, SDD_DIR);
47
+ if (fs.existsSync(dir))
48
+ return { path: dir, created: false };
49
+ ensureDir(dir, dryRun);
50
+ return { path: dir, created: true };
51
+ }
@@ -0,0 +1,40 @@
1
+ export declare function ensureDir(dir: string, dryRun: boolean): void;
2
+ export declare function writeText(file: string, content: string, dryRun: boolean): string;
3
+ export declare function readText(file: string): string;
4
+ /** Seeds a file without clobbering one a team already maintains; skipped=true even under --dry-run. */
5
+ export declare function writeTextIfAbsent(file: string, content: string, dryRun: boolean): {
6
+ path: string;
7
+ skipped: boolean;
8
+ };
9
+ /** Build artifacts, VCS metadata, Python bytecode: shared by copyDir and any caller walking a source tree with the same exclusion, so the two never drift. */
10
+ export declare function isJunkEntry(name: string): boolean;
11
+ /** Copies every file under src into dst; `transformOnly` gates which files `transform` reads as text (the rest copy raw, bytes and mode untouched; a transformed file's target is chmod'd to the source mode). */
12
+ export declare function copyDir(src: string, dst: string, dryRun: boolean, opts?: {
13
+ skip?: (name: string) => boolean;
14
+ transform?: (content: string, name: string) => string;
15
+ transformOnly?: (name: string) => boolean;
16
+ }): string[];
17
+ export declare function copyExecutable(src: string, dst: string, dryRun: boolean): string;
18
+ export interface HookEntry {
19
+ matcher?: string;
20
+ hooks: {
21
+ type: string;
22
+ command: string;
23
+ async?: boolean;
24
+ timeout?: number;
25
+ }[];
26
+ }
27
+ /** Additive, deduped by command; never removes existing entries, so claude-mem stays intact. */
28
+ export declare function mergeClaudeHook(settingsPath: string, eventName: string, entry: HookEntry, dryRun: boolean): {
29
+ added: boolean;
30
+ };
31
+ /** A key is written only when absent, so a value the user (or another tool) set is never overwritten; `$comment` keys are never copied. */
32
+ export declare function mergeSettingsKeys(settingsPath: string, entries: Record<string, unknown>, dryRun: boolean): {
33
+ added: string[];
34
+ kept: string[];
35
+ };
36
+ /** Marker file the config guard hook honors (`.freeharness/allow-config-edit`). */
37
+ export declare const CONFIG_EDIT_MARKER = "allow-config-edit";
38
+ export declare function configMarkerPath(scope: "user" | "project", projectRoot: string, home: string): string;
39
+ /** Writes the marker with an explicit expiry unless one already exists (never replaced or removed); the closer removes it only when this call created it. */
40
+ export declare function openConfigWindow(file: string, ttlMs?: number): () => void;
@@ -0,0 +1,143 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export function ensureDir(dir, dryRun) {
4
+ if (dryRun)
5
+ return;
6
+ fs.mkdirSync(dir, { recursive: true });
7
+ }
8
+ export function writeText(file, content, dryRun) {
9
+ if (!dryRun) {
10
+ ensureDir(path.dirname(file), false);
11
+ fs.writeFileSync(file, content, "utf8");
12
+ }
13
+ return file;
14
+ }
15
+ export function readText(file) {
16
+ return fs.readFileSync(file, "utf8");
17
+ }
18
+ /** Seeds a file without clobbering one a team already maintains; skipped=true even under --dry-run. */
19
+ export function writeTextIfAbsent(file, content, dryRun) {
20
+ if (fs.existsSync(file))
21
+ return { path: file, skipped: true };
22
+ if (!dryRun) {
23
+ ensureDir(path.dirname(file), false);
24
+ fs.writeFileSync(file, content, "utf8");
25
+ }
26
+ return { path: file, skipped: false };
27
+ }
28
+ const JUNK = new Set(["node_modules", "__pycache__", ".git", "dist", ".DS_Store"]);
29
+ /** Build artifacts, VCS metadata, Python bytecode: shared by copyDir and any caller walking a source tree with the same exclusion, so the two never drift. */
30
+ export function isJunkEntry(name) {
31
+ return JUNK.has(name) || name.endsWith(".pyc");
32
+ }
33
+ /** Copies every file under src into dst; `transformOnly` gates which files `transform` reads as text (the rest copy raw, bytes and mode untouched; a transformed file's target is chmod'd to the source mode). */
34
+ export function copyDir(src, dst, dryRun, opts = {}) {
35
+ const written = [];
36
+ const walk = (rel) => {
37
+ const abs = path.join(src, rel);
38
+ for (const entry of fs.readdirSync(abs, { withFileTypes: true })) {
39
+ if (isJunkEntry(entry.name))
40
+ continue;
41
+ const childRel = path.join(rel, entry.name);
42
+ if (entry.isDirectory()) {
43
+ walk(childRel);
44
+ }
45
+ else {
46
+ if (opts.skip?.(entry.name))
47
+ continue;
48
+ const target = path.join(dst, childRel);
49
+ const srcFile = path.join(src, childRel);
50
+ const transform = opts.transform;
51
+ if (!dryRun) {
52
+ ensureDir(path.dirname(target), false);
53
+ if (transform !== undefined && (opts.transformOnly === undefined || opts.transformOnly(entry.name))) {
54
+ writeText(target, transform(readText(srcFile), entry.name), false);
55
+ fs.chmodSync(target, fs.statSync(srcFile).mode);
56
+ }
57
+ else {
58
+ fs.copyFileSync(srcFile, target);
59
+ }
60
+ }
61
+ written.push(target);
62
+ }
63
+ }
64
+ };
65
+ walk("");
66
+ return written;
67
+ }
68
+ export function copyExecutable(src, dst, dryRun) {
69
+ if (!dryRun) {
70
+ ensureDir(path.dirname(dst), false);
71
+ fs.copyFileSync(src, dst);
72
+ fs.chmodSync(dst, 0o755);
73
+ }
74
+ return dst;
75
+ }
76
+ /** Additive, deduped by command; never removes existing entries, so claude-mem stays intact. */
77
+ export function mergeClaudeHook(settingsPath, eventName, entry, dryRun) {
78
+ let settings = {};
79
+ if (fs.existsSync(settingsPath)) {
80
+ settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
81
+ }
82
+ settings.hooks ??= {};
83
+ settings.hooks[eventName] ??= [];
84
+ const arr = settings.hooks[eventName];
85
+ const ourCmd = entry.hooks[0].command;
86
+ const exists = arr.some((e) => e.hooks.some((h) => h.command === ourCmd));
87
+ if (!exists)
88
+ arr.push(entry);
89
+ if (!dryRun && !exists) {
90
+ ensureDir(path.dirname(settingsPath), false);
91
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf8");
92
+ }
93
+ return { added: !exists };
94
+ }
95
+ /** A key is written only when absent, so a value the user (or another tool) set is never overwritten; `$comment` keys are never copied. */
96
+ export function mergeSettingsKeys(settingsPath, entries, dryRun) {
97
+ let settings = {};
98
+ if (fs.existsSync(settingsPath)) {
99
+ settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
100
+ }
101
+ const added = [];
102
+ const kept = [];
103
+ for (const [k, v] of Object.entries(entries)) {
104
+ if (k.startsWith("$"))
105
+ continue;
106
+ if (Object.prototype.hasOwnProperty.call(settings, k))
107
+ kept.push(k);
108
+ else {
109
+ settings[k] = v;
110
+ added.push(k);
111
+ }
112
+ }
113
+ if (!dryRun && added.length > 0) {
114
+ ensureDir(path.dirname(settingsPath), false);
115
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf8");
116
+ }
117
+ return { added, kept };
118
+ }
119
+ /** Marker file the config guard hook honors (`.freeharness/allow-config-edit`). */
120
+ export const CONFIG_EDIT_MARKER = "allow-config-edit";
121
+ export function configMarkerPath(scope, projectRoot, home) {
122
+ return scope === "user" ? path.join(home, ".claude", "freeharness", CONFIG_EDIT_MARKER) : path.join(projectRoot, ".freeharness", CONFIG_EDIT_MARKER);
123
+ }
124
+ /** Writes the marker with an explicit expiry unless one already exists (never replaced or removed); the closer removes it only when this call created it. */
125
+ export function openConfigWindow(file, ttlMs = 10 * 60_000) {
126
+ if (fs.existsSync(file))
127
+ return () => undefined;
128
+ try {
129
+ ensureDir(path.dirname(file), false);
130
+ fs.writeFileSync(file, String(Date.now() + ttlMs), "utf8");
131
+ }
132
+ catch {
133
+ return () => undefined;
134
+ }
135
+ return () => {
136
+ try {
137
+ fs.rmSync(file, { force: true });
138
+ }
139
+ catch {
140
+ // best effort
141
+ }
142
+ };
143
+ }
@@ -0,0 +1,76 @@
1
+ import type { AdapterResult, Scope } from "@freeharness/core";
2
+ /** One emitted file (or directory) and the step that produced it. */
3
+ export interface ManifestFile {
4
+ path: string;
5
+ capability: string;
6
+ target: string;
7
+ }
8
+ /** The persisted manifest shape. */
9
+ export interface InstallManifest {
10
+ /** CLI version that last applied. */
11
+ version: string;
12
+ /** Short HEAD of the repo checkout that served the canonical source, when it was one. */
13
+ sourceCommit?: string;
14
+ scope: Scope;
15
+ targets: string[];
16
+ capabilities: string[];
17
+ /** First install (ISO). */
18
+ installedAt: string;
19
+ /** Last apply pass (ISO). */
20
+ updatedAt: string;
21
+ files: ManifestFile[];
22
+ /** Descriptions of the side-effecting actions that were run. */
23
+ actions: string[];
24
+ /** Opt-in profiles applied to this scope (v4 P6), by profile id. */
25
+ profiles?: Record<string, ProfileRecord>;
26
+ }
27
+ /** What one profile wrote, so `init --profile none` removes exactly that and nothing else. */
28
+ export interface ProfileRecord {
29
+ target: string;
30
+ /** File that received the merge (settings.json or opencode.json). */
31
+ file: string;
32
+ /** Keys written by the profile (env keys, or `provider.<id>` for OpenCode). */
33
+ keys: string[];
34
+ /** Extra files written (notes), removed with the profile. */
35
+ files: string[];
36
+ appliedAt: string;
37
+ }
38
+ /**
39
+ * Resolves the manifest path for a scope.
40
+ * @param scope user | project.
41
+ * @param projectRoot Project root (project scope).
42
+ * @returns Absolute manifest path.
43
+ */
44
+ export declare function manifestPath(scope: Scope, projectRoot: string): string;
45
+ /**
46
+ * Reads a manifest when present.
47
+ * @param file Manifest path.
48
+ * @returns The parsed manifest, or undefined when absent/unreadable.
49
+ */
50
+ export declare function readManifest(file: string): InstallManifest | undefined;
51
+ /**
52
+ * Short HEAD of a repo checkout (git must be on PATH).
53
+ * @param repoRoot Checkout root.
54
+ * @returns The 8-char commit, or undefined.
55
+ */
56
+ export declare function repoHead(repoRoot: string): string | undefined;
57
+ /** Inputs for one apply pass, merged into the manifest. */
58
+ export interface ManifestUpdate {
59
+ version: string;
60
+ sourceCommit?: string;
61
+ scope: Scope;
62
+ projectRoot: string;
63
+ targets: string[];
64
+ capabilities: string[];
65
+ results: AdapterResult[];
66
+ actions: string[];
67
+ /** Profile records to set (value) or drop (null); absent = untouched. */
68
+ profiles?: Record<string, ProfileRecord | null>;
69
+ }
70
+ /**
71
+ * Merges an apply pass into the manifest on disk (creating it) and registers the
72
+ * manifest file itself last. Files the adapters reported as skipped are never recorded.
73
+ * @param u The pass to record.
74
+ * @returns The manifest path.
75
+ */
76
+ export declare function writeManifest(u: ManifestUpdate): string;
@@ -0,0 +1,92 @@
1
+ // Install manifest: the record of what FreeHarness emitted into a scope, so `uninstall`
2
+ // removes only that, `update --check` can compare versions, and `doctor` can report drift.
3
+ // Lives at <claudeDir>/freeharness/install-manifest.json (user: ~/.claude; project:
4
+ // <project>/.claude). Merged on every apply pass: entries are added, never dropped.
5
+ import fs from "node:fs";
6
+ import os from "node:os";
7
+ import path from "node:path";
8
+ import { spawnSync } from "node:child_process";
9
+ /**
10
+ * Resolves the manifest path for a scope.
11
+ * @param scope user | project.
12
+ * @param projectRoot Project root (project scope).
13
+ * @returns Absolute manifest path.
14
+ */
15
+ export function manifestPath(scope, projectRoot) {
16
+ const claudeDir = scope === "user" ? path.join(os.homedir(), ".claude") : path.join(projectRoot, ".claude");
17
+ return path.join(claudeDir, "freeharness", "install-manifest.json");
18
+ }
19
+ /**
20
+ * Reads a manifest when present.
21
+ * @param file Manifest path.
22
+ * @returns The parsed manifest, or undefined when absent/unreadable.
23
+ */
24
+ export function readManifest(file) {
25
+ try {
26
+ const m = JSON.parse(fs.readFileSync(file, "utf8"));
27
+ return Array.isArray(m.files) ? m : undefined;
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ /**
34
+ * Short HEAD of a repo checkout (git must be on PATH).
35
+ * @param repoRoot Checkout root.
36
+ * @returns The 8-char commit, or undefined.
37
+ */
38
+ export function repoHead(repoRoot) {
39
+ const r = spawnSync("git", ["-C", repoRoot, "rev-parse", "--short=8", "HEAD"], { encoding: "utf8" });
40
+ const c = r.status === 0 ? r.stdout.trim() : "";
41
+ return /^[0-9a-f]{7,40}$/.test(c) ? c : undefined;
42
+ }
43
+ /**
44
+ * Merges an apply pass into the manifest on disk (creating it) and registers the
45
+ * manifest file itself last. Files the adapters reported as skipped are never recorded.
46
+ * @param u The pass to record.
47
+ * @returns The manifest path.
48
+ */
49
+ export function writeManifest(u) {
50
+ const file = manifestPath(u.scope, u.projectRoot);
51
+ const now = new Date().toISOString();
52
+ const prev = readManifest(file);
53
+ const byPath = new Map();
54
+ for (const f of prev?.files ?? [])
55
+ byPath.set(f.path, f);
56
+ for (const r of u.results) {
57
+ const skipped = new Set(r.skipped ?? []);
58
+ for (const p of r.written) {
59
+ if (skipped.has(p) || p === file)
60
+ continue;
61
+ byPath.set(p, { path: p, capability: r.capabilityId, target: r.target });
62
+ }
63
+ }
64
+ byPath.set(file, { path: file, capability: "manifest", target: "freeharness" });
65
+ const union = (a, b) => [...new Set([...(a ?? []), ...b])];
66
+ const profiles = { ...(prev?.profiles ?? {}) };
67
+ for (const [id, rec] of Object.entries(u.profiles ?? {})) {
68
+ if (rec === null) {
69
+ // A removed profile takes its files (notes) out of the manifest with it.
70
+ for (const f of profiles[id]?.files ?? [])
71
+ byPath.delete(f);
72
+ delete profiles[id];
73
+ }
74
+ else
75
+ profiles[id] = rec;
76
+ }
77
+ const manifest = {
78
+ version: u.version,
79
+ sourceCommit: u.sourceCommit ?? prev?.sourceCommit,
80
+ scope: u.scope,
81
+ targets: union(prev?.targets, u.targets),
82
+ capabilities: union(prev?.capabilities, u.capabilities),
83
+ installedAt: prev?.installedAt ?? now,
84
+ updatedAt: now,
85
+ files: [...byPath.values()],
86
+ actions: union(prev?.actions, u.actions),
87
+ ...(Object.keys(profiles).length > 0 ? { profiles } : {}),
88
+ };
89
+ fs.mkdirSync(path.dirname(file), { recursive: true });
90
+ fs.writeFileSync(file, JSON.stringify(manifest, null, 2) + "\n", "utf8");
91
+ return file;
92
+ }
@@ -0,0 +1,19 @@
1
+ export declare function resolveCanonicalDir(): string;
2
+ /**
3
+ * Reads the running CLI version from the package's own package.json (dist/ or src/ layout).
4
+ * @returns The semver string, or "0.0.0" when the manifest cannot be read.
5
+ */
6
+ export declare function cliVersion(): string;
7
+ /**
8
+ * Locates the FreeHarness repo checkout the canonical dir belongs to, when it is one
9
+ * (bundled `packages/cli/canonical` or the repo-root `canonical/`); npx installs have none.
10
+ * @param canonicalDir Resolved canonical directory.
11
+ * @returns The repo root, or undefined when canonical is not inside a checkout.
12
+ */
13
+ export declare function sourceRepoRoot(canonicalDir: string): string | undefined;
14
+ /**
15
+ * Locates the vendored `openspec` bin of the @freeharness/sdd package this CLI resolves
16
+ * (workspace link in a checkout, bundled copy in an npm install), for exposing it on PATH.
17
+ * @returns Absolute path of dist/src/bin/openspec.js, or undefined when not resolvable.
18
+ */
19
+ export declare function vendoredSddBin(): string | undefined;
@@ -0,0 +1,74 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ import fs from "node:fs";
5
+ // Resolve canonical/: $FREEHARNESS_CANONICAL, else walk up to a canonical/ with rules/CLAUDE.md.
6
+ export function resolveCanonicalDir() {
7
+ const env = process.env.FREEHARNESS_CANONICAL;
8
+ if (env && fs.existsSync(env))
9
+ return env;
10
+ let dir = path.dirname(fileURLToPath(import.meta.url));
11
+ for (let i = 0; i < 8; i++) {
12
+ const candidate = path.join(dir, "canonical");
13
+ if (fs.existsSync(path.join(candidate, "rules", "CLAUDE.md"))) {
14
+ return candidate;
15
+ }
16
+ const parent = path.dirname(dir);
17
+ if (parent === dir)
18
+ break;
19
+ dir = parent;
20
+ }
21
+ throw new Error("canonical/ not found (looked for rules/CLAUDE.md). Set FREEHARNESS_CANONICAL to the framework's canonical directory.");
22
+ }
23
+ /**
24
+ * Reads the running CLI version from the package's own package.json (dist/ or src/ layout).
25
+ * @returns The semver string, or "0.0.0" when the manifest cannot be read.
26
+ */
27
+ export function cliVersion() {
28
+ const here = path.dirname(fileURLToPath(import.meta.url));
29
+ for (const candidate of [path.join(here, "..", "..", "package.json"), path.join(here, "..", "package.json")]) {
30
+ try {
31
+ const v = JSON.parse(fs.readFileSync(candidate, "utf8"));
32
+ if (v.name === "freeharness" && typeof v.version === "string")
33
+ return v.version;
34
+ }
35
+ catch {
36
+ /* try the next layout */
37
+ }
38
+ }
39
+ return "0.0.0";
40
+ }
41
+ /**
42
+ * Locates the FreeHarness repo checkout the canonical dir belongs to, when it is one
43
+ * (bundled `packages/cli/canonical` or the repo-root `canonical/`); npx installs have none.
44
+ * @param canonicalDir Resolved canonical directory.
45
+ * @returns The repo root, or undefined when canonical is not inside a checkout.
46
+ */
47
+ export function sourceRepoRoot(canonicalDir) {
48
+ for (const up of [path.resolve(canonicalDir, ".."), path.resolve(canonicalDir, "..", "..", "..")]) {
49
+ try {
50
+ const pkg = JSON.parse(fs.readFileSync(path.join(up, "package.json"), "utf8"));
51
+ if (pkg.name === "freeharness-monorepo" && fs.existsSync(path.join(up, ".git")))
52
+ return up;
53
+ }
54
+ catch {
55
+ /* not a checkout root */
56
+ }
57
+ }
58
+ return undefined;
59
+ }
60
+ /**
61
+ * Locates the vendored `openspec` bin of the @freeharness/sdd package this CLI resolves
62
+ * (workspace link in a checkout, bundled copy in an npm install), for exposing it on PATH.
63
+ * @returns Absolute path of dist/src/bin/openspec.js, or undefined when not resolvable.
64
+ */
65
+ export function vendoredSddBin() {
66
+ try {
67
+ const main = createRequire(import.meta.url).resolve("@freeharness/sdd");
68
+ const bin = path.join(path.dirname(main), "bin", "openspec.js");
69
+ return fs.existsSync(bin) ? bin : undefined;
70
+ }
71
+ catch {
72
+ return undefined;
73
+ }
74
+ }
@@ -0,0 +1,4 @@
1
+ /** Routing file name. */
2
+ export declare const ROUTING_FILE = "freeharness-routing.md";
3
+ /** Canonical routing source path. */
4
+ export declare function routingSourcePath(canonicalDir: string): string;
@@ -0,0 +1,7 @@
1
+ import path from "node:path";
2
+ /** Routing file name. */
3
+ export const ROUTING_FILE = "freeharness-routing.md";
4
+ /** Canonical routing source path. */
5
+ export function routingSourcePath(canonicalDir) {
6
+ return path.join(canonicalDir, "rules", "routing.md");
7
+ }
@@ -0,0 +1,30 @@
1
+ import type { HarnessTarget } from "./registry.js";
2
+ export type Scope = "project" | "user";
3
+ export interface InstallContext {
4
+ scope: Scope;
5
+ projectRoot: string;
6
+ canonicalDir: string;
7
+ dryRun: boolean;
8
+ /**
9
+ * Lets a harness adapter copy machine-global capabilities (skills, agents, safety,
10
+ * cost, memory, MCP) into a project. Off by default: at project scope those live in the
11
+ * user install and a copy would be loaded twice by the harness. Adapters that are
12
+ * project-only by nature (OpenCode) ignore it.
13
+ */
14
+ allowProjectCopies?: boolean;
15
+ }
16
+ export interface ExecStep {
17
+ description: string;
18
+ command: string;
19
+ cwd?: string;
20
+ optional?: boolean;
21
+ }
22
+ export interface AdapterResult {
23
+ capabilityId: string;
24
+ target: HarnessTarget;
25
+ written: string[];
26
+ /** Subset of `written` left untouched (absent-guarded seeds that already existed); never recorded as ours. */
27
+ skipped?: string[];
28
+ actions: ExecStep[];
29
+ notes: string[];
30
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export * from "./registry.js";
2
+ export * from "./tiers.js";
3
+ export * from "./context.js";
4
+ export * from "./ports.js";
5
+ export * from "./planner.js";
@@ -0,0 +1,6 @@
1
+ // Core domain barrel. Pure: no I/O, no adapter/cli imports.
2
+ export * from "./registry.js";
3
+ export * from "./tiers.js";
4
+ export * from "./context.js";
5
+ export * from "./ports.js";
6
+ export * from "./planner.js";
@@ -0,0 +1,13 @@
1
+ import type { HarnessTarget } from "./registry.js";
2
+ import type { Scope } from "./context.js";
3
+ export interface PlanStep {
4
+ target: HarnessTarget;
5
+ capabilityId: string;
6
+ }
7
+ export interface InstallPlan {
8
+ scope: Scope;
9
+ targets: HarnessTarget[];
10
+ capabilityIds: string[];
11
+ steps: PlanStep[];
12
+ }
13
+ export declare function buildPlan(requested: string[], targets: HarnessTarget[], scope: Scope): InstallPlan;
@@ -0,0 +1,18 @@
1
+ import { getCapability, resolveInstallOrder } from "./registry.js";
2
+ // Ordering matches the historical runner: targets outer, capabilities (in
3
+ // dependency order) inner, filtered to those supporting the target. Deterministic.
4
+ export function buildPlan(requested, targets, scope) {
5
+ const capabilityIds = resolveInstallOrder(requested, targets);
6
+ const steps = [];
7
+ for (const target of targets) {
8
+ for (const id of capabilityIds) {
9
+ const cap = getCapability(id);
10
+ if (!cap)
11
+ continue;
12
+ if (!cap.targets.includes(target))
13
+ continue;
14
+ steps.push({ target, capabilityId: id });
15
+ }
16
+ }
17
+ return { scope, targets, capabilityIds, steps };
18
+ }