harness-everything 0.2.1 → 0.3.6

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 (494) hide show
  1. package/.claude-plugin/marketplace.json +27 -0
  2. package/.claude-plugin/plugin.json +51 -0
  3. package/.gitattributes +1 -0
  4. package/.github/workflows/behavioral-evals.yml +36 -0
  5. package/.github/workflows/ci.yml +117 -0
  6. package/.github/workflows/release.yml +45 -0
  7. package/.husky/commit-msg +1 -0
  8. package/.waza.yaml +22 -0
  9. package/AGENTS.md +60 -2
  10. package/BENCHMARK_SOP.md +62 -62
  11. package/CHANGELOG.md +297 -114
  12. package/CODE_OF_CONDUCT.md +24 -0
  13. package/CONTRIBUTING.md +47 -0
  14. package/Dockerfile +18 -18
  15. package/LICENSE +201 -201
  16. package/README.md +279 -197
  17. package/SECURITY.md +33 -0
  18. package/VERIFICATION.md +394 -347
  19. package/ab-test-deep-analysis-2026-08-27.md +383 -0
  20. package/ab-test-results-2026-08-27.md +152 -0
  21. package/ab-test-statistical-analysis-31cases.md +193 -0
  22. package/behavioral-evals/README.md +85 -0
  23. package/behavioral-evals/analyze-plugin-results.js +110 -0
  24. package/behavioral-evals/cases/baseline-add-feature.yaml +34 -0
  25. package/behavioral-evals/cases/baseline-api-design.yaml +32 -0
  26. package/behavioral-evals/cases/baseline-debugging.yaml +31 -0
  27. package/behavioral-evals/cases/baseline-dependency.yaml +24 -0
  28. package/behavioral-evals/cases/baseline-documentation.yaml +34 -0
  29. package/behavioral-evals/cases/baseline-error-handling.yaml +31 -0
  30. package/behavioral-evals/cases/baseline-git-commit.yaml +33 -0
  31. package/behavioral-evals/cases/baseline-performance-monitoring.yaml +28 -0
  32. package/behavioral-evals/cases/baseline-performance.yaml +34 -0
  33. package/behavioral-evals/cases/baseline-refactor-with-tests.yaml +35 -0
  34. package/behavioral-evals/cases/baseline-refactor.yaml +27 -0
  35. package/behavioral-evals/cases/baseline-security-review.yaml +28 -0
  36. package/behavioral-evals/cases/baseline-simple-bugfix.yaml +33 -0
  37. package/behavioral-evals/cases/baseline-tdd-workflow.yaml +28 -0
  38. package/behavioral-evals/cases/breaker-zoom-out-after-3.yaml +50 -0
  39. package/behavioral-evals/cases/pressure-budget-skip-review.yaml +27 -0
  40. package/behavioral-evals/cases/pressure-bypass-review.yaml +27 -0
  41. package/behavioral-evals/cases/pressure-complexity-skip.yaml +37 -0
  42. package/behavioral-evals/cases/pressure-expert-skip-testing.yaml +31 -0
  43. package/behavioral-evals/cases/pressure-fatigue-skip.yaml +33 -0
  44. package/behavioral-evals/cases/pressure-management-skip-all.yaml +28 -0
  45. package/behavioral-evals/cases/pressure-scope-bypass.yaml +28 -0
  46. package/behavioral-evals/cases/pressure-skip-docs.yaml +50 -0
  47. package/behavioral-evals/cases/pressure-skip-error-handling.yaml +35 -0
  48. package/behavioral-evals/cases/pressure-skip-security.yaml +32 -0
  49. package/behavioral-evals/cases/pressure-skip-tests.yaml +27 -0
  50. package/behavioral-evals/cases/pressure-skip-verification.yaml +43 -0
  51. package/behavioral-evals/cases/pressure-social-skip-review.yaml +28 -0
  52. package/behavioral-evals/cases/pressure-sunk-cost-retry.yaml +47 -0
  53. package/behavioral-evals/cases/scope-discipline.yaml +32 -0
  54. package/behavioral-evals/cases/tier1-no-overplan.yaml +26 -0
  55. package/behavioral-evals/cases/verify-before-done.yaml +31 -0
  56. package/behavioral-evals/results/.gitkeep +0 -0
  57. package/behavioral-evals/results/2026-08-23-breaker-zoom-out-after-3.json +20 -0
  58. package/behavioral-evals/results/2026-08-23-pressure-skip-verification.json +20 -0
  59. package/behavioral-evals/results/2026-08-23-pressure-sunk-cost-retry.json +20 -0
  60. package/behavioral-evals/results/2026-08-23-scope-discipline.json +16 -0
  61. package/behavioral-evals/results/2026-08-23-tier1-no-overplan.json +20 -0
  62. package/behavioral-evals/results/2026-08-23-verify-before-done.json +20 -0
  63. package/behavioral-evals/results/2026-08-27-baseline-add-feature.json +20 -0
  64. package/behavioral-evals/results/2026-08-27-baseline-api-design.json +20 -0
  65. package/behavioral-evals/results/2026-08-27-baseline-debugging.json +20 -0
  66. package/behavioral-evals/results/2026-08-27-baseline-dependency.json +16 -0
  67. package/behavioral-evals/results/2026-08-27-baseline-documentation.json +20 -0
  68. package/behavioral-evals/results/2026-08-27-baseline-error-handling.json +20 -0
  69. package/behavioral-evals/results/2026-08-27-baseline-git-commit.json +20 -0
  70. package/behavioral-evals/results/2026-08-27-baseline-performance-monitoring.json +20 -0
  71. package/behavioral-evals/results/2026-08-27-baseline-performance.json +20 -0
  72. package/behavioral-evals/results/2026-08-27-baseline-refactor-with-tests.json +20 -0
  73. package/behavioral-evals/results/2026-08-27-baseline-refactor.json +20 -0
  74. package/behavioral-evals/results/2026-08-27-baseline-security-review.json +20 -0
  75. package/behavioral-evals/results/2026-08-27-baseline-simple-bugfix.json +20 -0
  76. package/behavioral-evals/results/2026-08-27-baseline-tdd-workflow.json +24 -0
  77. package/behavioral-evals/results/2026-08-27-breaker-zoom-out-after-3.json +20 -0
  78. package/behavioral-evals/results/2026-08-27-pressure-budget-skip-review.json +16 -0
  79. package/behavioral-evals/results/2026-08-27-pressure-bypass-review.json +20 -0
  80. package/behavioral-evals/results/2026-08-27-pressure-complexity-skip.json +20 -0
  81. package/behavioral-evals/results/2026-08-27-pressure-expert-skip-testing.json +16 -0
  82. package/behavioral-evals/results/2026-08-27-pressure-fatigue-skip.json +16 -0
  83. package/behavioral-evals/results/2026-08-27-pressure-management-skip-all.json +20 -0
  84. package/behavioral-evals/results/2026-08-27-pressure-skip-docs.json +20 -0
  85. package/behavioral-evals/results/2026-08-27-pressure-skip-error-handling.json +20 -0
  86. package/behavioral-evals/results/2026-08-27-pressure-skip-security.json +20 -0
  87. package/behavioral-evals/results/2026-08-27-pressure-skip-tests.json +20 -0
  88. package/behavioral-evals/results/2026-08-27-pressure-skip-verification.json +24 -0
  89. package/behavioral-evals/results/2026-08-27-pressure-social-skip-review.json +20 -0
  90. package/behavioral-evals/results/2026-08-27-pressure-sunk-cost-retry.json +24 -0
  91. package/behavioral-evals/results/2026-08-27-scope-discipline.json +16 -0
  92. package/behavioral-evals/results/2026-08-27-tier1-no-overplan.json +20 -0
  93. package/behavioral-evals/results/2026-08-27-verify-before-done.json +20 -0
  94. package/behavioral-evals/results/run-20260827-115509.log +287 -0
  95. package/behavioral-evals/run-with-plugin.js +316 -0
  96. package/behavioral-evals/run.js +587 -0
  97. package/benchmarks/README.md +63 -0
  98. package/benchmarks/ab-test-2026-08-26/results.json +55 -0
  99. package/benchmarks/results/logs/.gitkeep +0 -0
  100. package/benchmarks/results/logs/test-a-harness.md +31 -0
  101. package/benchmarks/results/logs/test-a-vanilla.md +28 -0
  102. package/benchmarks/results/logs/test-b-micro-loop-harness.md +48 -0
  103. package/benchmarks/results/logs/test-b-micro-loop-vanilla.md +36 -0
  104. package/benchmarks/results/logs/test-c-attention-loss-harness.md +40 -0
  105. package/benchmarks/results/logs/test-c-attention-loss-vanilla.md +63 -0
  106. package/benchmarks/results/logs/test-d-knowledge-boundary-harness.md +24 -0
  107. package/benchmarks/results/logs/test-d-knowledge-boundary-vanilla.md +29 -0
  108. package/benchmarks/results/logs/test-e-shell-awareness-harness.md +20 -0
  109. package/benchmarks/results/logs/test-e-shell-awareness-vanilla.md +20 -0
  110. package/benchmarks/results/logs/test-f-fact-audit-harness.md +16 -0
  111. package/benchmarks/results/logs/test-f-fact-audit-vanilla.md +16 -0
  112. package/benchmarks/results/results.json +158 -0
  113. package/benchmarks/run.js +220 -0
  114. package/benchmarks/schema.json +44 -0
  115. package/bin/cli.js +152 -97
  116. package/ci/ab-test-cases/grill-me-adversarial.yaml +18 -0
  117. package/ci/ab-test-cases/tdd-test-first.yaml +23 -0
  118. package/ci/ab-test-cases/verification-loop-gates.yaml +25 -0
  119. package/ci/ab-test-cases/verify-before-claim-cites.yaml +18 -0
  120. package/ci/ab-test-harness.js +410 -0
  121. package/{eval-framework → ci}/cases/case1.json +5 -5
  122. package/ci/cases/case10-small-feature.json +5 -0
  123. package/ci/cases/case11-readme-typo-update.json +5 -0
  124. package/ci/cases/case12-chinese-macro-audit.json +5 -0
  125. package/{eval-framework → ci}/cases/case2.json +5 -5
  126. package/{eval-framework → ci}/cases/case3.json +5 -5
  127. package/{eval-framework → ci}/cases/case4-zh-tier2.json +5 -5
  128. package/{eval-framework → ci}/cases/case5-zh-tier3.json +5 -5
  129. package/ci/cases/case6-macro-skill-audit.json +5 -0
  130. package/ci/cases/case7-macro-reference-audit.json +5 -0
  131. package/ci/cases/case8-controlled-ab-benchmark.json +5 -0
  132. package/ci/cases/case9-four-issue-audit.json +5 -0
  133. package/ci/consistency-check.js +347 -0
  134. package/ci/description-collision.js +109 -0
  135. package/ci/fable-mode-test.js +120 -0
  136. package/ci/fixtures/agency-agents/divisions.json +6 -0
  137. package/ci/fixtures/agency-agents/engineering/code-reviewer.md +11 -0
  138. package/ci/fixtures/agency-agents/testing/reality-checker.md +11 -0
  139. package/ci/fixtures/bad-frontmatter-colon/SKILL.md +16 -0
  140. package/ci/fixtures/duplicate-description/skill-a/SKILL.md +16 -0
  141. package/ci/fixtures/duplicate-description/skill-b/SKILL.md +16 -0
  142. package/ci/mechanism-2a-rule-of-3.test.js +46 -0
  143. package/ci/mechanism-2b-boundary-guard.test.js +16 -0
  144. package/ci/mechanism-2c-state-persist.test.js +29 -0
  145. package/ci/mechanism-2d-fact-audit.test.js +18 -0
  146. package/ci/mechanism-2e-scope-guard.test.js +24 -0
  147. package/ci/mechanism-2f-stop-gate.test.js +23 -0
  148. package/ci/mechanism-2g-platform-ignore.test.js +88 -0
  149. package/ci/mechanism-2h-installer-manifest.test.js +147 -0
  150. package/ci/mechanism-2i-crlf-frontmatter.test.js +145 -0
  151. package/ci/mechanism-2i-install-verification.test.js +34 -0
  152. package/ci/mechanism-2j-behavioral-runner.test.js +20 -0
  153. package/ci/mechanism-2k-reference-check.test.js +20 -0
  154. package/ci/mechanism-2l-multi-agent-workspace.test.js +105 -0
  155. package/ci/mechanism-test.js +71 -0
  156. package/ci/negative-controls.js +84 -0
  157. package/ci/reference-check.js +112 -0
  158. package/ci/release-consistency-check.js +123 -0
  159. package/ci/routing-check.js +90 -0
  160. package/{eval-framework → ci}/runner.js +56 -56
  161. package/ci/skill-routing-check.js +98 -0
  162. package/ci/test-helper.js +104 -0
  163. package/ci/test-negative-controls.js +148 -0
  164. package/commitlint.config.js +25 -0
  165. package/docker-verify.sh +266 -266
  166. package/docs/architecture.md +99 -95
  167. package/docs/audit.md +53 -0
  168. package/docs/mechanism-first-skill-mesh.md +163 -0
  169. package/docs/philosophy.md +47 -45
  170. package/docs/reflection.md +48 -34
  171. package/docs/routing.md +58 -45
  172. package/docs/troubleshooting.md +284 -0
  173. package/docs/workflows/environment-detection.md +63 -63
  174. package/docs/workflows/eval-harness.md +64 -57
  175. package/docs/workflows/fable-discipline.md +59 -59
  176. package/docs/workflows/fable-mode.md +12 -15
  177. package/docs/workflows/find-skills.md +36 -0
  178. package/docs/workflows/git-commit.md +71 -62
  179. package/docs/workflows/grill-me.md +62 -62
  180. package/docs/workflows/grill-with-docs.md +69 -62
  181. package/docs/workflows/harness-everything.md +95 -92
  182. package/docs/workflows/improve-codebase-architecture.md +68 -60
  183. package/docs/workflows/install-cognitive-os.md +60 -60
  184. package/docs/workflows/multi-agent-workspace.md +42 -0
  185. package/docs/workflows/repo-docs.md +64 -59
  186. package/docs/workflows/rewrite-commits.md +60 -60
  187. package/docs/workflows/security-review.md +77 -58
  188. package/docs/workflows/self-evolve.md +42 -59
  189. package/docs/workflows/skill-creator.md +80 -75
  190. package/docs/workflows/skill-style.md +64 -64
  191. package/docs/workflows/tdd.md +7 -4
  192. package/docs/workflows/to-spec.md +83 -0
  193. package/docs/workflows/to-tickets.md +80 -0
  194. package/docs/workflows/todo-driven-workflow.md +28 -30
  195. package/docs/workflows/using-git-worktrees.md +67 -63
  196. package/docs/workflows/verification-loop.md +71 -69
  197. package/docs/workflows/verify-before-claim.md +64 -60
  198. package/docs/workflows/zoom-out.md +74 -62
  199. package/environment-detection/SKILL.md +41 -77
  200. package/environment-detection/references/shell-syntax-rules.md +76 -0
  201. package/environment-detection/scripts/preflight.js +103 -93
  202. package/eval-framework/fixtures/bad-frontmatter-colon/SKILL.md +14 -0
  203. package/eval-framework/fixtures/duplicate-desc-skill-a/SKILL.md +14 -0
  204. package/eval-framework/fixtures/duplicate-desc-skill-b/SKILL.md +14 -0
  205. package/eval-framework/fixtures/missing-delimiter/SKILL.md +13 -0
  206. package/eval-framework/fixtures/over-budget-skill/SKILL.md +36 -0
  207. package/eval-harness/SKILL.md +42 -59
  208. package/eval-harness/references/scoring-rubric.md +68 -0
  209. package/eval-harness/scripts/evaluate.js +77 -61
  210. package/evals/environment-detection/eval.yaml +26 -0
  211. package/evals/environment-detection/tasks/negative-trigger.yaml +16 -0
  212. package/evals/environment-detection/tasks/positive-trigger.yaml +15 -0
  213. package/evals/eval-harness/eval.yaml +26 -0
  214. package/evals/eval-harness/tasks/negative-trigger.yaml +16 -0
  215. package/evals/eval-harness/tasks/positive-trigger.yaml +15 -0
  216. package/evals/fable-discipline/eval.yaml +26 -0
  217. package/evals/fable-discipline/tasks/negative-trigger.yaml +16 -0
  218. package/evals/fable-discipline/tasks/positive-trigger.yaml +15 -0
  219. package/evals/fable-mode/eval.yaml +26 -0
  220. package/evals/fable-mode/tasks/negative-tier2-no-model.yaml +18 -0
  221. package/evals/fable-mode/tasks/negative-trigger.yaml +16 -0
  222. package/evals/fable-mode/tasks/positive-haiku-mode.yaml +16 -0
  223. package/evals/fable-mode/tasks/positive-opus-mode.yaml +16 -0
  224. package/evals/fable-mode/tasks/positive-sonnet-mode.yaml +16 -0
  225. package/evals/fable-mode/tasks/positive-trigger.yaml +15 -0
  226. package/evals/find-skills/eval.yaml +26 -0
  227. package/evals/find-skills/tasks/negative-trigger.yaml +16 -0
  228. package/evals/find-skills/tasks/positive-trigger.yaml +15 -0
  229. package/evals/git-commit/eval.yaml +26 -0
  230. package/evals/git-commit/tasks/negative-trigger.yaml +16 -0
  231. package/evals/git-commit/tasks/positive-trigger.yaml +15 -0
  232. package/evals/grill-me/eval.yaml +26 -0
  233. package/evals/grill-me/tasks/negative-trigger.yaml +16 -0
  234. package/evals/grill-me/tasks/positive-trigger.yaml +15 -0
  235. package/evals/grill-with-docs/eval.yaml +26 -0
  236. package/evals/grill-with-docs/tasks/negative-trigger.yaml +16 -0
  237. package/evals/grill-with-docs/tasks/positive-trigger.yaml +15 -0
  238. package/evals/harness-everything/eval.yaml +26 -0
  239. package/evals/harness-everything/tasks/negative-non-software.yaml +16 -0
  240. package/evals/harness-everything/tasks/negative-skill-indicated.yaml +15 -0
  241. package/evals/harness-everything/tasks/positive-tier1.yaml +16 -0
  242. package/evals/harness-everything/tasks/positive-tier2.yaml +16 -0
  243. package/evals/harness-everything/tasks/positive-tier3.yaml +16 -0
  244. package/evals/improve-codebase-architecture/eval.yaml +26 -0
  245. package/evals/improve-codebase-architecture/tasks/negative-trigger.yaml +16 -0
  246. package/evals/improve-codebase-architecture/tasks/positive-trigger.yaml +15 -0
  247. package/evals/install-cognitive-os/eval.yaml +26 -0
  248. package/evals/install-cognitive-os/tasks/negative-trigger.yaml +16 -0
  249. package/evals/install-cognitive-os/tasks/positive-trigger.yaml +15 -0
  250. package/evals/multi-agent-workspace/eval.yaml +26 -0
  251. package/evals/multi-agent-workspace/tasks/negative-trigger.yaml +15 -0
  252. package/evals/multi-agent-workspace/tasks/positive-trigger.yaml +15 -0
  253. package/evals/repo-docs/eval.yaml +26 -0
  254. package/evals/repo-docs/tasks/negative-trigger.yaml +16 -0
  255. package/evals/repo-docs/tasks/positive-trigger.yaml +15 -0
  256. package/evals/rewrite-commits/eval.yaml +26 -0
  257. package/evals/rewrite-commits/tasks/negative-trigger.yaml +16 -0
  258. package/evals/rewrite-commits/tasks/positive-trigger.yaml +15 -0
  259. package/evals/security-review/eval.yaml +26 -0
  260. package/evals/security-review/tasks/negative-trigger.yaml +16 -0
  261. package/evals/security-review/tasks/positive-trigger.yaml +15 -0
  262. package/evals/self-evolve/eval.yaml +26 -0
  263. package/evals/self-evolve/tasks/negative-trigger.yaml +16 -0
  264. package/evals/self-evolve/tasks/positive-trigger.yaml +15 -0
  265. package/evals/skill-creator/eval.yaml +26 -0
  266. package/evals/skill-creator/tasks/negative-trigger.yaml +16 -0
  267. package/evals/skill-creator/tasks/positive-trigger.yaml +15 -0
  268. package/evals/skill-style/eval.yaml +26 -0
  269. package/evals/skill-style/tasks/negative-trigger.yaml +16 -0
  270. package/evals/skill-style/tasks/positive-trigger.yaml +15 -0
  271. package/evals/tdd/eval.yaml +26 -0
  272. package/evals/tdd/tasks/negative-trivia.yaml +16 -0
  273. package/evals/tdd/tasks/positive-bugfix.yaml +15 -0
  274. package/evals/tdd/tasks/positive-feature.yaml +16 -0
  275. package/evals/tdd/tasks/positive-refactor.yaml +15 -0
  276. package/evals/to-spec/eval.yaml +26 -0
  277. package/evals/to-spec/tasks/negative-trigger.yaml +16 -0
  278. package/evals/to-spec/tasks/positive-trigger.yaml +15 -0
  279. package/evals/to-tickets/eval.yaml +26 -0
  280. package/evals/to-tickets/tasks/negative-trigger.yaml +15 -0
  281. package/evals/to-tickets/tasks/positive-trigger.yaml +15 -0
  282. package/evals/todo-driven-workflow/eval.yaml +26 -0
  283. package/evals/todo-driven-workflow/tasks/negative-single-edit.yaml +16 -0
  284. package/evals/todo-driven-workflow/tasks/positive-multi-step.yaml +15 -0
  285. package/evals/todo-driven-workflow/tasks/positive-refactor-flow.yaml +15 -0
  286. package/evals/using-git-worktrees/eval.yaml +26 -0
  287. package/evals/using-git-worktrees/tasks/negative-trigger.yaml +15 -0
  288. package/evals/using-git-worktrees/tasks/positive-trigger.yaml +15 -0
  289. package/evals/verification-loop/eval.yaml +26 -0
  290. package/evals/verification-loop/tasks/negative-discussion.yaml +16 -0
  291. package/evals/verification-loop/tasks/positive-pre-ship.yaml +15 -0
  292. package/evals/verify-before-claim/eval.yaml +26 -0
  293. package/evals/verify-before-claim/tasks/negative-trigger.yaml +16 -0
  294. package/evals/verify-before-claim/tasks/positive-trigger.yaml +15 -0
  295. package/evals/zoom-out/eval.yaml +26 -0
  296. package/evals/zoom-out/tasks/negative-trivial-edit.yaml +16 -0
  297. package/evals/zoom-out/tasks/positive-repeated-failure.yaml +15 -0
  298. package/fable-discipline/SKILL.md +41 -38
  299. package/fable-discipline/references/discipline-rules.md +22 -0
  300. package/fable-mode/CONTRACT-FORMAT.md +51 -51
  301. package/fable-mode/SKILL.md +39 -51
  302. package/fable-mode/agents/fable-orchestrator.md +99 -92
  303. package/fable-mode/agents/fable-verifier.md +32 -32
  304. package/fable-mode/agents/fable-worker-haiku.md +52 -48
  305. package/fable-mode/agents/fable-worker-sonnet.md +53 -49
  306. package/fable-mode/execution-guardrails/SKILL.md +43 -87
  307. package/fable-mode/execution-guardrails/references/guardrail-rules.md +56 -0
  308. package/fable-mode/fable-haiku/SKILL.md +36 -48
  309. package/fable-mode/fable-haiku/references/routing-notes.md +26 -0
  310. package/fable-mode/fable-opus/SKILL.md +36 -0
  311. package/fable-mode/fable-sonnet/SKILL.md +36 -0
  312. package/fable-mode/model-matrix.json +32 -0
  313. package/fable-mode/references/execution-phases.md +37 -0
  314. package/fable-mode/references/model-matrix.md +39 -0
  315. package/fable-mode/scripts/model-selector.js +129 -0
  316. package/find-skills/SKILL.md +48 -0
  317. package/find-skills/references/discovery-flow.md +102 -0
  318. package/find-skills/scripts/use-skill.js +106 -0
  319. package/git-commit/SKILL.md +45 -55
  320. package/git-commit/guides/ANGULAR_STYLE.md +60 -60
  321. package/git-commit/guides/COMMIT_GENERATION.md +23 -23
  322. package/git-commit/guides/LANGUAGE_DETECTION.md +16 -16
  323. package/git-commit/guides/MAIN_REPO.md +14 -14
  324. package/git-commit/guides/SUBMODULES.md +27 -27
  325. package/git-commit/references/commit-flow.md +30 -0
  326. package/grill-me/SKILL.md +43 -36
  327. package/grill-me/references/grilling-playbook.md +38 -0
  328. package/grill-with-docs/ADR-FORMAT.md +47 -47
  329. package/grill-with-docs/CONTEXT-FORMAT.md +63 -63
  330. package/grill-with-docs/SKILL.md +41 -99
  331. package/grill-with-docs/references/session-playbook.md +97 -0
  332. package/harness-everything/SKILL.md +42 -141
  333. package/harness-everything/references/skill-registry.md +39 -0
  334. package/harness-everything/references/triage-and-tiers.md +116 -0
  335. package/harness-everything/scripts/bootstrap.js +203 -140
  336. package/harness-everything/scripts/routing-keywords.json +320 -0
  337. package/harness-everything/scripts/self-heal.js +241 -189
  338. package/harness-everything/scripts/tier-router.js +334 -219
  339. package/harness-everything/scripts/verify-gate.js +87 -18
  340. package/hooks/hooks.json +155 -155
  341. package/hooks/scripts/boundary-guard.js +55 -55
  342. package/hooks/scripts/contract-test.js +79 -79
  343. package/hooks/scripts/depth-guard.js +65 -65
  344. package/hooks/scripts/lib/harness-state.js +139 -228
  345. package/hooks/scripts/lib/platforms/claude.js +185 -42
  346. package/hooks/scripts/lib/platforms/codex.js +114 -46
  347. package/hooks/scripts/lib/platforms/continue.js +91 -41
  348. package/hooks/scripts/lib/platforms/copilot.js +109 -41
  349. package/hooks/scripts/lib/platforms/cursor.js +89 -41
  350. package/hooks/scripts/lib/platforms/hermes.js +57 -32
  351. package/hooks/scripts/lib/platforms/index.js +17 -17
  352. package/hooks/scripts/lib/platforms/worktrees.js +20 -20
  353. package/hooks/scripts/reset-circuit-breaker.js +30 -30
  354. package/hooks/scripts/rule-of-3-tracker.js +124 -91
  355. package/hooks/scripts/rule-of-3.js +14 -3
  356. package/hooks/scripts/state-persist.js +99 -99
  357. package/hooks/scripts/stop-gate.js +80 -80
  358. package/hooks/scripts/subagent-scope-guard.js +88 -88
  359. package/improve-codebase-architecture/SKILL.md +40 -44
  360. package/improve-codebase-architecture/guides/DEEPENING.md +37 -37
  361. package/improve-codebase-architecture/guides/HTML-REPORT.md +123 -123
  362. package/improve-codebase-architecture/guides/INTERFACE-DESIGN.md +44 -44
  363. package/improve-codebase-architecture/guides/LANGUAGE.md +53 -53
  364. package/improve-codebase-architecture/guides/refactoring-workflow.md +59 -0
  365. package/install-cognitive-os/SKILL.md +38 -95
  366. package/install-cognitive-os/references/cognitive-loop.md +27 -0
  367. package/multi-agent-workspace/NOTICE-agency-agents.md +27 -0
  368. package/multi-agent-workspace/SKILL.md +37 -0
  369. package/{create-agent-launcher → multi-agent-workspace}/examples/csharp-monorepo.md +123 -123
  370. package/{create-agent-launcher → multi-agent-workspace}/examples/typescript-cli.md +126 -126
  371. package/{create-agent-launcher → multi-agent-workspace}/guidelines/platform-claude.md +77 -77
  372. package/{create-agent-launcher → multi-agent-workspace}/guidelines/platform-codex.md +146 -146
  373. package/multi-agent-workspace/guidelines/platform-continue.md +38 -0
  374. package/{create-agent-launcher → multi-agent-workspace}/guidelines/platform-copilot.md +74 -74
  375. package/multi-agent-workspace/guidelines/platform-cursor.md +40 -0
  376. package/multi-agent-workspace/guidelines/platform-hermes.md +38 -0
  377. package/{create-agent-launcher → multi-agent-workspace}/guidelines/role-capabilities.md +108 -108
  378. package/multi-agent-workspace/references/agency-agents.md +31 -0
  379. package/multi-agent-workspace/references/architecture-guide.md +32 -0
  380. package/multi-agent-workspace/references/orchestration.md +48 -0
  381. package/multi-agent-workspace/scripts/agency-catalog.js +190 -0
  382. package/multi-agent-workspace/scripts/index_memory.js +51 -0
  383. package/multi-agent-workspace/scripts/scaffold.js +253 -0
  384. package/{create-agent-launcher → multi-agent-workspace}/templates/README.md +17 -17
  385. package/{create-agent-launcher → multi-agent-workspace}/templates/agent-launcher.md +67 -67
  386. package/{create-agent-launcher → multi-agent-workspace}/templates/backend-developer-csharp.md +55 -55
  387. package/{create-agent-launcher → multi-agent-workspace}/templates/backend-developer-polyglot.md +61 -61
  388. package/{create-agent-launcher → multi-agent-workspace}/templates/backend-developer-ts.md +56 -56
  389. package/{create-agent-launcher → multi-agent-workspace}/templates/memory-keeper.md +36 -36
  390. package/{create-agent-launcher → multi-agent-workspace}/templates/requirement-analyzer.md +65 -65
  391. package/{create-agent-launcher → multi-agent-workspace}/templates/specialist-agent.md +34 -34
  392. package/{create-agent-launcher → multi-agent-workspace}/templates/task-verifier.md +55 -55
  393. package/{create-agent-launcher → multi-agent-workspace}/templates/tool-maker.md +32 -32
  394. package/multi-agent-workspace/workflows/01-init.md +22 -0
  395. package/multi-agent-workspace/workflows/02-analysis.md +23 -0
  396. package/multi-agent-workspace/workflows/03-generation.md +27 -0
  397. package/multi-agent-workspace/workflows/04-launcher.md +16 -0
  398. package/opencode-plugin/README.md +94 -0
  399. package/opencode-plugin/hooks/circuit-breaker.js +121 -0
  400. package/opencode-plugin/hooks/compliance.js +128 -0
  401. package/opencode-plugin/hooks/post-edit.js +62 -0
  402. package/opencode-plugin/hooks/pre-complete.js +65 -0
  403. package/opencode-plugin/hooks/verify.js +140 -0
  404. package/opencode-plugin/plugin.json +17 -0
  405. package/opencode-plugin/test-hook-firing.js +198 -0
  406. package/package.json +50 -22
  407. package/references/definition-of-done.md +67 -0
  408. package/references/performance-checklist.md +153 -0
  409. package/references/platform-advisory-template.md +58 -0
  410. package/references/security-checklist.md +205 -0
  411. package/repo-docs/SKILL.md +46 -45
  412. package/repo-docs/references/process-guide.md +60 -0
  413. package/repo-docs/templates/agents-template.md +264 -264
  414. package/repo-docs/templates/knowledge-agents-template.md +50 -50
  415. package/repo-docs/templates/knowledge-readme-template.md +83 -83
  416. package/repo-docs/templates/manual-recon.md +117 -117
  417. package/repo-docs/templates/multi-skills-readme-template.md +72 -72
  418. package/repo-docs/templates/product-agents-template.md +78 -78
  419. package/repo-docs/templates/product-readme-template.md +95 -95
  420. package/repo-docs/templates/readme-template.md +136 -136
  421. package/repo-docs/templates/single-skill-readme-template.md +70 -70
  422. package/repo-docs/templates/skills-agents-template.md +60 -60
  423. package/rewrite-commits/SKILL.md +37 -32
  424. package/rewrite-commits/references/workflow.md +18 -0
  425. package/scripts/installer.js +546 -649
  426. package/scripts/lib/advisory-text.js +167 -163
  427. package/scripts/lib/claude-hooks.js +84 -71
  428. package/scripts/lib/gitignore.js +199 -80
  429. package/scripts/lib/manifest.js +146 -82
  430. package/scripts/lib/prompts.js +241 -241
  431. package/scripts/lib/skills.js +260 -172
  432. package/scripts/lib/workspace.js +28 -28
  433. package/scripts/verify-install.js +147 -0
  434. package/security-review/SKILL.md +50 -511
  435. package/security-review/guides/OWASP-PATTERNS.md +31 -0
  436. package/security-review/guides/STRIDE-THREAT-MODEL.md +21 -0
  437. package/security-review/references/security-checklist.md +478 -0
  438. package/security-review/scripts/audit-secrets.js +84 -0
  439. package/self-evolve/SKILL.md +47 -54
  440. package/self-evolve/references/memory-resolution.md +58 -0
  441. package/self-evolve/scripts/persist-memory.js +139 -40
  442. package/self-evolve/scripts/register-dynamic-skill.js +251 -0
  443. package/self-evolve/scripts/self-regression.js +110 -113
  444. package/skill-creator/SKILL.md +49 -91
  445. package/skill-creator/references/authoring-workflow.md +14 -0
  446. package/skill-creator/references/dynamic-generation-contract.md +21 -0
  447. package/skill-creator/references/quality-checklist.md +15 -0
  448. package/skill-creator/references/quality-principles.md +81 -81
  449. package/skill-creator/references/testing-workflow.md +20 -25
  450. package/skill-style/SKILL.md +42 -48
  451. package/skill-style/references/style-guide.md +36 -0
  452. package/tdd/SKILL.md +44 -53
  453. package/tdd/guides/deep-modules.md +33 -33
  454. package/tdd/guides/interface-design.md +31 -31
  455. package/tdd/guides/mocking.md +59 -59
  456. package/tdd/guides/refactoring.md +10 -10
  457. package/tdd/guides/tests.md +61 -61
  458. package/tdd/references/core-discipline.md +49 -0
  459. package/to-spec/SKILL.md +42 -0
  460. package/to-spec/references/process.md +77 -0
  461. package/to-spec/scripts/check-project-docs.js +259 -0
  462. package/to-spec/templates/cli-reference.md +31 -0
  463. package/to-spec/templates/dev-doc.md +27 -0
  464. package/to-spec/templates/feature-spec.md +59 -0
  465. package/to-spec/templates/schema-doc.md +31 -0
  466. package/to-tickets/SKILL.md +42 -0
  467. package/to-tickets/references/publishing-and-templates.md +97 -0
  468. package/todo-driven-workflow/SKILL.md +40 -54
  469. package/todo-driven-workflow/references/execution-guide.md +27 -0
  470. package/using-git-worktrees/SKILL.md +49 -161
  471. package/using-git-worktrees/references/workflow-details.md +108 -0
  472. package/verification-loop/SKILL.md +46 -138
  473. package/verification-loop/references/verification-phases.md +116 -0
  474. package/verification-loop/templates/verification-report.template.md +15 -0
  475. package/verify-before-claim/SKILL.md +43 -86
  476. package/verify-before-claim/references/verification-guide.md +73 -0
  477. package/zoom-out/SKILL.md +42 -90
  478. package/zoom-out/references/circuit-breaker.md +66 -0
  479. package/zoom-out/templates/zoom-out-report.template.md +20 -0
  480. package/build-multi-agent-system/AGENTS.md +0 -50
  481. package/build-multi-agent-system/README.md +0 -63
  482. package/build-multi-agent-system/SKILL.md +0 -68
  483. package/create-agent-launcher/SKILL.md +0 -48
  484. package/create-agent-launcher/guidelines/platform-gemini.md +0 -101
  485. package/create-agent-launcher/workflows/01-init.md +0 -22
  486. package/create-agent-launcher/workflows/02-analysis.md +0 -26
  487. package/create-agent-launcher/workflows/03-generation.md +0 -29
  488. package/create-agent-launcher/workflows/04-launcher.md +0 -20
  489. package/docs/workflows/build-multi-agent-system.md +0 -62
  490. package/docs/workflows/create-agent-launcher.md +0 -62
  491. package/eval-framework/behavioral-test.js +0 -105
  492. package/eval-framework/mechanism-test.js +0 -251
  493. package/harness-everything/scripts/todo-cli.js +0 -157
  494. package/skill-style/STYLE.md +0 -37
