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
+ import type { AdapterResult, ExecStep, InstallContext, Scope } from "@freeharness/core";
2
+ import type { HarnessTarget } from "@freeharness/core";
3
+ import type { MemoryPort } from "@freeharness/core";
4
+ export declare const MARKETPLACE_OWNER = "freeharness";
5
+ export declare const PLUGIN_NAME = "free-mem";
6
+ export declare const PLUGIN_KEY = "free-mem@freeharness";
7
+ /** The upstream marketplace owner (thedotmack/claude-mem), detected for the disable-only migration. */
8
+ export declare const UPSTREAM_MARKETPLACE_OWNER = "thedotmack";
9
+ /** The upstream plugin name; never used for our own registration, only upstream detection. */
10
+ export declare const UPSTREAM_PLUGIN_NAME = "claude-mem";
11
+ export declare const UPSTREAM_PLUGIN_KEY = "claude-mem@thedotmack";
12
+ /**
13
+ * Locate the vendored claude-mem engine next to the canonical directory.
14
+ *
15
+ * Args:
16
+ * canonicalDir: The resolved FreeHarness canonical directory (packages/cli/canonical).
17
+ *
18
+ * Returns:
19
+ * Absolute engine path, or null when the vendored tree is absent (npx bridge).
20
+ */
21
+ export declare function vendoredMemoryEngineDir(canonicalDir: string): string | null;
22
+ /**
23
+ * Build step for harness adapters that only stage the build (OpenCode bridge path).
24
+ *
25
+ * Args:
26
+ * engineDir: Absolute path of the vendored engine.
27
+ *
28
+ * Returns:
29
+ * Gated ExecSteps that build the plugin bundles with Bun.
30
+ */
31
+ export declare function memoryBuildActions(engineDir: string): ExecStep[];
32
+ /** Claude Code config/plugin paths (upstream utils/paths.ts), honoring CLAUDE_CONFIG_DIR. */
33
+ export interface ClaudePluginPaths {
34
+ configDir: string;
35
+ pluginsDir: string;
36
+ knownMarketplaces: string;
37
+ installedPlugins: string;
38
+ settings: string;
39
+ marketplaceDir: string;
40
+ cacheDir: string;
41
+ }
42
+ /**
43
+ * Resolve the Claude Code plugin paths for a plugin version.
44
+ *
45
+ * Args:
46
+ * version: The plugin version (cache directory leaf).
47
+ *
48
+ * Returns:
49
+ * The path set mirrored from upstream utils/paths.ts.
50
+ */
51
+ export declare function claudePluginPaths(version: string): ClaudePluginPaths;
52
+ /**
53
+ * Content digest of a plugin bundle: every file's relative posix path and bytes, in
54
+ * path order, so a same-version rebuild whose output is byte-identical digests the
55
+ * same as the previous run. Skips `node_modules` (matches the copy filter) and the
56
+ * marker file itself, so re-staging never invalidates its own guard.
57
+ *
58
+ * Args:
59
+ * dir: Directory to digest recursively (absent dir digests as empty).
60
+ *
61
+ * Returns:
62
+ * Lowercase hex SHA-256 over `path + NUL + content + NUL` per file.
63
+ */
64
+ export declare function bundleDigest(dir: string): string;
65
+ /** Probe result for one runtime tool. */
66
+ export interface ToolProbe {
67
+ path: string | null;
68
+ version: string | null;
69
+ }
70
+ /**
71
+ * Probe Bun (upstream setup-runtime getBunPath: PATH first, then common install dirs).
72
+ *
73
+ * Returns:
74
+ * Path + version, or nulls when absent.
75
+ */
76
+ export declare function probeBun(): ToolProbe;
77
+ /**
78
+ * Probe uv (Chroma vector store runtime; upstream setup-runtime getUvPath).
79
+ *
80
+ * Returns:
81
+ * Path + version, or nulls when absent.
82
+ */
83
+ export declare function probeUv(): ToolProbe;
84
+ /**
85
+ * Whether an upstream registration (UPSTREAM_PLUGIN_KEY) is still live: present in
86
+ * installed_plugins.json, or enabledPlugins carries it at a value other than `false`. Once a
87
+ * migration sets it to `false`, later `registerHooks` runs see it as already migrated and skip
88
+ * the backup/disable branch (idempotent on re-run).
89
+ *
90
+ * Args:
91
+ * installed: Parsed installed_plugins.json (or `{}` when absent).
92
+ * settings: Parsed settings.json (or `{}` when absent).
93
+ *
94
+ * Returns:
95
+ * True when the upstream key still needs disabling.
96
+ */
97
+ export declare function isUpstreamRegistered(installed: {
98
+ plugins?: Record<string, unknown>;
99
+ }, settings: {
100
+ enabledPlugins?: Record<string, boolean>;
101
+ }): boolean;
102
+ /** MemoryPort implementation backed by the vendored claude-mem plugin build. */
103
+ export declare class VendoredMemory implements MemoryPort {
104
+ readonly engineDir: string;
105
+ readonly vendorDir: string;
106
+ readonly pluginDir: string;
107
+ private readonly target;
108
+ /**
109
+ * Args:
110
+ * engineDir: Absolute path of packages/memory/vendor/claude-mem/engine.
111
+ * target: The harness this install is for (reported in results).
112
+ */
113
+ constructor(engineDir: string, target?: HarnessTarget);
114
+ /** Plugin version from the vendored manifest (upstream readPluginVersion). */
115
+ version(): string;
116
+ private result;
117
+ /**
118
+ * Probe Bun and uv; instruct when missing. Never installs them (user's machine).
119
+ *
120
+ * Returns:
121
+ * Notes only. A missing Bun is reported as "MISSING" and blocks install().
122
+ */
123
+ ensurePrerequisites(_ctx: InstallContext): Promise<AdapterResult>;
124
+ /**
125
+ * Build the plugin bundles and stage the plugin in the Claude Code plugin cache
126
+ * and marketplace directories (upstream copyPluginToCache / copyPluginToMarketplace
127
+ * / installPluginDependencies / writeInstallMarker).
128
+ */
129
+ install(_scope: Scope, ctx: InstallContext): Promise<AdapterResult>;
130
+ /**
131
+ * Register the plugin with Claude Code (upstream registerMarketplace / registerPlugin /
132
+ * enablePluginInClaudeSettings). All three writes are additive merges: other
133
+ * marketplaces, plugins, hooks and settings keys are preserved byte-for-byte in shape.
134
+ *
135
+ * When an upstream registration (UPSTREAM_PLUGIN_KEY) is found (installed_plugins.json
136
+ * or a still-true enabledPlugins entry), this also backs up the three files below with a
137
+ * shared timestamp before the first write and disables (never deletes) the old key; the
138
+ * old cache directory is left in place for `freeharness cleanup` to offer later.
139
+ */
140
+ registerHooks(_scope: Scope, ctx: InstallContext): Promise<AdapterResult>;
141
+ /**
142
+ * Keep an existing ~/.claude-mem data directory from a delegated install. The
143
+ * plugin resolves it by convention (CLAUDE_MEM_DATA_DIR or ~/.claude-mem), so no
144
+ * write is needed and nothing is deleted.
145
+ */
146
+ migrateExisting(_ctx: InstallContext): Promise<AdapterResult>;
147
+ private run;
148
+ private missingPluginDeps;
149
+ private verifyPluginDeps;
150
+ }
@@ -0,0 +1,487 @@
1
+ // MemoryPort adapter for the vendored claude-mem engine (FreeHarness V3-P0 memory-runtime).
2
+ //
3
+ // Installs free-mem as a real Claude Code plugin from OUR build, reproducing the
4
+ // upstream `npx claude-mem install` mechanics (src/npx-cli/commands/install.ts +
5
+ // utils/paths.ts + install/setup-runtime.ts) in TypeScript:
6
+ //
7
+ // ensurePrerequisites Bun (build + hook runtime) and uv (Chroma) probes.
8
+ // install bun run build:plugin -> copyPluginToCache(version)
9
+ // -> copyPluginToMarketplace -> plugin deps install
10
+ // -> .install-version markers.
11
+ // registerHooks known_marketplaces.json + installed_plugins.json +
12
+ // settings.enabledPlugins["free-mem@freeharness"]=true,
13
+ // all ADDITIVE merges (other entries survive). When an
14
+ // upstream registration (UPSTREAM_PLUGIN_KEY) is found, it
15
+ // also backs up the three registration files, registers the
16
+ // new key and disables (never deletes) the old one.
17
+ // migrateExisting an existing ~/.claude-mem data dir is kept; the plugin
18
+ // already resolves it by convention (nothing is deleted).
19
+ //
20
+ // The FreeHarness build declares its own identity (marketplace freeharness, plugin
21
+ // free-mem) so `doctor` and `cleanup` can tell it apart from an upstream or
22
+ // pre-rename install; the data directory stays ~/.claude-mem by convention on
23
+ // both sides. Every write honors ctx.dryRun (writes nothing, reports the plan).
24
+ import crypto from "node:crypto";
25
+ import fs from "node:fs";
26
+ import os from "node:os";
27
+ import path from "node:path";
28
+ import { spawnSync } from "node:child_process";
29
+ const IS_WINDOWS = process.platform === "win32";
30
+ /** Name of the digest marker written into the plugin cache directory after a copy. */
31
+ const BUNDLE_MARKER_NAME = "bundle.sha256";
32
+ export const MARKETPLACE_OWNER = "freeharness";
33
+ export const PLUGIN_NAME = "free-mem";
34
+ export const PLUGIN_KEY = `${PLUGIN_NAME}@${MARKETPLACE_OWNER}`;
35
+ /** The upstream marketplace owner (thedotmack/claude-mem), detected for the disable-only migration. */
36
+ export const UPSTREAM_MARKETPLACE_OWNER = "thedotmack";
37
+ /** The upstream plugin name; never used for our own registration, only upstream detection. */
38
+ export const UPSTREAM_PLUGIN_NAME = "claude-mem";
39
+ export const UPSTREAM_PLUGIN_KEY = `${UPSTREAM_PLUGIN_NAME}@${UPSTREAM_MARKETPLACE_OWNER}`;
40
+ const CAPABILITY_ID = "memory";
41
+ /**
42
+ * Locate the vendored claude-mem engine next to the canonical directory.
43
+ *
44
+ * Args:
45
+ * canonicalDir: The resolved FreeHarness canonical directory (packages/cli/canonical).
46
+ *
47
+ * Returns:
48
+ * Absolute engine path, or null when the vendored tree is absent (npx bridge).
49
+ */
50
+ export function vendoredMemoryEngineDir(canonicalDir) {
51
+ const engineDir = path.join(canonicalDir, "..", "..", "memory", "vendor", UPSTREAM_PLUGIN_NAME, "engine");
52
+ return fs.existsSync(path.join(engineDir, "package.json")) ? path.resolve(engineDir) : null;
53
+ }
54
+ /**
55
+ * Build step for harness adapters that only stage the build (OpenCode bridge path).
56
+ *
57
+ * Args:
58
+ * engineDir: Absolute path of the vendored engine.
59
+ *
60
+ * Returns:
61
+ * Gated ExecSteps that build the plugin bundles with Bun.
62
+ */
63
+ export function memoryBuildActions(engineDir) {
64
+ return [
65
+ {
66
+ description: "Build the vendored claude-mem plugin bundles (worker, mcp, context, watcher) with Bun",
67
+ command: "bun install && bun run build:plugin",
68
+ cwd: engineDir,
69
+ optional: true,
70
+ },
71
+ ];
72
+ }
73
+ /**
74
+ * Resolve the Claude Code plugin paths for a plugin version.
75
+ *
76
+ * Args:
77
+ * version: The plugin version (cache directory leaf).
78
+ *
79
+ * Returns:
80
+ * The path set mirrored from upstream utils/paths.ts.
81
+ */
82
+ export function claudePluginPaths(version) {
83
+ const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
84
+ const pluginsDir = path.join(configDir, "plugins");
85
+ return {
86
+ configDir,
87
+ pluginsDir,
88
+ knownMarketplaces: path.join(pluginsDir, "known_marketplaces.json"),
89
+ installedPlugins: path.join(pluginsDir, "installed_plugins.json"),
90
+ settings: path.join(configDir, "settings.json"),
91
+ marketplaceDir: path.join(pluginsDir, "marketplaces", MARKETPLACE_OWNER),
92
+ cacheDir: path.join(pluginsDir, "cache", MARKETPLACE_OWNER, PLUGIN_NAME, version),
93
+ };
94
+ }
95
+ /**
96
+ * Content digest of a plugin bundle: every file's relative posix path and bytes, in
97
+ * path order, so a same-version rebuild whose output is byte-identical digests the
98
+ * same as the previous run. Skips `node_modules` (matches the copy filter) and the
99
+ * marker file itself, so re-staging never invalidates its own guard.
100
+ *
101
+ * Args:
102
+ * dir: Directory to digest recursively (absent dir digests as empty).
103
+ *
104
+ * Returns:
105
+ * Lowercase hex SHA-256 over `path + NUL + content + NUL` per file.
106
+ */
107
+ export function bundleDigest(dir) {
108
+ const files = [];
109
+ const walk = (rel) => {
110
+ for (const entry of fs.readdirSync(path.join(dir, rel), { withFileTypes: true })) {
111
+ if (entry.name === "node_modules" || entry.name === BUNDLE_MARKER_NAME)
112
+ continue;
113
+ const childRel = rel ? `${rel}/${entry.name}` : entry.name;
114
+ if (entry.isDirectory())
115
+ walk(childRel);
116
+ else if (entry.isFile())
117
+ files.push(childRel);
118
+ }
119
+ };
120
+ if (fs.existsSync(dir))
121
+ walk("");
122
+ files.sort();
123
+ const hash = crypto.createHash("sha256");
124
+ for (const rel of files) {
125
+ hash.update(rel, "utf-8");
126
+ hash.update("\0");
127
+ hash.update(fs.readFileSync(path.join(dir, rel)));
128
+ hash.update("\0");
129
+ }
130
+ return hash.digest("hex");
131
+ }
132
+ function probeTool(command, commonPaths) {
133
+ const candidates = [command, ...commonPaths];
134
+ for (const candidate of candidates) {
135
+ if (candidate !== command && !fs.existsSync(candidate))
136
+ continue;
137
+ try {
138
+ const r = spawnSync(candidate, ["--version"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], windowsHide: true });
139
+ if (r.status === 0) {
140
+ // "1.3.14" (bun) or "uv 0.9.2 (hash date)" -> first semver-looking token.
141
+ const semver = r.stdout.match(/\d+\.\d+(?:\.\d+)?/);
142
+ return { path: candidate, version: semver ? semver[0] : r.stdout.trim() };
143
+ }
144
+ }
145
+ catch {
146
+ // try the next candidate
147
+ }
148
+ }
149
+ return { path: null, version: null };
150
+ }
151
+ /**
152
+ * Probe Bun (upstream setup-runtime getBunPath: PATH first, then common install dirs).
153
+ *
154
+ * Returns:
155
+ * Path + version, or nulls when absent.
156
+ */
157
+ export function probeBun() {
158
+ const home = os.homedir();
159
+ const common = IS_WINDOWS
160
+ ? [path.join(home, ".bun", "bin", "bun.exe")]
161
+ : [path.join(home, ".bun", "bin", "bun"), "/usr/local/bin/bun", "/opt/homebrew/bin/bun", "/home/linuxbrew/.linuxbrew/bin/bun"];
162
+ return probeTool("bun", common);
163
+ }
164
+ /**
165
+ * Probe uv (Chroma vector store runtime; upstream setup-runtime getUvPath).
166
+ *
167
+ * Returns:
168
+ * Path + version, or nulls when absent.
169
+ */
170
+ export function probeUv() {
171
+ const home = os.homedir();
172
+ const common = IS_WINDOWS
173
+ ? [path.join(home, ".local", "bin", "uv.exe"), path.join(home, ".cargo", "bin", "uv.exe")]
174
+ : [path.join(home, ".local", "bin", "uv"), path.join(home, ".cargo", "bin", "uv"), "/usr/local/bin/uv", "/opt/homebrew/bin/uv"];
175
+ return probeTool("uv", common);
176
+ }
177
+ /**
178
+ * Whether an upstream registration (UPSTREAM_PLUGIN_KEY) is still live: present in
179
+ * installed_plugins.json, or enabledPlugins carries it at a value other than `false`. Once a
180
+ * migration sets it to `false`, later `registerHooks` runs see it as already migrated and skip
181
+ * the backup/disable branch (idempotent on re-run).
182
+ *
183
+ * Args:
184
+ * installed: Parsed installed_plugins.json (or `{}` when absent).
185
+ * settings: Parsed settings.json (or `{}` when absent).
186
+ *
187
+ * Returns:
188
+ * True when the upstream key still needs disabling.
189
+ */
190
+ export function isUpstreamRegistered(installed, settings) {
191
+ if (settings.enabledPlugins?.[UPSTREAM_PLUGIN_KEY] === false)
192
+ return false;
193
+ if (settings.enabledPlugins?.[UPSTREAM_PLUGIN_KEY] === true)
194
+ return true;
195
+ return Boolean(installed.plugins?.[UPSTREAM_PLUGIN_KEY]);
196
+ }
197
+ function readJsonSafe(file, fallback) {
198
+ if (!fs.existsSync(file))
199
+ return fallback;
200
+ const raw = fs.readFileSync(file, "utf-8").replace(/^\uFEFF/, "");
201
+ try {
202
+ return JSON.parse(raw);
203
+ }
204
+ catch (err) {
205
+ throw new Error(`Corrupt JSON file, refusing to overwrite: ${file}: ${err instanceof Error ? err.message : String(err)}`);
206
+ }
207
+ }
208
+ // Atomic write (temp + rename), as upstream shared/atomic-json.ts does.
209
+ function writeJsonAtomic(file, value) {
210
+ fs.mkdirSync(path.dirname(file), { recursive: true });
211
+ const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
212
+ fs.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n", "utf-8");
213
+ fs.renameSync(tmp, file);
214
+ }
215
+ function writeInstallMarker(dir, version, bun, uv) {
216
+ const marker = path.join(dir, ".install-version");
217
+ fs.mkdirSync(dir, { recursive: true });
218
+ fs.writeFileSync(marker, JSON.stringify({ version, bun, uv, installedAt: new Date().toISOString() }));
219
+ return marker;
220
+ }
221
+ /** MemoryPort implementation backed by the vendored claude-mem plugin build. */
222
+ export class VendoredMemory {
223
+ engineDir;
224
+ vendorDir;
225
+ pluginDir;
226
+ target;
227
+ /**
228
+ * Args:
229
+ * engineDir: Absolute path of packages/memory/vendor/claude-mem/engine.
230
+ * target: The harness this install is for (reported in results).
231
+ */
232
+ constructor(engineDir, target = "claude-code") {
233
+ this.engineDir = engineDir;
234
+ this.vendorDir = path.resolve(engineDir, "..");
235
+ this.pluginDir = path.join(this.vendorDir, "plugin");
236
+ this.target = target;
237
+ }
238
+ /** Plugin version from the vendored manifest (upstream readPluginVersion). */
239
+ version() {
240
+ const manifest = path.join(this.pluginDir, ".claude-plugin", "plugin.json");
241
+ const parsed = readJsonSafe(manifest, {});
242
+ return parsed.version ?? "0.0.0";
243
+ }
244
+ result() {
245
+ return { capabilityId: CAPABILITY_ID, target: this.target, written: [], actions: [], notes: [] };
246
+ }
247
+ /**
248
+ * Probe Bun and uv; instruct when missing. Never installs them (user's machine).
249
+ *
250
+ * Returns:
251
+ * Notes only. A missing Bun is reported as "MISSING" and blocks install().
252
+ */
253
+ async ensurePrerequisites(_ctx) {
254
+ const res = this.result();
255
+ const bun = probeBun();
256
+ const uv = probeUv();
257
+ res.notes.push(bun.path ? `Bun ${bun.version} found (${bun.path}).` : "MISSING: Bun — install from https://bun.sh (curl -fsSL https://bun.sh/install | bash), then re-run.");
258
+ res.notes.push(uv.path
259
+ ? `uv ${uv.version} found (${uv.path}).`
260
+ : "MISSING: uv — Chroma vector search needs it at runtime (https://docs.astral.sh/uv). Install is not blocked; recall degrades to SQLite-only until uv is present.");
261
+ return res;
262
+ }
263
+ /**
264
+ * Build the plugin bundles and stage the plugin in the Claude Code plugin cache
265
+ * and marketplace directories (upstream copyPluginToCache / copyPluginToMarketplace
266
+ * / installPluginDependencies / writeInstallMarker).
267
+ */
268
+ async install(_scope, ctx) {
269
+ const res = this.result();
270
+ const version = this.version();
271
+ const P = claudePluginPaths(version);
272
+ const bun = probeBun();
273
+ if (!bun.path) {
274
+ res.notes.push("Install skipped: Bun is required to build the plugin and to run its hooks.");
275
+ return res;
276
+ }
277
+ const uv = probeUv();
278
+ const scriptsDir = path.join(this.pluginDir, "scripts");
279
+ const marketplacePlugin = path.join(P.marketplaceDir, "plugin");
280
+ // 1. Build (bun install once, then build:plugin).
281
+ if (ctx.dryRun) {
282
+ res.notes.push(`Would build the plugin bundles: bun run build:plugin (in ${this.engineDir}).`);
283
+ }
284
+ else {
285
+ const bundles = ["bun-runner.js", "worker-service.cjs", "mcp-server.cjs"];
286
+ const reuse = process.env.FREEHARNESS_MEMORY_SKIP_BUILD === "1" && bundles.every((f) => fs.existsSync(path.join(scriptsDir, f)));
287
+ if (reuse) {
288
+ res.notes.push(`Reused the built plugin bundles in ${scriptsDir} (FREEHARNESS_MEMORY_SKIP_BUILD=1).`);
289
+ }
290
+ else {
291
+ if (!fs.existsSync(path.join(this.engineDir, "node_modules")))
292
+ this.run(bun.path, ["install"], this.engineDir, "bun install (engine)");
293
+ this.run(bun.path, ["run", "build:plugin"], this.engineDir, "bun run build:plugin");
294
+ for (const f of bundles) {
295
+ if (!fs.existsSync(path.join(scriptsDir, f)))
296
+ throw new Error(`build:plugin did not produce ${f}`);
297
+ }
298
+ res.notes.push(`Built plugin bundles v${version} into ${scriptsDir}.`);
299
+ }
300
+ }
301
+ // 2. copyPluginToCache: rm + cp plugin/ -> cache/<owner>/<name>/<version>/, skipped
302
+ // when the cache already holds this exact bundle (bundle.sha256 marker matches) AND
303
+ // its runtime deps still resolve; a matching marker over a broken or partial
304
+ // node_modules takes the full re-stage path instead of being reported healthy.
305
+ const digest = bundleDigest(this.pluginDir);
306
+ const markerFile = path.join(P.cacheDir, BUNDLE_MARKER_NAME);
307
+ const markerMatches = fs.existsSync(P.cacheDir) && fs.existsSync(markerFile) && fs.readFileSync(markerFile, "utf-8").trim() === digest;
308
+ const cacheUpToDate = markerMatches && fs.existsSync(path.join(P.cacheDir, "node_modules")) && this.missingPluginDeps(P.cacheDir).length === 0;
309
+ res.written.push(P.cacheDir);
310
+ if (cacheUpToDate) {
311
+ res.notes.push(`plugin cache up to date (bundle ${digest.slice(0, 8)})`);
312
+ }
313
+ else if (ctx.dryRun) {
314
+ res.notes.push(`Would re-stage the plugin cache at ${P.cacheDir} (bundle ${digest.slice(0, 8)}).`);
315
+ }
316
+ else {
317
+ fs.rmSync(P.cacheDir, { recursive: true, force: true });
318
+ fs.mkdirSync(P.cacheDir, { recursive: true });
319
+ fs.cpSync(this.pluginDir, P.cacheDir, { recursive: true, force: true, filter: (src) => path.basename(src) !== "node_modules" });
320
+ fs.writeFileSync(markerFile, `${digest}\n`, "utf-8");
321
+ }
322
+ // 3. copyPluginToMarketplace: plugin/ + marketplace manifest + package bits.
323
+ res.written.push(marketplacePlugin);
324
+ if (!ctx.dryRun) {
325
+ fs.mkdirSync(P.marketplaceDir, { recursive: true });
326
+ fs.rmSync(marketplacePlugin, { recursive: true, force: true });
327
+ fs.cpSync(this.pluginDir, marketplacePlugin, { recursive: true, force: true, filter: (src) => path.basename(src) !== "node_modules" });
328
+ for (const entry of [".claude-plugin", "PROVENANCE.yaml", "FIXUP.md"]) {
329
+ const src = path.join(this.vendorDir, entry);
330
+ if (!fs.existsSync(src))
331
+ continue;
332
+ const dst = path.join(P.marketplaceDir, entry);
333
+ fs.rmSync(dst, { recursive: true, force: true });
334
+ fs.cpSync(src, dst, { recursive: true, force: true });
335
+ }
336
+ }
337
+ // 4. installPluginDependencies in the cache dir (the hooks run from there); skipped
338
+ // with the copy when the cache already matches the bundle digest.
339
+ if (cacheUpToDate) {
340
+ res.notes.push(`Plugin runtime deps already staged in ${P.cacheDir}.`);
341
+ }
342
+ else if (ctx.dryRun) {
343
+ res.notes.push(`Would install plugin runtime deps in ${P.cacheDir} (bun install --ignore-scripts).`);
344
+ }
345
+ else {
346
+ this.run(bun.path, ["install", "--ignore-scripts"], P.cacheDir, "bun install (plugin deps)");
347
+ this.verifyPluginDeps(P.cacheDir);
348
+ res.notes.push("Plugin runtime deps installed (zod resolvable from plugin/node_modules).");
349
+ }
350
+ // 5. Install markers (cache + marketplace + marketplace/plugin, as upstream); the
351
+ // cache marker is rewritten only when the cache itself was re-staged, so an unchanged
352
+ // bundle leaves every file under the cache directory untouched.
353
+ for (const dir of [P.marketplaceDir, marketplacePlugin]) {
354
+ res.written.push(path.join(dir, ".install-version"));
355
+ if (!ctx.dryRun)
356
+ writeInstallMarker(dir, version, bun.version ?? "unknown", uv.version ?? "unknown");
357
+ }
358
+ res.written.push(path.join(P.cacheDir, ".install-version"));
359
+ if (!cacheUpToDate && !ctx.dryRun)
360
+ writeInstallMarker(P.cacheDir, version, bun.version ?? "unknown", uv.version ?? "unknown");
361
+ res.notes.push(`Plugin v${version} staged at ${P.cacheDir} (marketplace: ${P.marketplaceDir}).`);
362
+ return res;
363
+ }
364
+ /**
365
+ * Register the plugin with Claude Code (upstream registerMarketplace / registerPlugin /
366
+ * enablePluginInClaudeSettings). All three writes are additive merges: other
367
+ * marketplaces, plugins, hooks and settings keys are preserved byte-for-byte in shape.
368
+ *
369
+ * When an upstream registration (UPSTREAM_PLUGIN_KEY) is found (installed_plugins.json
370
+ * or a still-true enabledPlugins entry), this also backs up the three files below with a
371
+ * shared timestamp before the first write and disables (never deletes) the old key; the
372
+ * old cache directory is left in place for `freeharness cleanup` to offer later.
373
+ */
374
+ async registerHooks(_scope, ctx) {
375
+ const res = this.result();
376
+ const version = this.version();
377
+ const P = claudePluginPaths(version);
378
+ const now = new Date().toISOString();
379
+ const installedBefore = readJsonSafe(P.installedPlugins, {});
380
+ const settingsBefore = readJsonSafe(P.settings, {});
381
+ const migrating = isUpstreamRegistered(installedBefore, settingsBefore);
382
+ if (migrating && !ctx.dryRun) {
383
+ const stamp = now.replace(/[:.]/g, "-");
384
+ for (const f of [P.installedPlugins, P.knownMarketplaces, P.settings]) {
385
+ if (!fs.existsSync(f))
386
+ continue;
387
+ const backup = `${f}.${stamp}.bak`;
388
+ fs.copyFileSync(f, backup);
389
+ res.written.push(backup);
390
+ }
391
+ }
392
+ // known_marketplaces.json: { "<owner>": { source, installLocation, lastUpdated, autoUpdate } }
393
+ const known = readJsonSafe(P.knownMarketplaces, {});
394
+ known[MARKETPLACE_OWNER] = {
395
+ source: { source: "github", repo: `${MARKETPLACE_OWNER}/${PLUGIN_NAME}` },
396
+ installLocation: P.marketplaceDir,
397
+ lastUpdated: now,
398
+ autoUpdate: false, // our build is the source of truth; never let CC pull upstream over it
399
+ };
400
+ res.written.push(P.knownMarketplaces);
401
+ if (!ctx.dryRun)
402
+ writeJsonAtomic(P.knownMarketplaces, known);
403
+ // installed_plugins.json: { version: 2, plugins: { "<name>@<owner>": [ {scope, installPath, version, installedAt, lastUpdated} ] } }
404
+ // The upstream entry (if any) is never removed here: invariant 3 forbids deleting a
405
+ // foreign key, and the cache directory it points at stays reachable for a rollback.
406
+ const installed = readJsonSafe(P.installedPlugins, {});
407
+ if (!installed.version)
408
+ installed.version = 2;
409
+ if (!installed.plugins)
410
+ installed.plugins = {};
411
+ installed.plugins[PLUGIN_KEY] = [{ scope: "user", installPath: P.cacheDir, version, installedAt: now, lastUpdated: now }];
412
+ res.written.push(P.installedPlugins);
413
+ if (!ctx.dryRun)
414
+ writeJsonAtomic(P.installedPlugins, installed);
415
+ // settings.json: enabledPlugins[PLUGIN_KEY] = true (everything else kept); migrating
416
+ // also sets enabledPlugins[UPSTREAM_PLUGIN_KEY] = false so Claude Code stops loading it.
417
+ const settings = readJsonSafe(P.settings, {});
418
+ if (!settings.enabledPlugins)
419
+ settings.enabledPlugins = {};
420
+ const already = settings.enabledPlugins[PLUGIN_KEY] === true;
421
+ settings.enabledPlugins[PLUGIN_KEY] = true;
422
+ if (migrating)
423
+ settings.enabledPlugins[UPSTREAM_PLUGIN_KEY] = false;
424
+ res.written.push(P.settings);
425
+ if (!ctx.dryRun)
426
+ writeJsonAtomic(P.settings, settings);
427
+ if (migrating) {
428
+ res.notes.push(ctx.dryRun
429
+ ? `Would migrate from ${UPSTREAM_PLUGIN_KEY}: back up installed_plugins.json, known_marketplaces.json and settings.json before writing, register ${PLUGIN_KEY} v${version}, and set enabledPlugins["${UPSTREAM_PLUGIN_KEY}"] to false. The old cache directory is left in place.`
430
+ : `Migrated from ${UPSTREAM_PLUGIN_KEY}: backed up installed_plugins.json, known_marketplaces.json and settings.json before the first write, registered ${PLUGIN_KEY} v${version}, and set enabledPlugins["${UPSTREAM_PLUGIN_KEY}"] to false. The old cache directory is left in place.`);
431
+ }
432
+ else {
433
+ res.notes.push(`${ctx.dryRun ? "Would register" : "Registered"} plugin ${PLUGIN_KEY} v${version}: marketplace + installed_plugins + enabledPlugins (additive)${already ? "; was already enabled" : ""}.`);
434
+ }
435
+ res.notes.push("Lifecycle hooks and the mem-search MCP come from the plugin's own hooks.json / .mcp.json (no settings.json hook entries are written).");
436
+ return res;
437
+ }
438
+ /**
439
+ * Keep an existing ~/.claude-mem data directory from a delegated install. The
440
+ * plugin resolves it by convention (CLAUDE_MEM_DATA_DIR or ~/.claude-mem), so no
441
+ * write is needed and nothing is deleted.
442
+ */
443
+ async migrateExisting(_ctx) {
444
+ const res = this.result();
445
+ const dataDir = process.env.CLAUDE_MEM_DATA_DIR || path.join(os.homedir(), ".claude-mem");
446
+ if (!fs.existsSync(dataDir)) {
447
+ res.notes.push(`No existing claude-mem data dir at ${dataDir}; the worker creates it on first start.`);
448
+ return res;
449
+ }
450
+ const db = path.join(dataDir, "claude-mem.db");
451
+ const settings = readJsonSafe(path.join(dataDir, "settings.json"), {});
452
+ const redirected = typeof settings.CLAUDE_MEM_DATA_DIR === "string" ? settings.CLAUDE_MEM_DATA_DIR : null;
453
+ res.notes.push(`Existing claude-mem data kept at ${dataDir}${fs.existsSync(db) ? " (claude-mem.db present)" : ""}; the vendored plugin reads it by convention${redirected ? ` (settings redirect data to ${redirected})` : ""}. Nothing deleted.`);
454
+ const legacyMarker = path.join(dataDir, ".install-version");
455
+ if (fs.existsSync(legacyMarker))
456
+ res.notes.push("A legacy install marker exists in the data dir; the plugin markers now live in the plugin cache.");
457
+ return res;
458
+ }
459
+ run(cmd, args, cwd, label) {
460
+ const r = spawnSync(cmd, args, { cwd, stdio: "inherit", windowsHide: true, env: process.env });
461
+ if (r.error)
462
+ throw new Error(`${label} failed: ${r.error.message}`);
463
+ if (r.status !== 0)
464
+ throw new Error(`${label} failed (exit ${r.status})`);
465
+ }
466
+ // upstream verifyCriticalModules: every declared plugin dep must resolve from
467
+ // plugin/node_modules, plus the zod subpaths the worker bundle requires.
468
+ missingPluginDeps(dir) {
469
+ const pkg = readJsonSafe(path.join(dir, "package.json"), {});
470
+ const nm = path.join(dir, "node_modules");
471
+ const missing = [];
472
+ for (const dep of Object.keys(pkg.dependencies ?? {})) {
473
+ if (!fs.existsSync(path.join(nm, dep, "package.json")))
474
+ missing.push(dep);
475
+ }
476
+ for (const sub of ["v3", "v4", "v4-mini"]) {
477
+ if (!fs.existsSync(path.join(nm, "zod", sub)))
478
+ missing.push(`zod/${sub}`);
479
+ }
480
+ return missing;
481
+ }
482
+ verifyPluginDeps(dir) {
483
+ const missing = this.missingPluginDeps(dir);
484
+ if (missing.length > 0)
485
+ throw new Error(`plugin deps missing after install in ${dir}: ${missing.join(", ")}`);
486
+ }
487
+ }
@@ -0,0 +1,9 @@
1
+ import type { ExecStep } from "@freeharness/core";
2
+ import type { PackagePort } from "@freeharness/core";
3
+ export declare class NodePackage implements PackagePort {
4
+ isPresent(tool: string): Promise<boolean>;
5
+ exec(step: ExecStep): Promise<{
6
+ ok: boolean;
7
+ code: number;
8
+ }>;
9
+ }
@@ -0,0 +1,20 @@
1
+ // PackagePort adapter: probe tools and run side-effecting steps via the shell.
2
+ // Commands are FreeHarness-constructed (not user input); run through `sh -c` with
3
+ // inherited stdio so installs stream their own output.
4
+ import { spawn } from "node:child_process";
5
+ export class NodePackage {
6
+ async isPresent(tool) {
7
+ return new Promise((resolve) => {
8
+ const p = spawn("sh", ["-c", `command -v ${tool}`], { stdio: "ignore" });
9
+ p.on("close", (code) => resolve(code === 0));
10
+ p.on("error", () => resolve(false));
11
+ });
12
+ }
13
+ async exec(step) {
14
+ return new Promise((resolve) => {
15
+ const p = spawn("sh", ["-c", step.command], { cwd: step.cwd ?? process.cwd(), stdio: "inherit" });
16
+ p.on("close", (code) => resolve({ ok: code === 0, code: code ?? 1 }));
17
+ p.on("error", () => resolve({ ok: false, code: 1 }));
18
+ });
19
+ }
20
+ }