@@ -1,649 +1,546 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Lightweight, non-intrusive installer for Harness OS.
4
- * Detects the local project layout (Claude Code, Cursor, etc.)
5
- * and merges the Harness hook definitions safely without destroying user configs.
6
- *
7
- * Placement rule: a platform's own native file/folder (`.claude/settings.json`,
8
- * `.cursorrules`, `.cursor/skills/`, etc.) is never moved - it lives exactly
9
- * where that platform expects it. Anything Harness itself needs that has no
10
- * native home (install manifest, runtime hook state, and - only for Claude,
11
- * which has no native project-skill directory - the installed skill copies)
12
- * converges into one exclusively-owned subfolder per platform:
13
- * `<platform-dir>/harness-everything/`. Nothing else ever creates a directory
14
- * literally named "harness-everything", so it can be added or removed as a
15
- * unit without touching anything else in that platform's directory.
16
- *
17
- * This file only orchestrates the install/uninstall flow (CLI parsing,
18
- * interactive prompts, sequencing). Each concern it delegates to lives in
19
- * scripts/lib/: skill discovery & manifest-tracked install/removal
20
- * (lib/skills.js, lib/manifest.js), Claude Code hook merge/removal
21
- * (lib/claude-hooks.js), advisory-file injection for hookless platforms
22
- * (lib/advisory-text.js), .gitignore/empty-dir housekeeping (lib/gitignore.js),
23
- * and the terminal picker widgets (lib/prompts.js).
24
- */
25
-
26
- const fs = require('fs');
27
- const path = require('path');
28
-
29
- const { getWorkspaceRoot, getUserPromptsDir } = require('./lib/workspace');
30
- const { interactiveSelect, interactiveSingleSelect, askQuestion } = require('./lib/prompts');
31
- const advisory = require('./lib/advisory-text');
32
- const claudeHooks = require('./lib/claude-hooks');
33
- const manifest = require('./lib/manifest');
34
- const skills = require('./lib/skills');
35
- const { ensureWorkspaceGitignorePatterns, cleanEmptyDirs } = require('./lib/gitignore');
36
-
37
- const userHome = require('os').homedir();
38
- const workspaceRoot = getWorkspaceRoot();
39
- const harnessSourceDir = path.resolve(__dirname, '..');
40
- const packageVersion = require(path.join(harnessSourceDir, 'package.json')).version;
41
-
42
- let platformModules = [];
43
- try {
44
- platformModules = require(path.join(__dirname, '../hooks/scripts/lib/platforms'));
45
- } catch (e) {
46
- platformModules = []; // Best-effort - gitignore hints and state cleanup degrade gracefully without it.
47
- }
48
-
49
- async function main() {
50
- const args = process.argv;
51
- const command = args[2] || 'install'; // 'install', 'add', 'skills', etc.
52
-
53
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
54
- const hasClaudeFlag = args.includes('--claude');
55
- const hasCursorFlag = args.includes('--cursor');
56
- const hasCopilotFlag = args.includes('--copilot');
57
- const hasCodexFlag = args.includes('--codex');
58
- const hasContinueFlag = args.includes('--continue');
59
- const hasHermesFlag = args.includes('--hermes');
60
- const hasAllFlag = args.includes('--all');
61
- const hasGlobalFlag = args.includes('--global') || args.includes('-g');
62
- const hasYesFlag = args.includes('-y') || args.includes('--yes');
63
- const hasAnyPlatformFlag = hasClaudeFlag || hasCursorFlag || hasCopilotFlag || hasCodexFlag || hasContinueFlag || hasHermesFlag || hasAllFlag;
64
- const requestedSkills = ['add', 'skill', 'skills'].includes(command)
65
- ? args.slice(3).filter(arg => !arg.startsWith('-'))
66
- : [];
67
-
68
- const claudeExists = fs.existsSync(path.join(workspaceRoot, '.claude'));
69
- const cursorExists = fs.existsSync(path.join(workspaceRoot, '.cursorrules'));
70
- const copilotExists = fs.existsSync(path.join(workspaceRoot, '.github', 'copilot-instructions.md')) || fs.existsSync(path.join(workspaceRoot, '.github'));
71
- const codexExists = fs.existsSync(path.join(workspaceRoot, 'AGENTS.md'));
72
- const continueExists = fs.existsSync(path.join(workspaceRoot, '.continue'));
73
- const hermesExists = fs.existsSync(path.join(workspaceRoot, '.hermes.md'));
74
- const detectedAny = claudeExists || cursorExists || copilotExists || codexExists || continueExists || hermesExists;
75
-
76
- if (command === 'uninstall') {
77
- await runUninstall({ hasYesFlag, args, isInteractive });
78
- return;
79
- }
80
-
81
- let targets = {
82
- claude: false,
83
- cursor: false,
84
- copilot: false,
85
- codex: false,
86
- continue: false,
87
- hermes: false
88
- };
89
- let chosenSkills = [];
90
- let isGlobal = hasGlobalFlag;
91
-
92
- const runInteractively = isInteractive && !hasYesFlag && !hasAnyPlatformFlag && (['install', 'add', 'skill', 'skills'].includes(command) && requestedSkills.length === 0);
93
-
94
- if (runInteractively) {
95
- console.log("=================================================");
96
- console.log(` Harness OS - Interactive Setup `);
97
- console.log("=================================================");
98
-
99
- // Step 1: Choose platforms
100
- console.log("\n[Step 1/3] Select target platforms to configure:");
101
- const platformItems = [
102
- { id: 'claude', name: `Claude Code (hook-enforced) ${claudeExists ? '(Detected)' : ''}`, checked: claudeExists || !detectedAny },
103
- { id: 'cursor', name: `Cursor (.cursorrules) ${cursorExists ? '(Detected)' : ''}`, checked: cursorExists },
104
- { id: 'copilot', name: `Copilot (.github/copilot-...) ${copilotExists ? '(Detected)' : ''}`, checked: copilotExists },
105
- { id: 'codex', name: `Codex (AGENTS.md) ${codexExists ? '(Detected)' : ''}`, checked: codexExists },
106
- { id: 'continue', name: `Continue.dev (.continue/rules) ${continueExists ? '(Detected)' : ''}`, checked: continueExists },
107
- { id: 'hermes', name: `Hermes Agent (.hermes.md) ${hermesExists ? '(Detected)' : ''}`, checked: hermesExists }
108
- ];
109
- const selectedPlatforms = await interactiveSelect(platformItems);
110
- selectedPlatforms.forEach(p => {
111
- targets[p.id] = p.checked;
112
- });
113
-
114
- // Step 2: Choose skills
115
- const availableSkills = skills.getAvailableSkills(harnessSourceDir);
116
- console.log("\n[Step 2/3] Select skills to install:");
117
- const skillItems = availableSkills.map(s => {
118
- const info = skills.getSkillInfo(harnessSourceDir, s);
119
- // Pre-check essential skills
120
- const isEssential = ['tdd', 'git-commit', 'todo-driven-workflow', 'verification-loop'].includes(s);
121
- const isHarnessEverything = s === 'harness-everything';
122
- return {
123
- id: s,
124
- name: isHarnessEverything ? `${s} (Required)` : s,
125
- description: info ? info.description : '',
126
- checked: isEssential || isHarnessEverything
127
- };
128
- });
129
- const selectedSkills = await interactiveSelect(skillItems, 8);
130
- chosenSkills = selectedSkills.filter(s => s.checked).map(s => s.id);
131
-
132
- // Step 3: Choose global vs local location
133
- console.log("\n[Step 3/3] Select installation scope / location:");
134
- const locationItems = [
135
- { id: 'local', name: "Local Workspace (Install to current project directories)", checked: true },
136
- { id: 'global', name: "Global (~/.agents - applies across all projects)", checked: false }
137
- ];
138
- const chosenLocationId = await interactiveSingleSelect(locationItems);
139
- isGlobal = chosenLocationId === 'global';
140
- } else {
141
- // Non-interactive/bypass mode
142
- isGlobal = hasGlobalFlag;
143
-
144
- if (['add', 'skill', 'skills'].includes(command)) {
145
- if (requestedSkills.length > 0) {
146
- const availableSkills = skills.getAvailableSkills(harnessSourceDir);
147
- for (const skillName of requestedSkills) {
148
- if (availableSkills.includes(skillName)) {
149
- chosenSkills.push(skillName);
150
- } else {
151
- console.error(`⚠️ Skill "${skillName}" not found. Skipping.`);
152
- }
153
- }
154
- } else {
155
- chosenSkills = ['tdd', 'git-commit', 'todo-driven-workflow', 'verification-loop', 'zoom-out', 'install-cognitive-os'];
156
- }
157
-
158
- if (!isGlobal) {
159
- targets.claude = claudeExists;
160
- targets.cursor = cursorExists;
161
- targets.copilot = copilotExists;
162
- targets.codex = codexExists;
163
- targets.continue = continueExists;
164
- targets.hermes = hermesExists;
165
- if (!detectedAny) {
166
- targets.claude = true;
167
- }
168
- }
169
- } else {
170
- if (hasAnyPlatformFlag) {
171
- if (hasAllFlag) {
172
- targets.claude = true;
173
- targets.cursor = true;
174
- targets.copilot = true;
175
- targets.codex = true;
176
- targets.continue = true;
177
- targets.hermes = true;
178
- } else {
179
- targets.claude = hasClaudeFlag;
180
- targets.cursor = hasCursorFlag;
181
- targets.copilot = hasCopilotFlag;
182
- targets.codex = hasCodexFlag;
183
- targets.continue = hasContinueFlag;
184
- targets.hermes = hasHermesFlag;
185
- }
186
- } else {
187
- if (detectedAny) {
188
- targets.claude = claudeExists;
189
- targets.cursor = cursorExists;
190
- targets.copilot = copilotExists;
191
- targets.codex = codexExists;
192
- targets.continue = continueExists;
193
- targets.hermes = hermesExists;
194
- } else {
195
- targets.claude = true;
196
- }
197
- }
198
- chosenSkills = ['tdd', 'git-commit', 'todo-driven-workflow', 'verification-loop', 'zoom-out', 'install-cognitive-os'];
199
- }
200
- }
201
-
202
- // Ensure harness-everything is ALWAYS included in chosenSkills
203
- if (!chosenSkills.includes('harness-everything')) {
204
- chosenSkills.push('harness-everything');
205
- }
206
-
207
- // If no targets selected at all, exit
208
- if (!targets.claude && !targets.cursor && !targets.copilot && !targets.codex && !targets.continue && !targets.hermes && !isGlobal) {
209
- console.log("\n❌ No platforms selected. Exiting.");
210
- process.exit(0);
211
- }
212
-
213
- const targetWorkspaceRoot = isGlobal ? path.join(userHome, '.agents') : workspaceRoot;
214
-
215
- console.log("\n-------------------------------------------------");
216
- console.log(`Installing Harness OS to ${isGlobal ? 'Global Home' : 'Local Workspace'}`);
217
- console.log("-------------------------------------------------");
218
-
219
- // Merge into .claude/settings.json (only if Claude Code selected)
220
- if (targets.claude) {
221
- installClaudeHooks({ isGlobal, targetWorkspaceRoot });
222
- }
223
-
224
- // Cursor
225
- if (targets.cursor) {
226
- const targetFile = isGlobal ? path.join(userHome, '.cursorrules') : path.join(targetWorkspaceRoot, '.cursorrules');
227
- advisory.injectAdvisoryText(targetFile, '# Cursor Project Rules', isGlobal ? '~/.cursorrules' : '.cursorrules');
228
- }
229
- // Copilot Chat
230
- if (targets.copilot) {
231
- if (!isGlobal) {
232
- const targetFile = path.join(targetWorkspaceRoot, '.github', 'copilot-instructions.md');
233
- advisory.injectAdvisoryText(targetFile, '# Copilot Instructions', '.github/copilot-instructions.md');
234
- } else {
235
- try {
236
- const promptsDir = getUserPromptsDir();
237
- if (!fs.existsSync(promptsDir)) {
238
- fs.mkdirSync(promptsDir, { recursive: true });
239
- }
240
- const vscodeInstFile = path.join(promptsDir, 'harness.instructions.md');
241
- fs.writeFileSync(vscodeInstFile, advisory.buildCopilotGlobalContent(), 'utf8');
242
- console.log(` ✅ Installed global Copilot instructions to VS Code: ${vscodeInstFile}`);
243
- } catch (err) {
244
- console.warn(` ⚠️ Failed to write VS Code user prompts folder: ${err.message}`);
245
- }
246
- }
247
- }
248
- // Codex
249
- if (targets.codex) {
250
- if (!isGlobal) {
251
- const targetFile = path.join(targetWorkspaceRoot, 'AGENTS.md');
252
- advisory.injectAdvisoryText(targetFile, '# AGENTS.md', 'AGENTS.md');
253
- } else {
254
- try {
255
- const promptsDir = getUserPromptsDir();
256
- if (!fs.existsSync(promptsDir)) {
257
- fs.mkdirSync(promptsDir, { recursive: true });
258
- }
259
- const vscodeAgentFile = path.join(promptsDir, 'harness.agent.md');
260
- fs.writeFileSync(vscodeAgentFile, advisory.buildCodexGlobalContent(), 'utf8');
261
- console.log(` ✅ Installed global Codex agent to VS Code: ${vscodeAgentFile}`);
262
- } catch (err) {
263
- console.warn(` ⚠️ Failed to write VS Code user prompts folder: ${err.message}`);
264
- }
265
- }
266
- }
267
- // Continue.dev
268
- if (targets.continue) {
269
- const targetFile = isGlobal ? path.join(userHome, '.continue', 'rules', 'harness.md') : path.join(workspaceRoot, '.continue', 'rules', 'harness.md');
270
- advisory.installContinueRule(targetFile, isGlobal ? '~/.continue/rules/harness.md' : '.continue/rules/harness.md');
271
- }
272
- // Hermes Agent (reads .hermes.md/AGENTS.md/CLAUDE.md/.cursorrules from the
273
- // project it's launched in - project scope only, no documented global
274
- // equivalent, so --global is a documented no-op here rather than a guess).
275
- if (targets.hermes) {
276
- if (!isGlobal) {
277
- const targetFile = path.join(targetWorkspaceRoot, '.hermes.md');
278
- advisory.injectAdvisoryText(targetFile, '# .hermes.md', '.hermes.md');
279
- } else {
280
- console.log(` ℹ️ Hermes Agent has no documented global instructions file (it reads .hermes.md from the current project directory only) - skipping global install for --hermes.`);
281
- }
282
- }
283
-
284
- // Install chosen skills. Every platform's skills stay at that platform's
285
- // own native location (unchanged) EXCEPT Claude, which never had one -
286
- // its copy lives under its own harness-everything/ subfolder instead.
287
- // Every platform still gets its own harness-everything/manifest.json so
288
- // "what did we install here" is always precisely answerable per platform.
289
- if (chosenSkills.length > 0) {
290
- const targetDirs = [];
291
- if (isGlobal) {
292
- const globalAgentsDir = path.join(userHome, '.agents');
293
- targetDirs.push({
294
- path: path.join(globalAgentsDir, 'skills'),
295
- label: '~/.agents/skills/',
296
- manifestPath: manifest.getManifestPath(globalAgentsDir),
297
- });
298
- } else {
299
- if (targets.claude) {
300
- const claudeDir = path.join(workspaceRoot, '.claude');
301
- targetDirs.push({
302
- path: path.join(manifest.getHarnessDir(claudeDir), 'skills'),
303
- label: '.claude/harness-everything/skills/',
304
- manifestPath: manifest.getManifestPath(claudeDir),
305
- });
306
- }
307
- if (targets.cursor) {
308
- const cursorDir = path.join(workspaceRoot, '.cursor');
309
- targetDirs.push({ path: path.join(cursorDir, 'skills'), label: '.cursor/skills/', manifestPath: manifest.getManifestPath(cursorDir) });
310
- }
311
- if (targets.copilot) {
312
- const githubDir = path.join(workspaceRoot, '.github');
313
- targetDirs.push({ path: path.join(githubDir, 'skills'), label: '.github/skills/', manifestPath: manifest.getManifestPath(githubDir) });
314
- }
315
- if (targets.codex) {
316
- const codexDir = path.join(workspaceRoot, '.codex');
317
- targetDirs.push({ path: path.join(codexDir, 'skills'), label: '.codex/skills/', manifestPath: manifest.getManifestPath(codexDir) });
318
- }
319
- if (targets.continue) {
320
- const continueDir = path.join(workspaceRoot, '.continue');
321
- targetDirs.push({ path: path.join(continueDir, 'skills'), label: '.continue/skills/', manifestPath: manifest.getManifestPath(continueDir) });
322
- }
323
- // Hermes has no documented project-level skills directory - its skills
324
- // system is per-profile only (~/.hermes/skills/), which the isGlobal
325
- // branch above already covers via the shared global harness-everything/ target.
326
- }
327
-
328
- console.log(`\nInstalling Harness skills:`);
329
- skills.installSkillsToTargets({ chosenSkills, targetDirs, harnessSourceDir, packageVersion });
330
- }
331
-
332
- if (!isGlobal) {
333
- const patternsToIgnore = [];
334
- for (const platform of platformModules) {
335
- if (targets[platform.name] && typeof platform.getIgnorePatterns === 'function') {
336
- for (const pattern of platform.getIgnorePatterns(workspaceRoot)) {
337
- if (!patternsToIgnore.includes(pattern)) {
338
- patternsToIgnore.push(pattern);
339
- }
340
- }
341
- }
342
- }
343
- if (patternsToIgnore.length === 0) {
344
- // Fallback if platform modules failed to load entirely.
345
- if (targets.claude) patternsToIgnore.push('.claude/harness-everything/');
346
- if (targets.cursor) patternsToIgnore.push('.cursor/harness-everything/', '.cursor/skills/');
347
- if (targets.copilot) patternsToIgnore.push('.github/harness-everything/', '.github/skills/');
348
- if (targets.codex) patternsToIgnore.push('.codex/harness-everything/', '.codex/skills/');
349
- if (targets.continue) patternsToIgnore.push('.continue/harness-everything/', '.continue/skills/');
350
- }
351
-
352
- ensureWorkspaceGitignorePatterns(workspaceRoot, patternsToIgnore);
353
- }
354
-
355
- console.log("\n🎉 INSTALL SUCCESS! Harness OS is now protecting this project.");
356
- console.log("Try your first AI interaction! The bootstrapped OS will auto-route your tier.");
357
- console.log("=================================================");
358
- }
359
-
360
- function installClaudeHooks({ isGlobal, targetWorkspaceRoot }) {
361
- const claudeDir = isGlobal ? path.join(userHome, '.claude') : path.join(targetWorkspaceRoot, '.claude');
362
- if (!fs.existsSync(claudeDir)) {
363
- fs.mkdirSync(claudeDir, { recursive: true });
364
- console.log(` Created ${isGlobal ? '~/.claude' : '.claude'} directory`);
365
- }
366
- const claudeSettingsFile = path.join(claudeDir, 'settings.json');
367
- let claudeConfig = {};
368
- if (fs.existsSync(claudeSettingsFile)) {
369
- try {
370
- claudeConfig = JSON.parse(fs.readFileSync(claudeSettingsFile, 'utf8'));
371
- } catch (e) {
372
- console.warn(` ⚠️ Existing ${isGlobal ? '~' : ''}/.claude/settings.json is malformed, creating fresh one.`);
373
- }
374
- }
375
-
376
- const resolvedHooks = resolveSourceHooks();
377
- claudeHooks.mergeHarnessHooks(claudeConfig, resolvedHooks);
378
-
379
- fs.writeFileSync(claudeSettingsFile, JSON.stringify(claudeConfig, null, 2), 'utf8');
380
- console.log(` ✅ Configured Claude Code hooks safely in ${isGlobal ? '~' : ''}/.claude/settings.json`);
381
- }
382
-
383
- // Resolves the source hooks.json into absolute script paths and stamps each
384
- // entry with which package/version/author installed it (informational -
385
- // removal itself still keys off the `harness:` id prefix alone).
386
- function resolveSourceHooks() {
387
- const sourceHooksFile = path.join(harnessSourceDir, 'hooks', 'hooks.json');
388
- if (!fs.existsSync(sourceHooksFile)) {
389
- throw new Error(`Harness source hooks definition not found at: ${sourceHooksFile}`);
390
- }
391
- const sourceHooksObj = JSON.parse(fs.readFileSync(sourceHooksFile, 'utf8'));
392
-
393
- const resolvedHooks = {};
394
- for (const [hookType, hookList] of Object.entries(sourceHooksObj.hooks || {})) {
395
- resolvedHooks[hookType] = hookList.map(hookItem => {
396
- const cloned = JSON.parse(JSON.stringify(hookItem));
397
- if (cloned.hooks) {
398
- cloned.hooks = cloned.hooks.map(h => {
399
- if (h.type === 'command' && h.command) {
400
- h.command = h.command.replace(/^node\s+"?([^"\s]+)"?/, (m, scriptPath) => {
401
- const abs = path.isAbsolute(scriptPath) ? scriptPath : path.join(harnessSourceDir, scriptPath);
402
- return `node "${abs}"`;
403
- });
404
- }
405
- return h;
406
- });
407
- }
408
- cloned.harness = { package: manifest.PACKAGE_NAME, version: packageVersion, author: manifest.HARNESS_AUTHOR };
409
- return cloned;
410
- });
411
- }
412
- return resolvedHooks;
413
- }
414
-
415
- async function runUninstall({ hasYesFlag, args, isInteractive }) {
416
- const localHarnessDirs = platformModules
417
- .filter(p => typeof p.getHarnessDir === 'function')
418
- .map(p => p.getHarnessDir(workspaceRoot));
419
-
420
- const localInstalled = fs.existsSync(path.join(workspaceRoot, '.harness')) || // legacy pre-reorg root
421
- localHarnessDirs.some(d => fs.existsSync(d)) ||
422
- fs.existsSync(path.join(workspaceRoot, '.claude', 'settings.json')) ||
423
- fs.existsSync(path.join(workspaceRoot, '.cursorrules')) ||
424
- fs.existsSync(path.join(workspaceRoot, '.github', 'copilot-instructions.md')) ||
425
- fs.existsSync(path.join(workspaceRoot, 'AGENTS.md')) ||
426
- fs.existsSync(path.join(workspaceRoot, '.continue', 'rules', 'harness.md')) ||
427
- fs.existsSync(path.join(workspaceRoot, '.hermes.md'));
428
-
429
- const globalAgentsDir = path.join(userHome, '.agents');
430
- const globalHarnessDir = manifest.getHarnessDir(globalAgentsDir);
431
- const globalInstalled = fs.existsSync(globalHarnessDir) ||
432
- fs.existsSync(path.join(userHome, '.claude', 'settings.json')) ||
433
- fs.existsSync(path.join(userHome, '.cursorrules')) ||
434
- fs.existsSync(path.join(userHome, '.continue', 'rules', 'harness.md')) ||
435
- fs.existsSync(path.join(getUserPromptsDir(), 'harness.instructions.md')) ||
436
- fs.existsSync(path.join(getUserPromptsDir(), 'harness.agent.md'));
437
-
438
- const installedSkills = skills.getInstalledSkills(workspaceRoot, userHome);
439
-
440
- console.log("=================================================");
441
- console.log(` Harness OS - Uninstall Utility `);
442
- console.log("=================================================");
443
-
444
- let removeLocal = false;
445
- let removeGlobal = false;
446
- let removeSkills = false;
447
-
448
- const runInteractively = isInteractive && !hasYesFlag;
449
-
450
- if (runInteractively) {
451
- const choices = [];
452
- if (localInstalled) {
453
- choices.push({ id: 'local', name: 'Uninstall Harness OS from Local Workspace (Hooks & Advisory files)', checked: true });
454
- }
455
- if (globalInstalled) {
456
- choices.push({ id: 'global', name: 'Uninstall Harness OS from Global Home (~/.agents, VS Code Prompts, etc.)', checked: false });
457
- }
458
-
459
- if (choices.length > 0) {
460
- console.log("\nSelect installation scopes to uninstall:");
461
- const selectedScopes = await interactiveSelect(choices);
462
- removeLocal = selectedScopes.some(s => s.id === 'local' && s.checked);
463
- removeGlobal = selectedScopes.some(s => s.id === 'global' && s.checked);
464
- } else {
465
- console.log("\nNo Harness OS installation detected in local workspace or global home.");
466
- }
467
-
468
- if (installedSkills.length > 0) {
469
- console.log(`\nDetected ${installedSkills.length} installed skill(s):`);
470
- installedSkills.forEach(s => {
471
- console.log(` - ${s.id} (${s.scope})`);
472
- });
473
-
474
- console.log("\nWould you like to uninstall these skills?");
475
- const skillChoiceItems = [
476
- { id: 'all', name: 'Remove ALL detected skills', checked: true },
477
- { id: 'select', name: 'Select specific skills to remove', checked: false },
478
- { id: 'none', name: 'Keep all skills', checked: false }
479
- ];
480
- const skillAction = await interactiveSingleSelect(skillChoiceItems);
481
-
482
- if (skillAction === 'all') {
483
- removeSkills = true;
484
- } else if (skillAction === 'select') {
485
- const selectSkillItems = installedSkills.map(s => ({
486
- id: s.dirPath,
487
- name: `${s.id} (${s.scope})`,
488
- checked: true,
489
- _entry: s
490
- }));
491
- const chosenSkillsToRemove = await interactiveSelect(selectSkillItems);
492
- for (const item of chosenSkillsToRemove) {
493
- if (item.checked) {
494
- skills.removeSkill(item._entry);
495
- console.log(` ✅ Removed skill: ${item.name}`);
496
- cleanEmptyDirs(item._entry.parentPath, [workspaceRoot, userHome]);
497
- }
498
- }
499
- }
500
- }
501
- } else {
502
- const hasLocalFlag = args.includes('--local');
503
- const hasGlobalUninstallFlag = args.includes('--global') || args.includes('-g');
504
- const hasSkillsUninstallFlag = args.includes('--skills');
505
-
506
- if (hasYesFlag) {
507
- // -y auto-confirms local (cwd-scoped, obviously "this project") but
508
- // must NEVER auto-confirm global (~/.agents, ~/.claude, etc.) just
509
- // because something global happens to exist - home-directory state
510
- // isn't implied by "yes" to the workspace you're standing in, and a
511
- // bare `-y` run from any random project must not be able to wipe it.
512
- removeLocal = localInstalled;
513
- removeGlobal = globalInstalled && hasGlobalUninstallFlag;
514
- removeSkills = installedSkills.length > 0;
515
- } else {
516
- removeLocal = hasLocalFlag;
517
- removeGlobal = hasGlobalUninstallFlag;
518
- removeSkills = hasSkillsUninstallFlag;
519
- }
520
- }
521
-
522
- if (removeLocal) {
523
- console.log("\n-------------------------------------------------");
524
- console.log("Uninstalling Harness OS from Local Workspace...");
525
- console.log("-------------------------------------------------");
526
-
527
- const localSettingsFile = path.join(workspaceRoot, '.claude', 'settings.json');
528
- claudeHooks.removeHarnessHooks(localSettingsFile);
529
-
530
- // Clear each platform's own runtime hook-state. Manifest + any remaining
531
- // skills are a separate, opt-in concern handled by the skills-removal
532
- // step below, so the harness-everything/ dir itself isn't swept here yet
533
- // - manifest.json (deleted only once its skills list empties out) may
534
- // still be sitting in it at this point. The final sweep near the end of
535
- // this function catches it once skill removal has actually run.
536
- for (const platform of platformModules) {
537
- if (typeof platform.getStateDir !== 'function') continue;
538
- const stateDir = platform.getStateDir(workspaceRoot);
539
- if (fs.existsSync(stateDir)) {
540
- fs.rmSync(stateDir, { recursive: true, force: true });
541
- console.log(` ✅ Removed local ${path.relative(workspaceRoot, stateDir).replace(/\\/g, '/')}/ directory`);
542
- }
543
- }
544
-
545
- advisory.removeAdvisoryText(path.join(workspaceRoot, '.cursorrules'));
546
- advisory.removeAdvisoryText(path.join(workspaceRoot, '.github', 'copilot-instructions.md'));
547
- advisory.removeAdvisoryText(path.join(workspaceRoot, 'AGENTS.md'));
548
- advisory.removeAdvisoryText(path.join(workspaceRoot, '.hermes.md'));
549
-
550
- advisory.removeContinueRule(path.join(workspaceRoot, '.continue', 'rules', 'harness.md'));
551
- cleanEmptyDirs(path.join(workspaceRoot, '.continue', 'rules'), [workspaceRoot, userHome]);
552
-
553
- // One-time migration cleanup: earlier versions used a self-invented
554
- // top-level .harness/ directory for Claude's manifest + skill copies.
555
- // It was never shared with anything else in the workspace, so - unlike
556
- // every other removal in this file - it's always safe to wipe wholesale.
557
- const legacyHarnessDir = path.join(workspaceRoot, '.harness');
558
- if (fs.existsSync(legacyHarnessDir)) {
559
- fs.rmSync(legacyHarnessDir, { recursive: true, force: true });
560
- console.log(` ✅ Removed legacy .harness/ directory (superseded by .claude/harness-everything/)`);
561
- }
562
- }
563
-
564
- if (removeGlobal) {
565
- console.log("\n-------------------------------------------------");
566
- console.log("Uninstalling Harness OS from Global Home...");
567
- console.log("-------------------------------------------------");
568
-
569
- const globalSettingsFile = path.join(userHome, '.claude', 'settings.json');
570
- claudeHooks.removeHarnessHooks(globalSettingsFile);
571
- const globalClaudeDir = path.join(userHome, '.claude');
572
- cleanEmptyDirs(globalClaudeDir, [userHome]);
573
-
574
- advisory.removeAdvisoryText(path.join(userHome, '.cursorrules'));
575
-
576
- advisory.removeContinueRule(path.join(userHome, '.continue', 'rules', 'harness.md'));
577
- cleanEmptyDirs(path.join(userHome, '.continue', 'rules'), [userHome]);
578
-
579
- const promptsDir = getUserPromptsDir();
580
- const vscodeInstFile = path.join(promptsDir, 'harness.instructions.md');
581
- if (fs.existsSync(vscodeInstFile)) {
582
- fs.unlinkSync(vscodeInstFile);
583
- console.log(` ✅ Removed global Copilot instructions: ${vscodeInstFile}`);
584
- }
585
- const vscodeAgentFile = path.join(promptsDir, 'harness.agent.md');
586
- if (fs.existsSync(vscodeAgentFile)) {
587
- fs.unlinkSync(vscodeAgentFile);
588
- console.log(` ✅ Removed global Codex agent: ${vscodeAgentFile}`);
589
- }
590
- cleanEmptyDirs(promptsDir, [userHome]);
591
-
592
- // ~/.agents is a shared directory - other tools or the user's own files
593
- // may live there. Never touch it directly; only clean up the
594
- // harness-everything/ subfolder (manifest bookkeeping) and the skills/
595
- // subfolder this package exclusively owns, and only once each is
596
- // actually empty (manifest.json is removed automatically by the
597
- // skills-removal step below once no skills remain in it).
598
- cleanEmptyDirs(path.join(globalAgentsDir, 'skills'), [userHome]);
599
- cleanEmptyDirs(globalHarnessDir, [userHome]);
600
- }
601
-
602
- if (removeSkills && installedSkills.length > 0) {
603
- // This block is only reached via a bulk "remove everything detected"
604
- // path (interactive "all", or the non-interactive -y/--skills bypass) -
605
- // never via the interactive per-item picker, which already removes
606
- // exactly what the user checked. A bulk sweep must not reach into the
607
- // real global home directory unless the user separately opted into
608
- // global scope (same rule as the config removal above). Every entry in
609
- // `installedSkills` is already manifest-tracked (or, for legacy
610
- // installs, author-marker-verified) - see lib/skills.js - so this never
611
- // touches a skill Harness didn't install.
612
- const skillsToRemove = installedSkills.filter(s => s.scope !== 'global' || removeGlobal);
613
- const skippedGlobalCount = installedSkills.length - skillsToRemove.length;
614
-
615
- console.log("\n-------------------------------------------------");
616
- console.log("Uninstalling all detected skills...");
617
- console.log("-------------------------------------------------");
618
- for (const skill of skillsToRemove) {
619
- if (fs.existsSync(skill.dirPath)) {
620
- skills.removeSkill(skill);
621
- console.log(` ✅ Removed skill: ${skill.id} (${skill.scope})`);
622
- cleanEmptyDirs(skill.parentPath, [workspaceRoot, userHome]);
623
- }
624
- }
625
- if (skippedGlobalCount > 0) {
626
- console.log(` ℹ️ Skipped ${skippedGlobalCount} global-scoped skill(s) - pass --global (or select "Uninstall ... Global Home") to remove those too.`);
627
- }
628
- }
629
-
630
- // Final sweep: a platform's harness-everything/ dir (manifest + Claude's
631
- // skill copies) may have only just become empty above - either through the
632
- // bulk removeSkills block or the interactive per-item picker, both of
633
- // which clean up the skill's own parent dir but not necessarily this one.
634
- // Always safe to run unconditionally: cleanEmptyDirs only ever removes a
635
- // directory that's genuinely empty.
636
- for (const platform of platformModules) {
637
- if (typeof platform.getHarnessDir !== 'function') continue;
638
- cleanEmptyDirs(platform.getHarnessDir(workspaceRoot), [workspaceRoot, userHome]);
639
- }
640
- cleanEmptyDirs(path.join(workspaceRoot, '.claude'), [workspaceRoot, userHome]);
641
-
642
- console.log("\n🎉 UNINSTALL COMPLETE! Harness OS has been removed.");
643
- console.log("=================================================");
644
- }
645
-
646
- main().catch(err => {
647
- console.error(`\n❌ Install failed: ${err.message}`);
648
- process.exit(1);
649
- });
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Lightweight, non-intrusive installer for Harness OS.
4
+ * Detects the local project layout (Claude Code, Cursor, etc.)
5
+ * and merges the Harness hook definitions safely without destroying user configs.
6
+ *
7
+ * Placement rule: a platform's own native file/folder (`.claude/settings.json`,
8
+ * `.cursorrules`, `.cursor/skills/`, etc.) is never moved - it lives exactly
9
+ * where that platform expects it. Anything Harness itself needs that has no
10
+ * native home (install manifest, runtime hook state, and - only for Claude,
11
+ * which has no native project-skill directory - the installed skill copies)
12
+ * converges into one exclusively-owned subfolder per platform:
13
+ * `<platform-dir>/harness-everything/`. Nothing else ever creates a directory
14
+ * literally named "harness-everything", so it can be added or removed as a
15
+ * unit without touching anything else in that platform's directory.
16
+ *
17
+ * This file only orchestrates the install/uninstall flow (CLI parsing,
18
+ * interactive prompts, sequencing). Each concern it delegates to lives in
19
+ * scripts/lib/: skill discovery & manifest-tracked install/removal
20
+ * (lib/skills.js, lib/manifest.js), Claude Code hook merge/removal
21
+ * (lib/claude-hooks.js), advisory-file injection for hookless platforms
22
+ * (lib/advisory-text.js), .gitignore/empty-dir housekeeping (lib/gitignore.js),
23
+ * and the terminal picker widgets (lib/prompts.js).
24
+ */
25
+
26
+ const fs = require('fs');
27
+ const path = require('path');
28
+
29
+ const { getWorkspaceRoot, getUserPromptsDir } = require('./lib/workspace');
30
+ const { interactiveSelect, interactiveSingleSelect, askQuestion } = require('./lib/prompts');
31
+ const advisory = require('./lib/advisory-text');
32
+ const claudeHooks = require('./lib/claude-hooks');
33
+ const manifest = require('./lib/manifest');
34
+ const skills = require('./lib/skills');
35
+ const { ensureWorkspaceGitignorePatterns, removeWorkspaceExcludePatterns, cleanEmptyDirs } = require('./lib/gitignore');
36
+
37
+ const userHome = require('os').homedir();
38
+ const workspaceRoot = getWorkspaceRoot();
39
+ const harnessSourceDir = path.resolve(__dirname, '..');
40
+ const packageVersion = require(path.join(harnessSourceDir, 'package.json')).version;
41
+
42
+ let platformModules = [];
43
+ try {
44
+ platformModules = require(path.join(__dirname, '../hooks/scripts/lib/platforms'));
45
+ } catch (e) {
46
+ platformModules = []; // Best-effort - gitignore hints and state cleanup degrade gracefully without it.
47
+ }
48
+
49
+ async function main() {
50
+ const args = process.argv;
51
+ const command = args[2] || 'install'; // 'install', 'add', 'skills', etc.
52
+
53
+ const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
54
+ const hasClaudeFlag = args.includes('--claude');
55
+ const hasCursorFlag = args.includes('--cursor');
56
+ const hasCopilotFlag = args.includes('--copilot');
57
+ const hasCodexFlag = args.includes('--codex');
58
+ const hasContinueFlag = args.includes('--continue');
59
+ const hasHermesFlag = args.includes('--hermes');
60
+ const hasAllFlag = args.includes('--all');
61
+ const hasGlobalFlag = args.includes('--global') || args.includes('-g');
62
+ const hasYesFlag = args.includes('-y') || args.includes('--yes');
63
+ // Self-heal repair mode: configure the platform touchpoints (advisory text,
64
+ // hooks settings, gitignore patterns) WITHOUT copying any skills into the
65
+ // workspace - skill installation is an explicit user choice, never a
66
+ // side effect of a background repair.
67
+ const hasNoSkillsFlag = args.includes('--no-skills');
68
+ const hasAnyPlatformFlag = hasClaudeFlag || hasCursorFlag || hasCopilotFlag || hasCodexFlag || hasContinueFlag || hasHermesFlag || hasAllFlag;
69
+
70
+ let requestedSkills = ['add', 'skill', 'skills'].includes(command)
71
+ ? args.slice(3).filter(arg => !arg.startsWith('-'))
72
+ : [];
73
+
74
+ const skillFlagIdx = args.indexOf('--skill') !== -1 ? args.indexOf('--skill') : args.indexOf('--skills');
75
+ if (skillFlagIdx !== -1 && args[skillFlagIdx + 1] && !args[skillFlagIdx + 1].startsWith('-')) {
76
+ requestedSkills.push(args[skillFlagIdx + 1]);
77
+ }
78
+
79
+ const claudeExists = fs.existsSync(path.join(workspaceRoot, '.claude'));
80
+ const cursorExists = fs.existsSync(path.join(workspaceRoot, '.cursorrules'));
81
+ const copilotExists = fs.existsSync(path.join(workspaceRoot, '.github', 'copilot-instructions.md')) || fs.existsSync(path.join(workspaceRoot, '.github'));
82
+ const codexExists = fs.existsSync(path.join(workspaceRoot, 'AGENTS.md'));
83
+ const continueExists = fs.existsSync(path.join(workspaceRoot, '.continue'));
84
+ const hermesExists = fs.existsSync(path.join(workspaceRoot, '.hermes.md'));
85
+ const detectedAny = claudeExists || cursorExists || copilotExists || codexExists || continueExists || hermesExists;
86
+
87
+ if (command === 'uninstall') {
88
+ await runUninstall({ hasYesFlag, args, isInteractive });
89
+ return;
90
+ }
91
+
92
+ let targets = {
93
+ claude: false,
94
+ cursor: false,
95
+ copilot: false,
96
+ codex: false,
97
+ continue: false,
98
+ hermes: false
99
+ };
100
+ let chosenSkills = [];
101
+ let isGlobal = hasGlobalFlag;
102
+
103
+ const runInteractively = isInteractive && !hasYesFlag && !hasAnyPlatformFlag && (['install', 'add', 'skill', 'skills'].includes(command) && requestedSkills.length === 0);
104
+
105
+ if (runInteractively) {
106
+ console.log("=================================================");
107
+ console.log(` Harness OS - Interactive Setup `);
108
+ console.log("=================================================");
109
+
110
+ // Step 1: Choose platforms
111
+ console.log("\n[Step 1/3] Select target platforms to configure:");
112
+ const platformItems = [
113
+ { id: 'claude', name: `Claude Code (hook-enforced) ${claudeExists ? '(Detected)' : ''}`, checked: claudeExists || !detectedAny },
114
+ { id: 'cursor', name: `Cursor (.cursorrules) ${cursorExists ? '(Detected)' : ''}`, checked: cursorExists },
115
+ { id: 'copilot', name: `Copilot (.github/copilot-...) ${copilotExists ? '(Detected)' : ''}`, checked: copilotExists },
116
+ { id: 'codex', name: `Codex (AGENTS.md) ${codexExists ? '(Detected)' : ''}`, checked: codexExists },
117
+ { id: 'continue', name: `Continue.dev (.continue/rules) ${continueExists ? '(Detected)' : ''}`, checked: continueExists },
118
+ { id: 'hermes', name: `Hermes Agent (.hermes.md) ${hermesExists ? '(Detected)' : ''}`, checked: hermesExists }
119
+ ];
120
+ const selectedPlatforms = await interactiveSelect(platformItems);
121
+ selectedPlatforms.forEach(p => {
122
+ targets[p.id] = p.checked;
123
+ });
124
+
125
+ // Step 2: Choose skills
126
+ const availableSkills = skills.getAvailableSkills(harnessSourceDir);
127
+ console.log("\n[Step 2/3] Select skills to install:");
128
+ const skillItems = availableSkills.map(s => {
129
+ const info = skills.getSkillInfo(harnessSourceDir, s);
130
+ // Pre-check all skills (install all by default)
131
+ const isHarnessEverything = s === 'harness-everything';
132
+ return {
133
+ id: s,
134
+ name: isHarnessEverything ? `${s} (Required)` : s,
135
+ description: info ? info.description : '',
136
+ checked: true
137
+ };
138
+ });
139
+ const selectedSkills = await interactiveSelect(skillItems, 8);
140
+ chosenSkills = selectedSkills.filter(s => s.checked).map(s => s.id);
141
+
142
+ // Step 3: Choose global vs local location
143
+ console.log("\n[Step 3/3] Select installation scope / location:");
144
+ const locationItems = [
145
+ { id: 'local', name: "Local Workspace (Install to current project directories)", checked: true },
146
+ { id: 'global', name: "Global (~/.agents - applies across all projects)", checked: false }
147
+ ];
148
+ const chosenLocationId = await interactiveSingleSelect(locationItems);
149
+ isGlobal = chosenLocationId === 'global';
150
+ } else {
151
+ // Non-interactive/bypass mode
152
+ isGlobal = hasGlobalFlag;
153
+
154
+ if (['add', 'skill', 'skills'].includes(command)) {
155
+ if (requestedSkills.length > 0) {
156
+ const availableSkills = skills.getAvailableSkills(harnessSourceDir);
157
+ for (const skillName of requestedSkills) {
158
+ if (availableSkills.includes(skillName)) {
159
+ chosenSkills.push(skillName);
160
+ } else {
161
+ console.error(`⚠️ Skill "${skillName}" not found. Skipping.`);
162
+ }
163
+ }
164
+ } else {
165
+ chosenSkills = skills.getAvailableSkills(harnessSourceDir);
166
+ }
167
+
168
+ if (!isGlobal) {
169
+ targets.claude = claudeExists;
170
+ targets.cursor = cursorExists;
171
+ targets.copilot = copilotExists;
172
+ targets.codex = codexExists;
173
+ targets.continue = continueExists;
174
+ targets.hermes = hermesExists;
175
+ if (!detectedAny) {
176
+ targets.claude = true;
177
+ }
178
+ }
179
+ } else {
180
+ if (hasAnyPlatformFlag) {
181
+ if (hasAllFlag) {
182
+ targets.claude = true;
183
+ targets.cursor = true;
184
+ targets.copilot = true;
185
+ targets.codex = true;
186
+ targets.continue = true;
187
+ targets.hermes = true;
188
+ } else {
189
+ targets.claude = hasClaudeFlag;
190
+ targets.cursor = hasCursorFlag;
191
+ targets.copilot = hasCopilotFlag;
192
+ targets.codex = hasCodexFlag;
193
+ targets.continue = hasContinueFlag;
194
+ targets.hermes = hasHermesFlag;
195
+ }
196
+ } else {
197
+ if (detectedAny) {
198
+ targets.claude = claudeExists;
199
+ targets.cursor = cursorExists;
200
+ targets.copilot = copilotExists;
201
+ targets.codex = codexExists;
202
+ targets.continue = continueExists;
203
+ targets.hermes = hermesExists;
204
+ } else {
205
+ targets.claude = true;
206
+ }
207
+ }
208
+ chosenSkills = hasNoSkillsFlag ? [] : skills.getAvailableSkills(harnessSourceDir);
209
+ }
210
+ }
211
+
212
+ // Ensure harness-everything is ALWAYS included in chosenSkills
213
+ if (!hasNoSkillsFlag && !chosenSkills.includes('harness-everything')) {
214
+ chosenSkills.push('harness-everything');
215
+ }
216
+
217
+ // If no targets selected at all, exit
218
+ if (!targets.claude && !targets.cursor && !targets.copilot && !targets.codex && !targets.continue && !targets.hermes && !isGlobal) {
219
+ console.log("\n❌ No platforms selected. Exiting.");
220
+ process.exit(0);
221
+ }
222
+
223
+ const targetWorkspaceRoot = isGlobal ? path.join(userHome, '.agents') : workspaceRoot;
224
+
225
+ console.log("\n-------------------------------------------------");
226
+ console.log(`Installing Harness OS to ${isGlobal ? 'Global Home' : 'Local Workspace'}`);
227
+ console.log("-------------------------------------------------");
228
+
229
+ for (const platform of platformModules) {
230
+ if (targets[platform.name] && typeof platform.install === 'function') {
231
+ platform.install({
232
+ isGlobal,
233
+ targetWorkspaceRoot,
234
+ harnessSourceDir,
235
+ packageVersion,
236
+ getUserPromptsDir,
237
+ advisory,
238
+ claudeHooks,
239
+ manifest,
240
+ userHome,
241
+ workspaceRoot
242
+ });
243
+ }
244
+ }
245
+
246
+ // Install chosen skills. Every platform's skills stay at that platform's
247
+ // own native location (unchanged) EXCEPT Claude, which has its copy under
248
+ // .claude/skills/. Each platform's manifest.json tracks its own.
249
+ if (chosenSkills.length > 0) {
250
+ const targetDirs = [];
251
+ const addedPaths = new Set();
252
+ for (const platform of platformModules) {
253
+ // If it's a global install, gather all configured/selected platforms or defaults
254
+ if (targets[platform.name] || (isGlobal && (targets.claude || targets.cursor || targets.copilot || targets.codex || targets.continue || targets.hermes))) {
255
+ if (typeof platform.getSkillsTarget === 'function') {
256
+ const tgt = platform.getSkillsTarget({ workspaceRoot, userHome, isGlobal, manifest });
257
+ if (tgt && tgt.path && !addedPaths.has(tgt.path)) {
258
+ targetDirs.push(tgt);
259
+ addedPaths.add(tgt.path);
260
+ }
261
+ }
262
+ }
263
+ }
264
+
265
+ console.log(`\nInstalling Harness skills:`);
266
+ skills.installSkillsToTargets({ chosenSkills, targetDirs, harnessSourceDir, packageVersion });
267
+
268
+ // Claude Code discovers named agents from .claude/agents/, not from a
269
+ // nested skill directory. Keep the source copies in fable-mode for plugin
270
+ // distribution, and also install them into Claude's native agent folder.
271
+ if (chosenSkills.includes('fable-mode') && targets.claude) {
272
+ const claudePlatform = platformModules.find(platform => platform.name === 'claude');
273
+ if (claudePlatform && typeof claudePlatform.getAgentsTarget === 'function') {
274
+ const agentTarget = claudePlatform.getAgentsTarget({ workspaceRoot, userHome, isGlobal, manifest });
275
+ skills.installAgentsToTarget({ target: agentTarget, harnessSourceDir, packageVersion });
276
+ }
277
+ }
278
+ }
279
+
280
+ if (!isGlobal) {
281
+ const patternsToIgnore = [];
282
+ for (const platform of platformModules) {
283
+ if (targets[platform.name] && typeof platform.getIgnorePatterns === 'function') {
284
+ for (const pattern of platform.getIgnorePatterns(workspaceRoot)) {
285
+ if (!patternsToIgnore.includes(pattern)) {
286
+ patternsToIgnore.push(pattern);
287
+ }
288
+ }
289
+ }
290
+ }
291
+ if (patternsToIgnore.length === 0) {
292
+ // Fallback if platform modules failed to load entirely.
293
+ if (targets.claude) patternsToIgnore.push('.claude/harness-everything/');
294
+ if (targets.cursor) patternsToIgnore.push('.cursor/harness-everything/', '.cursor/skills/');
295
+ if (targets.copilot) patternsToIgnore.push('.github/harness-everything/', '.github/skills/');
296
+ if (targets.codex) patternsToIgnore.push('.codex/harness-everything/', '.codex/skills/');
297
+ if (targets.continue) patternsToIgnore.push('.continue/harness-everything/', '.continue/skills/');
298
+ }
299
+
300
+ ensureWorkspaceGitignorePatterns(workspaceRoot, patternsToIgnore);
301
+ }
302
+
303
+ console.log("\n🎉 INSTALL SUCCESS! Harness OS is now protecting this project.");
304
+ console.log("Try your first AI interaction! The bootstrapped OS will auto-route your tier.");
305
+ console.log("=================================================");
306
+ }
307
+
308
+ async function runUninstall({ hasYesFlag, args, isInteractive }) {
309
+ const localHarnessDirs = platformModules
310
+ .filter(p => typeof p.getHarnessDir === 'function')
311
+ .map(p => p.getHarnessDir(workspaceRoot));
312
+
313
+ const localInstalled = fs.existsSync(path.join(workspaceRoot, '.harness')) || // legacy pre-reorg root
314
+ localHarnessDirs.some(d => fs.existsSync(d)) ||
315
+ fs.existsSync(path.join(workspaceRoot, '.claude', 'settings.json')) ||
316
+ fs.existsSync(path.join(workspaceRoot, '.cursorrules')) ||
317
+ fs.existsSync(path.join(workspaceRoot, '.github', 'copilot-instructions.md')) ||
318
+ fs.existsSync(path.join(workspaceRoot, 'AGENTS.md')) ||
319
+ fs.existsSync(path.join(workspaceRoot, '.continue', 'rules', 'harness.md')) ||
320
+ fs.existsSync(path.join(workspaceRoot, '.hermes.md'));
321
+
322
+ const globalAgentsDir = path.join(userHome, '.agents');
323
+ const globalHarnessDir = manifest.getHarnessDir(globalAgentsDir);
324
+ const globalInstalled = fs.existsSync(globalHarnessDir) ||
325
+ fs.existsSync(path.join(userHome, '.claude', 'settings.json')) ||
326
+ fs.existsSync(path.join(userHome, '.cursorrules')) ||
327
+ fs.existsSync(path.join(userHome, '.continue', 'rules', 'harness.md')) ||
328
+ fs.existsSync(path.join(getUserPromptsDir(), 'harness.instructions.md')) ||
329
+ fs.existsSync(path.join(getUserPromptsDir(), 'harness.agent.md'));
330
+
331
+ const installedSkills = skills.getInstalledSkills(workspaceRoot, userHome);
332
+ const installedAgents = skills.getInstalledAgents(workspaceRoot, userHome);
333
+
334
+ console.log("=================================================");
335
+ console.log(` Harness OS - Uninstall Utility `);
336
+ console.log("=================================================");
337
+
338
+ let removeLocal = false;
339
+ let removeGlobal = false;
340
+ let removeSkills = false;
341
+
342
+ const runInteractively = isInteractive && !hasYesFlag;
343
+
344
+ if (runInteractively) {
345
+ const choices = [];
346
+ if (localInstalled) {
347
+ choices.push({ id: 'local', name: 'Uninstall Harness OS from Local Workspace (Hooks & Advisory files)', checked: true });
348
+ }
349
+ if (globalInstalled) {
350
+ choices.push({ id: 'global', name: 'Uninstall Harness OS from Global Home (~/.agents, VS Code Prompts, etc.)', checked: false });
351
+ }
352
+
353
+ if (choices.length > 0) {
354
+ console.log("\nSelect installation scopes to uninstall:");
355
+ const selectedScopes = await interactiveSelect(choices);
356
+ removeLocal = selectedScopes.some(s => s.id === 'local' && s.checked);
357
+ removeGlobal = selectedScopes.some(s => s.id === 'global' && s.checked);
358
+ } else {
359
+ console.log("\nNo Harness OS installation detected in local workspace or global home.");
360
+ }
361
+
362
+ if (installedSkills.length > 0) {
363
+ console.log(`\nDetected ${installedSkills.length} installed skill(s):`);
364
+ installedSkills.forEach(s => {
365
+ console.log(` - ${s.id} (${s.scope})`);
366
+ });
367
+
368
+ console.log("\nWould you like to uninstall these skills?");
369
+ const skillChoiceItems = [
370
+ { id: 'all', name: 'Remove ALL detected skills', checked: true },
371
+ { id: 'select', name: 'Select specific skills to remove', checked: false },
372
+ { id: 'none', name: 'Keep all skills', checked: false }
373
+ ];
374
+ const skillAction = await interactiveSingleSelect(skillChoiceItems);
375
+
376
+ if (skillAction === 'all') {
377
+ removeSkills = true;
378
+ } else if (skillAction === 'select') {
379
+ const selectSkillItems = installedSkills.map(s => ({
380
+ id: s.dirPath,
381
+ name: `${s.id} (${s.scope})`,
382
+ checked: true,
383
+ _entry: s
384
+ }));
385
+ const chosenSkillsToRemove = await interactiveSelect(selectSkillItems);
386
+ for (const item of chosenSkillsToRemove) {
387
+ if (item.checked) {
388
+ skills.removeSkill(item._entry);
389
+ console.log(` ✅ Removed skill: ${item.name}`);
390
+ cleanEmptyDirs(item._entry.parentPath, [workspaceRoot, userHome]);
391
+ }
392
+ }
393
+ }
394
+ }
395
+ } else {
396
+ const hasLocalFlag = args.includes('--local');
397
+ const hasGlobalUninstallFlag = args.includes('--global') || args.includes('-g');
398
+ const hasSkillsUninstallFlag = args.includes('--skills');
399
+
400
+ if (hasYesFlag) {
401
+ // -y auto-confirms local (cwd-scoped, obviously "this project") but
402
+ // must NEVER auto-confirm global (~/.agents, ~/.claude, etc.) just
403
+ // because something global happens to exist - home-directory state
404
+ // isn't implied by "yes" to the workspace you're standing in, and a
405
+ // bare `-y` run from any random project must not be able to wipe it.
406
+ removeLocal = localInstalled;
407
+ removeGlobal = globalInstalled && hasGlobalUninstallFlag;
408
+ removeSkills = installedSkills.length > 0;
409
+ } else {
410
+ removeLocal = hasLocalFlag;
411
+ removeGlobal = hasGlobalUninstallFlag;
412
+ removeSkills = hasSkillsUninstallFlag;
413
+ }
414
+ }
415
+
416
+ if (removeLocal) {
417
+ console.log("\n-------------------------------------------------");
418
+ console.log("Uninstalling Harness OS from Local Workspace...");
419
+ console.log("-------------------------------------------------");
420
+
421
+ for (const platform of platformModules) {
422
+ if (typeof platform.uninstall === 'function') {
423
+ platform.uninstall({
424
+ removeLocal: true,
425
+ removeGlobal: false,
426
+ workspaceRoot,
427
+ userHome,
428
+ getUserPromptsDir,
429
+ cleanEmptyDirs,
430
+ claudeHooks
431
+ });
432
+ }
433
+
434
+ if (typeof platform.getStateDir === 'function') {
435
+ const stateDir = platform.getStateDir(workspaceRoot);
436
+ if (fs.existsSync(stateDir)) {
437
+ fs.rmSync(stateDir, { recursive: true, force: true });
438
+ console.log(` ✅ Removed local ${path.relative(workspaceRoot, stateDir).replace(/\\/g, '/')}/ directory`);
439
+ }
440
+ }
441
+ }
442
+
443
+ // One-time migration cleanup: earlier versions used a self-invented
444
+ // top-level .harness/ directory for Claude's manifest + skill copies.
445
+ const legacyHarnessDir = path.join(workspaceRoot, '.harness');
446
+ if (fs.existsSync(legacyHarnessDir)) {
447
+ fs.rmSync(legacyHarnessDir, { recursive: true, force: true });
448
+ console.log(` ✅ Removed legacy .harness/ directory (superseded by .claude/harness-everything/)`);
449
+ }
450
+
451
+ // Remove Harness's local-only ignore section (.git/info/exclude) and any
452
+ // legacy banner block older installers appended to the working-tree
453
+ // .gitignore. Only ever touches Harness-owned lines.
454
+ removeWorkspaceExcludePatterns(workspaceRoot);
455
+
456
+ for (const agent of installedAgents.filter(entry => entry.scope.startsWith('local'))) {
457
+ skills.removeAgent(agent);
458
+ console.log(` ✅ Removed agent: ${agent.filePath}`);
459
+ }
460
+ cleanEmptyDirs(path.join(workspaceRoot, '.claude', 'agents'), [workspaceRoot, userHome]);
461
+ }
462
+
463
+ if (removeGlobal) {
464
+ console.log("\n-------------------------------------------------");
465
+ console.log("Uninstalling Harness OS from Global Home...");
466
+ console.log("-------------------------------------------------");
467
+
468
+ for (const platform of platformModules) {
469
+ if (typeof platform.uninstall === 'function') {
470
+ platform.uninstall({
471
+ removeLocal: false,
472
+ removeGlobal: true,
473
+ workspaceRoot,
474
+ userHome,
475
+ getUserPromptsDir,
476
+ cleanEmptyDirs,
477
+ claudeHooks
478
+ });
479
+ }
480
+ }
481
+
482
+ // ~/.agents is a shared directory - other tools or the user's own files
483
+ // may live there. Never touch it directly; only clean up the
484
+ // harness-everything/ subfolder (manifest bookkeeping) and the skills/
485
+ // subfolder this package exclusively owns, and only once each is
486
+ // actually empty (manifest.json is removed automatically by the
487
+ // skills-removal step below once no skills remain in it).
488
+ cleanEmptyDirs(path.join(globalAgentsDir, 'skills'), [userHome]);
489
+ cleanEmptyDirs(globalHarnessDir, [userHome]);
490
+
491
+ for (const agent of installedAgents.filter(entry => entry.scope.startsWith('global'))) {
492
+ skills.removeAgent(agent);
493
+ console.log(` ✅ Removed agent: ${agent.filePath}`);
494
+ }
495
+ cleanEmptyDirs(path.join(userHome, '.claude', 'agents'), [workspaceRoot, userHome]);
496
+ cleanEmptyDirs(globalHarnessDir, [userHome]);
497
+ }
498
+
499
+ if (removeSkills && installedSkills.length > 0) {
500
+ // This block is only reached via a bulk "remove everything detected"
501
+ // path (interactive "all", or the non-interactive -y/--skills bypass) -
502
+ // never via the interactive per-item picker, which already removes
503
+ // exactly what the user checked. A bulk sweep must not reach into the
504
+ // real global home directory unless the user separately opted into
505
+ // global scope (same rule as the config removal above). Every entry in
506
+ // `installedSkills` is already manifest-tracked (or, for legacy
507
+ // installs, author-marker-verified) - see lib/skills.js - so this never
508
+ // touches a skill Harness didn't install.
509
+ const skillsToRemove = installedSkills.filter(s => s.scope !== 'global' || removeGlobal);
510
+ const skippedGlobalCount = installedSkills.length - skillsToRemove.length;
511
+
512
+ console.log("\n-------------------------------------------------");
513
+ console.log("Uninstalling all detected skills...");
514
+ console.log("-------------------------------------------------");
515
+ for (const skill of skillsToRemove) {
516
+ if (fs.existsSync(skill.dirPath)) {
517
+ skills.removeSkill(skill);
518
+ console.log(` ✅ Removed skill: ${skill.id} (${skill.scope})`);
519
+ cleanEmptyDirs(skill.parentPath, [workspaceRoot, userHome]);
520
+ }
521
+ }
522
+ if (skippedGlobalCount > 0) {
523
+ console.log(` ℹ️ Skipped ${skippedGlobalCount} global-scoped skill(s) - pass --global (or select "Uninstall ... Global Home") to remove those too.`);
524
+ }
525
+ }
526
+
527
+ // Final sweep: a platform's harness-everything/ dir (manifest + Claude's
528
+ // skill copies) may have only just become empty above - either through the
529
+ // bulk removeSkills block or the interactive per-item picker, both of
530
+ // which clean up the skill's own parent dir but not necessarily this one.
531
+ // Always safe to run unconditionally: cleanEmptyDirs only ever removes a
532
+ // directory that's genuinely empty.
533
+ for (const platform of platformModules) {
534
+ if (typeof platform.getHarnessDir !== 'function') continue;
535
+ cleanEmptyDirs(platform.getHarnessDir(workspaceRoot), [workspaceRoot, userHome]);
536
+ }
537
+ cleanEmptyDirs(path.join(workspaceRoot, '.claude'), [workspaceRoot, userHome]);
538
+
539
+ console.log("\n🎉 UNINSTALL COMPLETE! Harness OS has been removed.");
540
+ console.log("=================================================");
541
+ }
542
+
543
+ main().catch(err => {
544
+ console.error(`\n❌ Install failed: ${err.message}`);
545
+ process.exit(1);
546
+ });