@harness-engineering/cli 1.15.0 → 1.16.0

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 (495) hide show
  1. package/dist/agents/commands/codex/AGENTS.md +39 -0
  2. package/dist/agents/commands/codex/harness/add-harness-component/SKILL.md +195 -0
  3. package/dist/agents/commands/codex/harness/add-harness-component/agents/openai.yaml +3 -0
  4. package/dist/agents/commands/codex/harness/cleanup-dead-code/SKILL.md +248 -0
  5. package/dist/agents/commands/codex/harness/cleanup-dead-code/agents/openai.yaml +3 -0
  6. package/dist/agents/commands/codex/harness/detect-doc-drift/SKILL.md +182 -0
  7. package/dist/agents/commands/codex/harness/detect-doc-drift/agents/openai.yaml +3 -0
  8. package/dist/agents/commands/codex/harness/enforce-architecture/SKILL.md +299 -0
  9. package/dist/agents/commands/codex/harness/enforce-architecture/agents/openai.yaml +3 -0
  10. package/dist/agents/commands/codex/harness/harness-architecture-advisor/SKILL.md +452 -0
  11. package/dist/agents/commands/codex/harness/harness-architecture-advisor/agents/openai.yaml +3 -0
  12. package/dist/agents/commands/codex/harness/harness-autopilot/SKILL.md +919 -0
  13. package/dist/agents/commands/codex/harness/harness-autopilot/agents/openai.yaml +3 -0
  14. package/dist/agents/commands/codex/harness/harness-brainstorming/SKILL.md +409 -0
  15. package/dist/agents/commands/codex/harness/harness-brainstorming/agents/openai.yaml +3 -0
  16. package/dist/agents/commands/codex/harness/harness-code-review/SKILL.md +860 -0
  17. package/dist/agents/commands/codex/harness/harness-code-review/agents/openai.yaml +3 -0
  18. package/dist/agents/commands/codex/harness/harness-codebase-cleanup/SKILL.md +227 -0
  19. package/dist/agents/commands/codex/harness/harness-codebase-cleanup/agents/openai.yaml +3 -0
  20. package/dist/agents/commands/codex/harness/harness-debugging/SKILL.md +369 -0
  21. package/dist/agents/commands/codex/harness/harness-debugging/agents/openai.yaml +3 -0
  22. package/dist/agents/commands/codex/harness/harness-dependency-health/SKILL.md +182 -0
  23. package/dist/agents/commands/codex/harness/harness-dependency-health/agents/openai.yaml +3 -0
  24. package/dist/agents/commands/codex/harness/harness-docs-pipeline/SKILL.md +463 -0
  25. package/dist/agents/commands/codex/harness/harness-docs-pipeline/agents/openai.yaml +3 -0
  26. package/dist/agents/commands/codex/harness/harness-execution/SKILL.md +513 -0
  27. package/dist/agents/commands/codex/harness/harness-execution/agents/openai.yaml +3 -0
  28. package/dist/agents/commands/codex/harness/harness-hotspot-detector/SKILL.md +164 -0
  29. package/dist/agents/commands/codex/harness/harness-hotspot-detector/agents/openai.yaml +3 -0
  30. package/dist/agents/commands/codex/harness/harness-impact-analysis/SKILL.md +187 -0
  31. package/dist/agents/commands/codex/harness/harness-impact-analysis/agents/openai.yaml +3 -0
  32. package/dist/agents/commands/codex/harness/harness-integrity/SKILL.md +170 -0
  33. package/dist/agents/commands/codex/harness/harness-integrity/agents/openai.yaml +3 -0
  34. package/dist/agents/commands/codex/harness/harness-onboarding/SKILL.md +291 -0
  35. package/dist/agents/commands/codex/harness/harness-onboarding/agents/openai.yaml +3 -0
  36. package/dist/agents/commands/codex/harness/harness-perf/SKILL.md +263 -0
  37. package/dist/agents/commands/codex/harness/harness-perf/agents/openai.yaml +3 -0
  38. package/dist/agents/commands/codex/harness/harness-planning/SKILL.md +582 -0
  39. package/dist/agents/commands/codex/harness/harness-planning/agents/openai.yaml +3 -0
  40. package/dist/agents/commands/codex/harness/harness-refactoring/SKILL.md +172 -0
  41. package/dist/agents/commands/codex/harness/harness-refactoring/agents/openai.yaml +3 -0
  42. package/dist/agents/commands/codex/harness/harness-release-readiness/SKILL.md +692 -0
  43. package/dist/agents/commands/codex/harness/harness-release-readiness/agents/openai.yaml +3 -0
  44. package/dist/agents/commands/codex/harness/harness-roadmap/SKILL.md +598 -0
  45. package/dist/agents/commands/codex/harness/harness-roadmap/agents/openai.yaml +3 -0
  46. package/dist/agents/commands/codex/harness/harness-security-scan/SKILL.md +157 -0
  47. package/dist/agents/commands/codex/harness/harness-security-scan/agents/openai.yaml +3 -0
  48. package/dist/agents/commands/codex/harness/harness-skill-authoring/SKILL.md +295 -0
  49. package/dist/agents/commands/codex/harness/harness-skill-authoring/agents/openai.yaml +3 -0
  50. package/dist/agents/commands/codex/harness/harness-soundness-review/SKILL.md +1270 -0
  51. package/dist/agents/commands/codex/harness/harness-soundness-review/agents/openai.yaml +3 -0
  52. package/dist/agents/commands/codex/harness/harness-supply-chain-audit/SKILL.md +247 -0
  53. package/dist/agents/commands/codex/harness/harness-supply-chain-audit/agents/openai.yaml +3 -0
  54. package/dist/agents/commands/codex/harness/harness-tdd/SKILL.md +180 -0
  55. package/dist/agents/commands/codex/harness/harness-tdd/agents/openai.yaml +3 -0
  56. package/dist/agents/commands/codex/harness/harness-test-advisor/SKILL.md +163 -0
  57. package/dist/agents/commands/codex/harness/harness-test-advisor/agents/openai.yaml +3 -0
  58. package/dist/agents/commands/codex/harness/harness-verification/SKILL.md +424 -0
  59. package/dist/agents/commands/codex/harness/harness-verification/agents/openai.yaml +3 -0
  60. package/dist/agents/commands/codex/harness/harness-verify/SKILL.md +162 -0
  61. package/dist/agents/commands/codex/harness/harness-verify/agents/openai.yaml +3 -0
  62. package/dist/agents/commands/codex/harness/initialize-harness-project/SKILL.md +235 -0
  63. package/dist/agents/commands/codex/harness/initialize-harness-project/agents/openai.yaml +3 -0
  64. package/dist/agents/commands/cursor/harness/add-harness-component.mdc +200 -0
  65. package/dist/agents/commands/cursor/harness/cleanup-dead-code.mdc +253 -0
  66. package/dist/agents/commands/cursor/harness/detect-doc-drift.mdc +187 -0
  67. package/dist/agents/commands/cursor/harness/enforce-architecture.mdc +304 -0
  68. package/dist/agents/commands/cursor/harness/harness-architecture-advisor.mdc +457 -0
  69. package/dist/agents/commands/cursor/harness/harness-autopilot.mdc +924 -0
  70. package/dist/agents/commands/cursor/harness/harness-brainstorming.mdc +414 -0
  71. package/dist/agents/commands/cursor/harness/harness-code-review.mdc +865 -0
  72. package/dist/agents/commands/cursor/harness/harness-codebase-cleanup.mdc +232 -0
  73. package/dist/agents/commands/cursor/harness/harness-debugging.mdc +374 -0
  74. package/dist/agents/commands/cursor/harness/harness-dependency-health.mdc +187 -0
  75. package/dist/agents/commands/cursor/harness/harness-docs-pipeline.mdc +468 -0
  76. package/dist/agents/commands/cursor/harness/harness-execution.mdc +518 -0
  77. package/dist/agents/commands/cursor/harness/harness-hotspot-detector.mdc +169 -0
  78. package/dist/agents/commands/cursor/harness/harness-impact-analysis.mdc +192 -0
  79. package/dist/agents/commands/cursor/harness/harness-integrity.mdc +175 -0
  80. package/dist/agents/commands/cursor/harness/harness-onboarding.mdc +296 -0
  81. package/dist/agents/commands/cursor/harness/harness-perf.mdc +268 -0
  82. package/dist/agents/commands/cursor/harness/harness-planning.mdc +587 -0
  83. package/dist/agents/commands/cursor/harness/harness-refactoring.mdc +177 -0
  84. package/dist/agents/commands/cursor/harness/harness-release-readiness.mdc +697 -0
  85. package/dist/agents/commands/cursor/harness/harness-roadmap.mdc +603 -0
  86. package/dist/agents/commands/cursor/harness/harness-security-scan.mdc +162 -0
  87. package/dist/agents/commands/cursor/harness/harness-skill-authoring.mdc +300 -0
  88. package/dist/agents/commands/cursor/harness/harness-soundness-review.mdc +1275 -0
  89. package/dist/agents/commands/cursor/harness/harness-supply-chain-audit.mdc +252 -0
  90. package/dist/agents/commands/cursor/harness/harness-tdd.mdc +185 -0
  91. package/dist/agents/commands/cursor/harness/harness-test-advisor.mdc +168 -0
  92. package/dist/agents/commands/cursor/harness/harness-verification.mdc +429 -0
  93. package/dist/agents/commands/cursor/harness/harness-verify.mdc +167 -0
  94. package/dist/agents/commands/cursor/harness/initialize-harness-project.mdc +240 -0
  95. package/dist/agents/skills/claude-code/enforce-architecture/SKILL.md +52 -0
  96. package/dist/agents/skills/claude-code/harness-api-design/SKILL.md +52 -0
  97. package/dist/agents/skills/claude-code/harness-architecture-advisor/SKILL.md +52 -0
  98. package/dist/agents/skills/claude-code/harness-auth/SKILL.md +52 -0
  99. package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +123 -14
  100. package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +6 -0
  101. package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +97 -3
  102. package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +6 -0
  103. package/dist/agents/skills/claude-code/harness-codebase-cleanup/SKILL.md +2 -4
  104. package/dist/agents/skills/claude-code/harness-database/SKILL.md +52 -0
  105. package/dist/agents/skills/claude-code/harness-deployment/SKILL.md +52 -0
  106. package/dist/agents/skills/claude-code/harness-planning/SKILL.md +99 -3
  107. package/dist/agents/skills/claude-code/harness-planning/skill.yaml +6 -0
  108. package/dist/agents/skills/claude-code/harness-pre-commit-review/SKILL.md +1 -1
  109. package/dist/agents/skills/claude-code/harness-security-review/SKILL.md +27 -7
  110. package/dist/agents/skills/claude-code/harness-security-scan/SKILL.md +52 -0
  111. package/dist/agents/skills/claude-code/harness-supply-chain-audit/SKILL.md +281 -0
  112. package/dist/agents/skills/claude-code/harness-supply-chain-audit/skill.yaml +51 -0
  113. package/dist/agents/skills/codex/add-harness-component/SKILL.md +192 -0
  114. package/dist/agents/skills/codex/add-harness-component/skill.yaml +33 -0
  115. package/dist/agents/skills/codex/align-documentation/SKILL.md +213 -0
  116. package/dist/agents/skills/codex/align-documentation/skill.yaml +32 -0
  117. package/dist/agents/skills/codex/check-mechanical-constraints/SKILL.md +191 -0
  118. package/dist/agents/skills/codex/check-mechanical-constraints/skill.yaml +33 -0
  119. package/dist/agents/skills/codex/cleanup-dead-code/SKILL.md +245 -0
  120. package/dist/agents/skills/codex/cleanup-dead-code/skill.yaml +34 -0
  121. package/dist/agents/skills/codex/detect-doc-drift/SKILL.md +179 -0
  122. package/dist/agents/skills/codex/detect-doc-drift/skill.yaml +31 -0
  123. package/dist/agents/skills/codex/enforce-architecture/SKILL.md +296 -0
  124. package/dist/agents/skills/codex/enforce-architecture/skill.yaml +35 -0
  125. package/dist/agents/skills/codex/harness-accessibility/SKILL.md +281 -0
  126. package/dist/agents/skills/codex/harness-accessibility/skill.yaml +52 -0
  127. package/dist/agents/skills/codex/harness-api-design/SKILL.md +356 -0
  128. package/dist/agents/skills/codex/harness-api-design/skill.yaml +74 -0
  129. package/dist/agents/skills/codex/harness-architecture-advisor/SKILL.md +449 -0
  130. package/dist/agents/skills/codex/harness-architecture-advisor/skill.yaml +49 -0
  131. package/dist/agents/skills/codex/harness-auth/SKILL.md +331 -0
  132. package/dist/agents/skills/codex/harness-auth/skill.yaml +81 -0
  133. package/dist/agents/skills/codex/harness-autopilot/SKILL.md +916 -0
  134. package/dist/agents/skills/codex/harness-autopilot/skill.yaml +67 -0
  135. package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +406 -0
  136. package/dist/agents/skills/codex/harness-brainstorming/skill.yaml +50 -0
  137. package/dist/agents/skills/codex/harness-caching/SKILL.md +309 -0
  138. package/dist/agents/skills/codex/harness-caching/skill.yaml +73 -0
  139. package/dist/agents/skills/codex/harness-chaos/SKILL.md +295 -0
  140. package/dist/agents/skills/codex/harness-chaos/skill.yaml +72 -0
  141. package/dist/agents/skills/codex/harness-code-review/SKILL.md +857 -0
  142. package/dist/agents/skills/codex/harness-code-review/skill.yaml +52 -0
  143. package/dist/agents/skills/codex/harness-codebase-cleanup/SKILL.md +224 -0
  144. package/dist/agents/skills/codex/harness-codebase-cleanup/skill.yaml +65 -0
  145. package/dist/agents/skills/codex/harness-compliance/SKILL.md +303 -0
  146. package/dist/agents/skills/codex/harness-compliance/skill.yaml +78 -0
  147. package/dist/agents/skills/codex/harness-containerization/SKILL.md +284 -0
  148. package/dist/agents/skills/codex/harness-containerization/skill.yaml +80 -0
  149. package/dist/agents/skills/codex/harness-data-pipeline/SKILL.md +274 -0
  150. package/dist/agents/skills/codex/harness-data-pipeline/skill.yaml +81 -0
  151. package/dist/agents/skills/codex/harness-data-validation/SKILL.md +343 -0
  152. package/dist/agents/skills/codex/harness-data-validation/skill.yaml +75 -0
  153. package/dist/agents/skills/codex/harness-database/SKILL.md +310 -0
  154. package/dist/agents/skills/codex/harness-database/skill.yaml +80 -0
  155. package/dist/agents/skills/codex/harness-debugging/SKILL.md +366 -0
  156. package/dist/agents/skills/codex/harness-debugging/skill.yaml +48 -0
  157. package/dist/agents/skills/codex/harness-dependency-health/SKILL.md +179 -0
  158. package/dist/agents/skills/codex/harness-dependency-health/skill.yaml +42 -0
  159. package/dist/agents/skills/codex/harness-deployment/SKILL.md +307 -0
  160. package/dist/agents/skills/codex/harness-deployment/skill.yaml +77 -0
  161. package/dist/agents/skills/codex/harness-design/SKILL.md +265 -0
  162. package/dist/agents/skills/codex/harness-design/skill.yaml +54 -0
  163. package/dist/agents/skills/codex/harness-design-mobile/SKILL.md +336 -0
  164. package/dist/agents/skills/codex/harness-design-mobile/skill.yaml +50 -0
  165. package/dist/agents/skills/codex/harness-design-system/SKILL.md +282 -0
  166. package/dist/agents/skills/codex/harness-design-system/skill.yaml +51 -0
  167. package/dist/agents/skills/codex/harness-design-web/SKILL.md +360 -0
  168. package/dist/agents/skills/codex/harness-design-web/skill.yaml +53 -0
  169. package/dist/agents/skills/codex/harness-diagnostics/SKILL.md +318 -0
  170. package/dist/agents/skills/codex/harness-diagnostics/skill.yaml +51 -0
  171. package/dist/agents/skills/codex/harness-docs-pipeline/SKILL.md +460 -0
  172. package/dist/agents/skills/codex/harness-docs-pipeline/skill.yaml +70 -0
  173. package/dist/agents/skills/codex/harness-dx/SKILL.md +276 -0
  174. package/dist/agents/skills/codex/harness-dx/skill.yaml +76 -0
  175. package/dist/agents/skills/codex/harness-e2e/SKILL.md +245 -0
  176. package/dist/agents/skills/codex/harness-e2e/skill.yaml +78 -0
  177. package/dist/agents/skills/codex/harness-event-driven/SKILL.md +280 -0
  178. package/dist/agents/skills/codex/harness-event-driven/skill.yaml +77 -0
  179. package/dist/agents/skills/codex/harness-execution/SKILL.md +510 -0
  180. package/dist/agents/skills/codex/harness-execution/skill.yaml +52 -0
  181. package/dist/agents/skills/codex/harness-feature-flags/SKILL.md +287 -0
  182. package/dist/agents/skills/codex/harness-feature-flags/skill.yaml +74 -0
  183. package/dist/agents/skills/codex/harness-git-workflow/SKILL.md +268 -0
  184. package/dist/agents/skills/codex/harness-git-workflow/skill.yaml +32 -0
  185. package/dist/agents/skills/codex/harness-hotspot-detector/SKILL.md +161 -0
  186. package/dist/agents/skills/codex/harness-hotspot-detector/skill.yaml +45 -0
  187. package/dist/agents/skills/codex/harness-i18n/SKILL.md +484 -0
  188. package/dist/agents/skills/codex/harness-i18n/skill.yaml +55 -0
  189. package/dist/agents/skills/codex/harness-i18n-process/SKILL.md +388 -0
  190. package/dist/agents/skills/codex/harness-i18n-process/skill.yaml +44 -0
  191. package/dist/agents/skills/codex/harness-i18n-workflow/SKILL.md +512 -0
  192. package/dist/agents/skills/codex/harness-i18n-workflow/skill.yaml +54 -0
  193. package/dist/agents/skills/codex/harness-impact-analysis/SKILL.md +184 -0
  194. package/dist/agents/skills/codex/harness-impact-analysis/skill.yaml +45 -0
  195. package/dist/agents/skills/codex/harness-incident-response/SKILL.md +223 -0
  196. package/dist/agents/skills/codex/harness-incident-response/skill.yaml +78 -0
  197. package/dist/agents/skills/codex/harness-infrastructure-as-code/SKILL.md +279 -0
  198. package/dist/agents/skills/codex/harness-infrastructure-as-code/skill.yaml +80 -0
  199. package/dist/agents/skills/codex/harness-integration-test/SKILL.md +271 -0
  200. package/dist/agents/skills/codex/harness-integration-test/skill.yaml +73 -0
  201. package/dist/agents/skills/codex/harness-integrity/SKILL.md +167 -0
  202. package/dist/agents/skills/codex/harness-integrity/skill.yaml +48 -0
  203. package/dist/agents/skills/codex/harness-knowledge-mapper/SKILL.md +195 -0
  204. package/dist/agents/skills/codex/harness-knowledge-mapper/skill.yaml +50 -0
  205. package/dist/agents/skills/codex/harness-load-testing/SKILL.md +274 -0
  206. package/dist/agents/skills/codex/harness-load-testing/skill.yaml +79 -0
  207. package/dist/agents/skills/codex/harness-ml-ops/SKILL.md +341 -0
  208. package/dist/agents/skills/codex/harness-ml-ops/skill.yaml +79 -0
  209. package/dist/agents/skills/codex/harness-mobile-patterns/SKILL.md +326 -0
  210. package/dist/agents/skills/codex/harness-mobile-patterns/skill.yaml +82 -0
  211. package/dist/agents/skills/codex/harness-mutation-test/SKILL.md +251 -0
  212. package/dist/agents/skills/codex/harness-mutation-test/skill.yaml +70 -0
  213. package/dist/agents/skills/codex/harness-observability/SKILL.md +283 -0
  214. package/dist/agents/skills/codex/harness-observability/skill.yaml +78 -0
  215. package/dist/agents/skills/codex/harness-onboarding/SKILL.md +288 -0
  216. package/dist/agents/skills/codex/harness-onboarding/skill.yaml +31 -0
  217. package/dist/agents/skills/codex/harness-parallel-agents/SKILL.md +256 -0
  218. package/dist/agents/skills/codex/harness-parallel-agents/skill.yaml +34 -0
  219. package/dist/agents/skills/codex/harness-perf/SKILL.md +260 -0
  220. package/dist/agents/skills/codex/harness-perf/skill.yaml +51 -0
  221. package/dist/agents/skills/codex/harness-perf-tdd/SKILL.md +249 -0
  222. package/dist/agents/skills/codex/harness-perf-tdd/skill.yaml +48 -0
  223. package/dist/agents/skills/codex/harness-planning/SKILL.md +579 -0
  224. package/dist/agents/skills/codex/harness-planning/skill.yaml +56 -0
  225. package/dist/agents/skills/codex/harness-pre-commit-review/SKILL.md +324 -0
  226. package/dist/agents/skills/codex/harness-pre-commit-review/skill.yaml +34 -0
  227. package/dist/agents/skills/codex/harness-product-spec/SKILL.md +285 -0
  228. package/dist/agents/skills/codex/harness-product-spec/skill.yaml +72 -0
  229. package/dist/agents/skills/codex/harness-property-test/SKILL.md +281 -0
  230. package/dist/agents/skills/codex/harness-property-test/skill.yaml +71 -0
  231. package/dist/agents/skills/codex/harness-refactoring/SKILL.md +169 -0
  232. package/dist/agents/skills/codex/harness-refactoring/skill.yaml +34 -0
  233. package/dist/agents/skills/codex/harness-release-readiness/SKILL.md +689 -0
  234. package/dist/agents/skills/codex/harness-release-readiness/skill.yaml +58 -0
  235. package/dist/agents/skills/codex/harness-resilience/SKILL.md +255 -0
  236. package/dist/agents/skills/codex/harness-resilience/skill.yaml +76 -0
  237. package/dist/agents/skills/codex/harness-roadmap/SKILL.md +595 -0
  238. package/dist/agents/skills/codex/harness-roadmap/skill.yaml +44 -0
  239. package/dist/agents/skills/codex/harness-secrets/SKILL.md +293 -0
  240. package/dist/agents/skills/codex/harness-secrets/skill.yaml +76 -0
  241. package/dist/agents/skills/codex/harness-security-review/SKILL.md +260 -0
  242. package/dist/agents/skills/codex/harness-security-review/skill.yaml +53 -0
  243. package/dist/agents/skills/codex/harness-security-scan/SKILL.md +154 -0
  244. package/dist/agents/skills/codex/harness-security-scan/skill.yaml +42 -0
  245. package/dist/agents/skills/codex/harness-skill-authoring/SKILL.md +292 -0
  246. package/dist/agents/skills/codex/harness-skill-authoring/skill.yaml +33 -0
  247. package/dist/agents/skills/codex/harness-soundness-review/SKILL.md +1267 -0
  248. package/dist/agents/skills/codex/harness-soundness-review/skill.yaml +49 -0
  249. package/dist/agents/skills/codex/harness-sql-review/SKILL.md +315 -0
  250. package/dist/agents/skills/codex/harness-sql-review/skill.yaml +74 -0
  251. package/dist/agents/skills/codex/harness-state-management/SKILL.md +309 -0
  252. package/dist/agents/skills/codex/harness-state-management/skill.yaml +33 -0
  253. package/dist/agents/skills/codex/harness-supply-chain-audit/SKILL.md +281 -0
  254. package/dist/agents/skills/codex/harness-supply-chain-audit/skill.yaml +51 -0
  255. package/dist/agents/skills/codex/harness-tdd/SKILL.md +177 -0
  256. package/dist/agents/skills/codex/harness-tdd/skill.yaml +49 -0
  257. package/dist/agents/skills/codex/harness-test-advisor/SKILL.md +160 -0
  258. package/dist/agents/skills/codex/harness-test-advisor/skill.yaml +45 -0
  259. package/dist/agents/skills/codex/harness-test-data/SKILL.md +268 -0
  260. package/dist/agents/skills/codex/harness-test-data/skill.yaml +74 -0
  261. package/dist/agents/skills/codex/harness-ux-copy/SKILL.md +271 -0
  262. package/dist/agents/skills/codex/harness-ux-copy/skill.yaml +77 -0
  263. package/dist/agents/skills/codex/harness-verification/SKILL.md +421 -0
  264. package/dist/agents/skills/codex/harness-verification/skill.yaml +43 -0
  265. package/dist/agents/skills/codex/harness-verify/SKILL.md +159 -0
  266. package/dist/agents/skills/codex/harness-verify/skill.yaml +41 -0
  267. package/dist/agents/skills/codex/harness-visual-regression/SKILL.md +257 -0
  268. package/dist/agents/skills/codex/harness-visual-regression/skill.yaml +74 -0
  269. package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +232 -0
  270. package/dist/agents/skills/codex/initialize-harness-project/skill.yaml +32 -0
  271. package/dist/agents/skills/codex/validate-context-engineering/SKILL.md +150 -0
  272. package/dist/agents/skills/codex/validate-context-engineering/skill.yaml +32 -0
  273. package/dist/agents/skills/cursor/add-harness-component/SKILL.md +192 -0
  274. package/dist/agents/skills/cursor/add-harness-component/skill.yaml +33 -0
  275. package/dist/agents/skills/cursor/align-documentation/SKILL.md +213 -0
  276. package/dist/agents/skills/cursor/align-documentation/skill.yaml +32 -0
  277. package/dist/agents/skills/cursor/check-mechanical-constraints/SKILL.md +191 -0
  278. package/dist/agents/skills/cursor/check-mechanical-constraints/skill.yaml +33 -0
  279. package/dist/agents/skills/cursor/cleanup-dead-code/SKILL.md +245 -0
  280. package/dist/agents/skills/cursor/cleanup-dead-code/skill.yaml +34 -0
  281. package/dist/agents/skills/cursor/detect-doc-drift/SKILL.md +179 -0
  282. package/dist/agents/skills/cursor/detect-doc-drift/skill.yaml +31 -0
  283. package/dist/agents/skills/cursor/enforce-architecture/SKILL.md +296 -0
  284. package/dist/agents/skills/cursor/enforce-architecture/skill.yaml +35 -0
  285. package/dist/agents/skills/cursor/harness-accessibility/SKILL.md +281 -0
  286. package/dist/agents/skills/cursor/harness-accessibility/skill.yaml +52 -0
  287. package/dist/agents/skills/cursor/harness-api-design/SKILL.md +356 -0
  288. package/dist/agents/skills/cursor/harness-api-design/skill.yaml +74 -0
  289. package/dist/agents/skills/cursor/harness-architecture-advisor/SKILL.md +449 -0
  290. package/dist/agents/skills/cursor/harness-architecture-advisor/skill.yaml +49 -0
  291. package/dist/agents/skills/cursor/harness-auth/SKILL.md +331 -0
  292. package/dist/agents/skills/cursor/harness-auth/skill.yaml +81 -0
  293. package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +916 -0
  294. package/dist/agents/skills/cursor/harness-autopilot/skill.yaml +67 -0
  295. package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +406 -0
  296. package/dist/agents/skills/cursor/harness-brainstorming/skill.yaml +50 -0
  297. package/dist/agents/skills/cursor/harness-caching/SKILL.md +309 -0
  298. package/dist/agents/skills/cursor/harness-caching/skill.yaml +73 -0
  299. package/dist/agents/skills/cursor/harness-chaos/SKILL.md +295 -0
  300. package/dist/agents/skills/cursor/harness-chaos/skill.yaml +72 -0
  301. package/dist/agents/skills/cursor/harness-code-review/SKILL.md +857 -0
  302. package/dist/agents/skills/cursor/harness-code-review/skill.yaml +52 -0
  303. package/dist/agents/skills/cursor/harness-codebase-cleanup/SKILL.md +224 -0
  304. package/dist/agents/skills/cursor/harness-codebase-cleanup/skill.yaml +65 -0
  305. package/dist/agents/skills/cursor/harness-compliance/SKILL.md +303 -0
  306. package/dist/agents/skills/cursor/harness-compliance/skill.yaml +78 -0
  307. package/dist/agents/skills/cursor/harness-containerization/SKILL.md +284 -0
  308. package/dist/agents/skills/cursor/harness-containerization/skill.yaml +80 -0
  309. package/dist/agents/skills/cursor/harness-data-pipeline/SKILL.md +274 -0
  310. package/dist/agents/skills/cursor/harness-data-pipeline/skill.yaml +81 -0
  311. package/dist/agents/skills/cursor/harness-data-validation/SKILL.md +343 -0
  312. package/dist/agents/skills/cursor/harness-data-validation/skill.yaml +75 -0
  313. package/dist/agents/skills/cursor/harness-database/SKILL.md +310 -0
  314. package/dist/agents/skills/cursor/harness-database/skill.yaml +80 -0
  315. package/dist/agents/skills/cursor/harness-debugging/SKILL.md +366 -0
  316. package/dist/agents/skills/cursor/harness-debugging/skill.yaml +48 -0
  317. package/dist/agents/skills/cursor/harness-dependency-health/SKILL.md +179 -0
  318. package/dist/agents/skills/cursor/harness-dependency-health/skill.yaml +42 -0
  319. package/dist/agents/skills/cursor/harness-deployment/SKILL.md +307 -0
  320. package/dist/agents/skills/cursor/harness-deployment/skill.yaml +77 -0
  321. package/dist/agents/skills/cursor/harness-design/SKILL.md +265 -0
  322. package/dist/agents/skills/cursor/harness-design/skill.yaml +54 -0
  323. package/dist/agents/skills/cursor/harness-design-mobile/SKILL.md +336 -0
  324. package/dist/agents/skills/cursor/harness-design-mobile/skill.yaml +50 -0
  325. package/dist/agents/skills/cursor/harness-design-system/SKILL.md +282 -0
  326. package/dist/agents/skills/cursor/harness-design-system/skill.yaml +51 -0
  327. package/dist/agents/skills/cursor/harness-design-web/SKILL.md +360 -0
  328. package/dist/agents/skills/cursor/harness-design-web/skill.yaml +53 -0
  329. package/dist/agents/skills/cursor/harness-diagnostics/SKILL.md +318 -0
  330. package/dist/agents/skills/cursor/harness-diagnostics/skill.yaml +51 -0
  331. package/dist/agents/skills/cursor/harness-docs-pipeline/SKILL.md +460 -0
  332. package/dist/agents/skills/cursor/harness-docs-pipeline/skill.yaml +70 -0
  333. package/dist/agents/skills/cursor/harness-dx/SKILL.md +276 -0
  334. package/dist/agents/skills/cursor/harness-dx/skill.yaml +76 -0
  335. package/dist/agents/skills/cursor/harness-e2e/SKILL.md +245 -0
  336. package/dist/agents/skills/cursor/harness-e2e/skill.yaml +78 -0
  337. package/dist/agents/skills/cursor/harness-event-driven/SKILL.md +280 -0
  338. package/dist/agents/skills/cursor/harness-event-driven/skill.yaml +77 -0
  339. package/dist/agents/skills/cursor/harness-execution/SKILL.md +510 -0
  340. package/dist/agents/skills/cursor/harness-execution/skill.yaml +52 -0
  341. package/dist/agents/skills/cursor/harness-feature-flags/SKILL.md +287 -0
  342. package/dist/agents/skills/cursor/harness-feature-flags/skill.yaml +74 -0
  343. package/dist/agents/skills/cursor/harness-git-workflow/SKILL.md +268 -0
  344. package/dist/agents/skills/cursor/harness-git-workflow/skill.yaml +32 -0
  345. package/dist/agents/skills/cursor/harness-hotspot-detector/SKILL.md +161 -0
  346. package/dist/agents/skills/cursor/harness-hotspot-detector/skill.yaml +45 -0
  347. package/dist/agents/skills/cursor/harness-i18n/SKILL.md +484 -0
  348. package/dist/agents/skills/cursor/harness-i18n/skill.yaml +55 -0
  349. package/dist/agents/skills/cursor/harness-i18n-process/SKILL.md +388 -0
  350. package/dist/agents/skills/cursor/harness-i18n-process/skill.yaml +44 -0
  351. package/dist/agents/skills/cursor/harness-i18n-workflow/SKILL.md +512 -0
  352. package/dist/agents/skills/cursor/harness-i18n-workflow/skill.yaml +54 -0
  353. package/dist/agents/skills/cursor/harness-impact-analysis/SKILL.md +184 -0
  354. package/dist/agents/skills/cursor/harness-impact-analysis/skill.yaml +45 -0
  355. package/dist/agents/skills/cursor/harness-incident-response/SKILL.md +223 -0
  356. package/dist/agents/skills/cursor/harness-incident-response/skill.yaml +78 -0
  357. package/dist/agents/skills/cursor/harness-infrastructure-as-code/SKILL.md +279 -0
  358. package/dist/agents/skills/cursor/harness-infrastructure-as-code/skill.yaml +80 -0
  359. package/dist/agents/skills/cursor/harness-integration-test/SKILL.md +271 -0
  360. package/dist/agents/skills/cursor/harness-integration-test/skill.yaml +73 -0
  361. package/dist/agents/skills/cursor/harness-integrity/SKILL.md +167 -0
  362. package/dist/agents/skills/cursor/harness-integrity/skill.yaml +48 -0
  363. package/dist/agents/skills/cursor/harness-knowledge-mapper/SKILL.md +195 -0
  364. package/dist/agents/skills/cursor/harness-knowledge-mapper/skill.yaml +50 -0
  365. package/dist/agents/skills/cursor/harness-load-testing/SKILL.md +274 -0
  366. package/dist/agents/skills/cursor/harness-load-testing/skill.yaml +79 -0
  367. package/dist/agents/skills/cursor/harness-ml-ops/SKILL.md +341 -0
  368. package/dist/agents/skills/cursor/harness-ml-ops/skill.yaml +79 -0
  369. package/dist/agents/skills/cursor/harness-mobile-patterns/SKILL.md +326 -0
  370. package/dist/agents/skills/cursor/harness-mobile-patterns/skill.yaml +82 -0
  371. package/dist/agents/skills/cursor/harness-mutation-test/SKILL.md +251 -0
  372. package/dist/agents/skills/cursor/harness-mutation-test/skill.yaml +70 -0
  373. package/dist/agents/skills/cursor/harness-observability/SKILL.md +283 -0
  374. package/dist/agents/skills/cursor/harness-observability/skill.yaml +78 -0
  375. package/dist/agents/skills/cursor/harness-onboarding/SKILL.md +288 -0
  376. package/dist/agents/skills/cursor/harness-onboarding/skill.yaml +31 -0
  377. package/dist/agents/skills/cursor/harness-parallel-agents/SKILL.md +256 -0
  378. package/dist/agents/skills/cursor/harness-parallel-agents/skill.yaml +34 -0
  379. package/dist/agents/skills/cursor/harness-perf/SKILL.md +260 -0
  380. package/dist/agents/skills/cursor/harness-perf/skill.yaml +51 -0
  381. package/dist/agents/skills/cursor/harness-perf-tdd/SKILL.md +249 -0
  382. package/dist/agents/skills/cursor/harness-perf-tdd/skill.yaml +48 -0
  383. package/dist/agents/skills/cursor/harness-planning/SKILL.md +579 -0
  384. package/dist/agents/skills/cursor/harness-planning/skill.yaml +56 -0
  385. package/dist/agents/skills/cursor/harness-pre-commit-review/SKILL.md +324 -0
  386. package/dist/agents/skills/cursor/harness-pre-commit-review/skill.yaml +34 -0
  387. package/dist/agents/skills/cursor/harness-product-spec/SKILL.md +285 -0
  388. package/dist/agents/skills/cursor/harness-product-spec/skill.yaml +72 -0
  389. package/dist/agents/skills/cursor/harness-property-test/SKILL.md +281 -0
  390. package/dist/agents/skills/cursor/harness-property-test/skill.yaml +71 -0
  391. package/dist/agents/skills/cursor/harness-refactoring/SKILL.md +169 -0
  392. package/dist/agents/skills/cursor/harness-refactoring/skill.yaml +34 -0
  393. package/dist/agents/skills/cursor/harness-release-readiness/SKILL.md +689 -0
  394. package/dist/agents/skills/cursor/harness-release-readiness/skill.yaml +58 -0
  395. package/dist/agents/skills/cursor/harness-resilience/SKILL.md +255 -0
  396. package/dist/agents/skills/cursor/harness-resilience/skill.yaml +76 -0
  397. package/dist/agents/skills/cursor/harness-roadmap/SKILL.md +595 -0
  398. package/dist/agents/skills/cursor/harness-roadmap/skill.yaml +44 -0
  399. package/dist/agents/skills/cursor/harness-secrets/SKILL.md +293 -0
  400. package/dist/agents/skills/cursor/harness-secrets/skill.yaml +76 -0
  401. package/dist/agents/skills/cursor/harness-security-review/SKILL.md +260 -0
  402. package/dist/agents/skills/cursor/harness-security-review/skill.yaml +53 -0
  403. package/dist/agents/skills/cursor/harness-security-scan/SKILL.md +154 -0
  404. package/dist/agents/skills/cursor/harness-security-scan/skill.yaml +42 -0
  405. package/dist/agents/skills/cursor/harness-skill-authoring/SKILL.md +292 -0
  406. package/dist/agents/skills/cursor/harness-skill-authoring/skill.yaml +33 -0
  407. package/dist/agents/skills/cursor/harness-soundness-review/SKILL.md +1267 -0
  408. package/dist/agents/skills/cursor/harness-soundness-review/skill.yaml +49 -0
  409. package/dist/agents/skills/cursor/harness-sql-review/SKILL.md +315 -0
  410. package/dist/agents/skills/cursor/harness-sql-review/skill.yaml +74 -0
  411. package/dist/agents/skills/cursor/harness-state-management/SKILL.md +309 -0
  412. package/dist/agents/skills/cursor/harness-state-management/skill.yaml +33 -0
  413. package/dist/agents/skills/cursor/harness-supply-chain-audit/SKILL.md +281 -0
  414. package/dist/agents/skills/cursor/harness-supply-chain-audit/skill.yaml +51 -0
  415. package/dist/agents/skills/cursor/harness-tdd/SKILL.md +177 -0
  416. package/dist/agents/skills/cursor/harness-tdd/skill.yaml +49 -0
  417. package/dist/agents/skills/cursor/harness-test-advisor/SKILL.md +160 -0
  418. package/dist/agents/skills/cursor/harness-test-advisor/skill.yaml +45 -0
  419. package/dist/agents/skills/cursor/harness-test-data/SKILL.md +268 -0
  420. package/dist/agents/skills/cursor/harness-test-data/skill.yaml +74 -0
  421. package/dist/agents/skills/cursor/harness-ux-copy/SKILL.md +271 -0
  422. package/dist/agents/skills/cursor/harness-ux-copy/skill.yaml +77 -0
  423. package/dist/agents/skills/cursor/harness-verification/SKILL.md +421 -0
  424. package/dist/agents/skills/cursor/harness-verification/skill.yaml +43 -0
  425. package/dist/agents/skills/cursor/harness-verify/SKILL.md +159 -0
  426. package/dist/agents/skills/cursor/harness-verify/skill.yaml +41 -0
  427. package/dist/agents/skills/cursor/harness-visual-regression/SKILL.md +257 -0
  428. package/dist/agents/skills/cursor/harness-visual-regression/skill.yaml +74 -0
  429. package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +232 -0
  430. package/dist/agents/skills/cursor/initialize-harness-project/skill.yaml +32 -0
  431. package/dist/agents/skills/cursor/validate-context-engineering/SKILL.md +150 -0
  432. package/dist/agents/skills/cursor/validate-context-engineering/skill.yaml +32 -0
  433. package/dist/agents/skills/gemini-cli/enforce-architecture/SKILL.md +52 -0
  434. package/dist/agents/skills/gemini-cli/harness-api-design/SKILL.md +52 -0
  435. package/dist/agents/skills/gemini-cli/harness-architecture-advisor/SKILL.md +52 -0
  436. package/dist/agents/skills/gemini-cli/harness-auth/SKILL.md +52 -0
  437. package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +123 -14
  438. package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +6 -0
  439. package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +97 -3
  440. package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +6 -0
  441. package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/SKILL.md +2 -4
  442. package/dist/agents/skills/gemini-cli/harness-database/SKILL.md +52 -0
  443. package/dist/agents/skills/gemini-cli/harness-deployment/SKILL.md +52 -0
  444. package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +99 -3
  445. package/dist/agents/skills/gemini-cli/harness-planning/skill.yaml +6 -0
  446. package/dist/agents/skills/gemini-cli/harness-pre-commit-review/SKILL.md +1 -1
  447. package/dist/agents/skills/gemini-cli/harness-security-review/SKILL.md +27 -7
  448. package/dist/agents/skills/gemini-cli/harness-security-scan/SKILL.md +52 -0
  449. package/dist/agents/skills/gemini-cli/harness-supply-chain-audit/SKILL.md +281 -0
  450. package/dist/agents/skills/gemini-cli/harness-supply-chain-audit/skill.yaml +51 -0
  451. package/dist/agents/skills/templates/discipline-template.md +49 -0
  452. package/dist/agents/skills/tests/schema.ts +1 -1
  453. package/dist/{agents-md-ZGNIDWAF.js → agents-md-VYDFPIRW.js} +1 -1
  454. package/dist/{architecture-ZLIH5533.js → architecture-K5HSRBGB.js} +2 -2
  455. package/dist/bin/harness-mcp.js +13 -13
  456. package/dist/bin/harness.js +18 -18
  457. package/dist/{check-phase-gate-ZOXVBDCN.js → check-phase-gate-5AS6SXL6.js} +3 -3
  458. package/dist/{chunk-LGYBN7Y6.js → chunk-5ZXHMCPL.js} +1 -1
  459. package/dist/{chunk-RCWZBSK5.js → chunk-6KWBH4EO.js} +1 -1
  460. package/dist/{chunk-Z2OOPXJO.js → chunk-ALFKNAZW.js} +1172 -74
  461. package/dist/{chunk-VEPAJXBW.js → chunk-AV6KMDO5.js} +2 -2
  462. package/dist/{chunk-2BKLWLY6.js → chunk-C7DTKLPW.js} +4 -4
  463. package/dist/{chunk-ZOAWBDWU.js → chunk-CJDVBBPB.js} +5 -1
  464. package/dist/{chunk-EDXIVMAP.js → chunk-DNDBFIZN.js} +18 -4
  465. package/dist/{chunk-XYLGHKG6.js → chunk-HKUX2X7O.js} +11 -2
  466. package/dist/{chunk-NNHDDXYT.js → chunk-JOP2NDNB.js} +396 -114
  467. package/dist/{chunk-YBJ262QL.js → chunk-LRG3B43J.js} +1 -1
  468. package/dist/{chunk-AOZRDOIP.js → chunk-M6TIO6NF.js} +1 -1
  469. package/dist/{chunk-B2HKP423.js → chunk-OCDDCGDE.js} +9 -1
  470. package/dist/{chunk-3ZZKVN62.js → chunk-QDF7COPQ.js} +1 -1
  471. package/dist/{chunk-YLXFKVJE.js → chunk-RWZPHW4H.js} +3 -3
  472. package/dist/{chunk-N25INEIX.js → chunk-SFRGPAK6.js} +1 -1
  473. package/dist/{chunk-J4RAX7YB.js → chunk-SHYWICGA.js} +1677 -501
  474. package/dist/{chunk-OFXQSFOW.js → chunk-TF6ZLHJV.js} +2 -2
  475. package/dist/{chunk-ND2ENWDM.js → chunk-ZJMU7MEV.js} +1 -1
  476. package/dist/{ci-workflow-765LSHRD.js → ci-workflow-CRWU723U.js} +1 -1
  477. package/dist/{create-skill-XSWHMSM5.js → create-skill-NDXQSTIK.js} +2 -2
  478. package/dist/{dist-ALQDD67R.js → dist-4LPXJYVZ.js} +59 -1
  479. package/dist/{docs-NRMQCOJ6.js → docs-4JRHTLUZ.js} +3 -3
  480. package/dist/{engine-3RB7MXPP.js → engine-3G3VIM6L.js} +1 -1
  481. package/dist/{entropy-6AGX2ZUN.js → entropy-G6CZ2A6P.js} +2 -2
  482. package/dist/{feedback-MY4QZIFD.js → feedback-QYKQ65HB.js} +1 -1
  483. package/dist/{generate-agent-definitions-ZAE726AU.js → generate-agent-definitions-SAAOAPT4.js} +3 -3
  484. package/dist/index.d.ts +33 -12
  485. package/dist/index.js +18 -18
  486. package/dist/{loader-UUTVMQCC.js → loader-VCOK3PF7.js} +1 -1
  487. package/dist/{mcp-VU5FMO52.js → mcp-YENEPHBW.js} +13 -13
  488. package/dist/{performance-2D7G6NMJ.js → performance-UBCFI2UP.js} +4 -2
  489. package/dist/{review-pipeline-RAQ55ISU.js → review-pipeline-IQAVCWAX.js} +1 -1
  490. package/dist/{runtime-BCK5RRZQ.js → runtime-PYFFIESU.js} +1 -1
  491. package/dist/{security-2RPQEN62.js → security-ZDADTPYW.js} +1 -1
  492. package/dist/{skill-executor-XZLYZYAK.js → skill-executor-XEVDGXUM.js} +2 -2
  493. package/dist/{validate-KBYQAEWE.js → validate-VRTUHALQ.js} +2 -2
  494. package/dist/{validate-cross-check-OABMREW4.js → validate-cross-check-4Y6NHNK3.js} +1 -1
  495. package/package.json +6 -5
@@ -6,11 +6,11 @@ import {
6
6
  OutputMode,
7
7
  createCheckPhaseGateCommand,
8
8
  findFiles
9
- } from "./chunk-LGYBN7Y6.js";
9
+ } from "./chunk-5ZXHMCPL.js";
10
10
  import {
11
11
  createGenerateAgentDefinitionsCommand,
12
12
  generateAgentDefinitions
13
- } from "./chunk-RCWZBSK5.js";
13
+ } from "./chunk-6KWBH4EO.js";
14
14
  import {
15
15
  listPersonas,
16
16
  loadPersona
@@ -20,13 +20,13 @@ import {
20
20
  } from "./chunk-TRAPF4IX.js";
21
21
  import {
22
22
  executeSkill
23
- } from "./chunk-AOZRDOIP.js";
23
+ } from "./chunk-M6TIO6NF.js";
24
24
  import {
25
25
  ALLOWED_PERSONA_COMMANDS
26
26
  } from "./chunk-TEFCFC4H.js";
27
27
  import {
28
28
  createCreateSkillCommand
29
- } from "./chunk-YBJ262QL.js";
29
+ } from "./chunk-LRG3B43J.js";
30
30
  import {
31
31
  logger
32
32
  } from "./chunk-EBJQ6N4M.js";
@@ -50,14 +50,14 @@ import {
50
50
  handleGetImpact,
51
51
  handleOrphanDeletion,
52
52
  persistToolingConfig
53
- } from "./chunk-NNHDDXYT.js";
53
+ } from "./chunk-JOP2NDNB.js";
54
54
  import {
55
55
  VALID_PLATFORMS
56
- } from "./chunk-ZOAWBDWU.js";
56
+ } from "./chunk-CJDVBBPB.js";
57
57
  import {
58
58
  findConfigFile,
59
59
  resolveConfig
60
- } from "./chunk-B2HKP423.js";
60
+ } from "./chunk-OCDDCGDE.js";
61
61
  import {
62
62
  resolveGlobalSkillsDir,
63
63
  resolvePersonasDir,
@@ -72,7 +72,7 @@ import {
72
72
  } from "./chunk-3WGJMBKH.js";
73
73
  import {
74
74
  SkillMetadataSchema
75
- } from "./chunk-XYLGHKG6.js";
75
+ } from "./chunk-HKUX2X7O.js";
76
76
  import {
77
77
  CLI_VERSION
78
78
  } from "./chunk-BM3PWGXQ.js";
@@ -96,6 +96,10 @@ import {
96
96
  archiveStream,
97
97
  buildSnapshot,
98
98
  checkDocCoverage,
99
+ checkTaint,
100
+ clearTaint,
101
+ computeOverallSeverity,
102
+ computeScanExitCode,
99
103
  createFixes,
100
104
  createStream,
101
105
  deepMergeConstraints,
@@ -107,8 +111,11 @@ import {
107
111
  extractBundle,
108
112
  generateSuggestions,
109
113
  listStreams,
114
+ listTaintedSessions,
110
115
  loadState,
111
116
  loadStreamIndex,
117
+ mapInjectionFindings,
118
+ mapSecurityFindings,
112
119
  parseDiff,
113
120
  parseManifest,
114
121
  parseSecurityConfig,
@@ -121,20 +128,21 @@ import {
121
128
  runAll,
122
129
  runCIChecks,
123
130
  runReviewPipeline,
131
+ scanForInjection,
124
132
  setActiveStream,
125
133
  validateAgentsMap,
126
134
  validateDependencies,
127
135
  validateKnowledgeMap,
128
136
  writeConfig,
129
137
  writeLockfile
130
- } from "./chunk-Z2OOPXJO.js";
138
+ } from "./chunk-ALFKNAZW.js";
131
139
  import {
132
140
  Err,
133
141
  Ok
134
142
  } from "./chunk-ERS5EVUZ.js";
135
143
 
136
144
  // src/index.ts
137
- import { Command as Command61 } from "commander";
145
+ import { Command as Command70 } from "commander";
138
146
 
139
147
  // src/commands/validate.ts
140
148
  import { Command } from "commander";
@@ -213,7 +221,7 @@ function createValidateCommand() {
213
221
  process.exit(result.error.exitCode);
214
222
  }
215
223
  if (opts.crossCheck) {
216
- const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-OABMREW4.js");
224
+ const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-4Y6NHNK3.js");
217
225
  const cwd = process.cwd();
218
226
  const specsDir = path.join(cwd, "docs", "specs");
219
227
  const plansDir = path.join(cwd, "docs", "plans");
@@ -481,10 +489,10 @@ async function runCheckSecurity(cwd, options) {
481
489
  const projectRoot = path4.resolve(cwd);
482
490
  let configData = {};
483
491
  try {
484
- const fs29 = await import("fs");
492
+ const fs34 = await import("fs");
485
493
  const configPath = path4.join(projectRoot, "harness.config.json");
486
- if (fs29.existsSync(configPath)) {
487
- const raw = fs29.readFileSync(configPath, "utf-8");
494
+ if (fs34.existsSync(configPath)) {
495
+ const raw = fs34.readFileSync(configPath, "utf-8");
488
496
  const parsed = JSON.parse(raw);
489
497
  configData = parsed.security ?? {};
490
498
  }
@@ -570,7 +578,7 @@ function registerBenchCommand(perf) {
570
578
  perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob, _opts, cmd) => {
571
579
  const globalOpts = cmd.optsWithGlobals();
572
580
  const cwd = process.cwd();
573
- const { BenchmarkRunner } = await import("./dist-ALQDD67R.js");
581
+ const { BenchmarkRunner } = await import("./dist-4LPXJYVZ.js");
574
582
  const runner = new BenchmarkRunner();
575
583
  const benchFiles = runner.discover(cwd, glob);
576
584
  if (benchFiles.length === 0) {
@@ -638,7 +646,7 @@ function registerBaselinesCommands(perf) {
638
646
  baselines.command("update").description("Update baselines from latest benchmark run").action(async (_opts, cmd) => {
639
647
  const globalOpts = cmd.optsWithGlobals();
640
648
  const cwd = process.cwd();
641
- const { BenchmarkRunner } = await import("./dist-ALQDD67R.js");
649
+ const { BenchmarkRunner } = await import("./dist-4LPXJYVZ.js");
642
650
  const runner = new BenchmarkRunner();
643
651
  const manager = new BaselineManager(cwd);
644
652
  logger.info("Running benchmarks to update baselines...");
@@ -671,7 +679,7 @@ function registerReportCommand(perf) {
671
679
  perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
672
680
  const globalOpts = cmd.optsWithGlobals();
673
681
  const cwd = process.cwd();
674
- const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-ALQDD67R.js");
682
+ const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-4LPXJYVZ.js");
675
683
  const analyzer = new EntropyAnalyzer2({
676
684
  rootDir: path5.resolve(cwd),
677
685
  analyze: { complexity: true, coupling: true }
@@ -852,33 +860,248 @@ function createCheckDocsCommand() {
852
860
  // src/commands/init.ts
853
861
  import { Command as Command8 } from "commander";
854
862
  import chalk2 from "chalk";
855
- import * as fs2 from "fs";
856
- import * as path8 from "path";
863
+ import * as fs4 from "fs";
864
+ import * as path10 from "path";
857
865
 
858
866
  // src/commands/setup-mcp.ts
859
867
  import { Command as Command7 } from "commander";
860
- import * as fs from "fs";
861
- import * as path7 from "path";
868
+ import * as fs3 from "fs";
869
+ import * as path9 from "path";
862
870
  import * as os from "os";
863
871
  import chalk from "chalk";
864
- var HARNESS_MCP_ENTRY = {
865
- command: "harness-mcp"
866
- };
867
- function readJsonFile(filePath) {
872
+ import * as clack from "@clack/prompts";
873
+
874
+ // src/integrations/config.ts
875
+ import * as fs from "fs";
876
+ import * as path7 from "path";
877
+ function readJsonSafe(filePath) {
868
878
  if (!fs.existsSync(filePath)) return null;
869
879
  try {
870
880
  return JSON.parse(fs.readFileSync(filePath, "utf-8"));
871
881
  } catch {
872
- fs.copyFileSync(filePath, filePath + ".bak");
873
882
  return null;
874
883
  }
875
884
  }
876
- function writeJsonFile(filePath, data) {
885
+ function writeJson(filePath, data) {
877
886
  const dir = path7.dirname(filePath);
878
887
  if (!fs.existsSync(dir)) {
879
888
  fs.mkdirSync(dir, { recursive: true });
880
889
  }
881
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
890
+ const tmp = filePath + ".tmp";
891
+ fs.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n");
892
+ fs.renameSync(tmp, filePath);
893
+ }
894
+ function readMcpConfig(filePath) {
895
+ const config = readJsonSafe(filePath);
896
+ if (!config) return { mcpServers: {} };
897
+ if (!config.mcpServers) config.mcpServers = {};
898
+ return config;
899
+ }
900
+ function writeMcpEntry(filePath, name, entry) {
901
+ const config = readMcpConfig(filePath);
902
+ config.mcpServers[name] = entry;
903
+ writeJson(filePath, config);
904
+ }
905
+ function removeMcpEntry(filePath, name) {
906
+ if (!fs.existsSync(filePath)) return;
907
+ const config = readMcpConfig(filePath);
908
+ delete config.mcpServers[name];
909
+ writeJson(filePath, config);
910
+ }
911
+ function readIntegrationsConfig(configPath) {
912
+ const raw = readJsonSafe(configPath);
913
+ if (!raw || !raw.integrations) return { enabled: [], dismissed: [] };
914
+ const integ = raw.integrations;
915
+ return {
916
+ enabled: Array.isArray(integ.enabled) ? integ.enabled : [],
917
+ dismissed: Array.isArray(integ.dismissed) ? integ.dismissed : []
918
+ };
919
+ }
920
+ function writeIntegrationsConfig(configPath, integrations) {
921
+ const raw = readJsonSafe(configPath) ?? {};
922
+ raw.integrations = integrations;
923
+ writeJson(configPath, raw);
924
+ }
925
+
926
+ // src/integrations/toml.ts
927
+ import * as fs2 from "fs";
928
+ import * as path8 from "path";
929
+ function writeTomlMcpEntry(filePath, name, entry) {
930
+ const dir = path8.dirname(filePath);
931
+ if (!fs2.existsSync(dir)) {
932
+ fs2.mkdirSync(dir, { recursive: true });
933
+ }
934
+ const existing = fs2.existsSync(filePath) ? fs2.readFileSync(filePath, "utf-8") : "";
935
+ const blockHeader = `[mcp_servers.${name}]`;
936
+ const newBlock = serializeTomlMcpBlock(name, entry);
937
+ let updated;
938
+ if (existing.includes(blockHeader)) {
939
+ updated = replaceTomlBlock(existing, blockHeader, newBlock);
940
+ } else {
941
+ const separator = existing.length > 0 && !existing.endsWith("\n\n") ? "\n" : "";
942
+ updated = existing + separator + newBlock;
943
+ }
944
+ const tmp = filePath + ".tmp";
945
+ fs2.writeFileSync(tmp, updated);
946
+ fs2.renameSync(tmp, filePath);
947
+ }
948
+ function serializeTomlMcpBlock(name, entry) {
949
+ const lines = [`[mcp_servers.${name}]`];
950
+ lines.push(`command = ${JSON.stringify(entry.command)}`);
951
+ if (entry.args !== void 0) {
952
+ const argsLiteral = "[" + entry.args.map((a) => JSON.stringify(a)).join(", ") + "]";
953
+ lines.push(`args = ${argsLiteral}`);
954
+ }
955
+ if (entry.enabled !== void 0) {
956
+ lines.push(`enabled = ${entry.enabled}`);
957
+ }
958
+ return lines.join("\n") + "\n";
959
+ }
960
+ function replaceTomlBlock(content, blockHeader, newBlock) {
961
+ const lines = content.split("\n");
962
+ const startIdx = lines.findIndex((l) => l.trim() === blockHeader);
963
+ if (startIdx === -1) return content + newBlock;
964
+ let endIdx = lines.length;
965
+ for (let i = startIdx + 1; i < lines.length; i++) {
966
+ if (lines[i]?.match(/^\[(?!\[)/)) {
967
+ endIdx = i;
968
+ break;
969
+ }
970
+ }
971
+ while (endIdx > startIdx + 1 && lines[endIdx - 1]?.trim() === "") {
972
+ endIdx--;
973
+ }
974
+ const newBlockLines = newBlock.trimEnd().split("\n");
975
+ const result = [
976
+ ...lines.slice(0, startIdx),
977
+ ...newBlockLines,
978
+ ...endIdx < lines.length ? ["", ...lines.slice(endIdx)] : []
979
+ ];
980
+ return result.join("\n") + (content.endsWith("\n") ? "\n" : "");
981
+ }
982
+
983
+ // src/commands/setup-mcp.ts
984
+ var HARNESS_MCP_ENTRY = {
985
+ command: "harness-mcp"
986
+ };
987
+ var CURSOR_CURATED_TOOLS = [
988
+ "run_skill",
989
+ "validate_project",
990
+ "emit_interaction",
991
+ "check_docs",
992
+ "manage_roadmap",
993
+ "run_code_review",
994
+ "check_phase_gate",
995
+ "gather_context",
996
+ "find_context_for",
997
+ "get_impact",
998
+ "detect_entropy",
999
+ "run_security_scan",
1000
+ "assess_project",
1001
+ "manage_state",
1002
+ "create_self_review",
1003
+ "analyze_diff",
1004
+ "request_peer_review",
1005
+ "review_changes",
1006
+ "check_dependencies",
1007
+ "search_skills",
1008
+ "code_search",
1009
+ "code_outline",
1010
+ "ask_graph",
1011
+ "query_graph",
1012
+ "detect_anomalies"
1013
+ ];
1014
+ var ALL_MCP_TOOLS = [
1015
+ "validate_project",
1016
+ "check_dependencies",
1017
+ "check_docs",
1018
+ "detect_entropy",
1019
+ "generate_linter",
1020
+ "validate_linter_config",
1021
+ "init_project",
1022
+ "list_personas",
1023
+ "generate_persona_artifacts",
1024
+ "run_persona",
1025
+ "add_component",
1026
+ "run_agent_task",
1027
+ "run_skill",
1028
+ "manage_state",
1029
+ "create_self_review",
1030
+ "analyze_diff",
1031
+ "request_peer_review",
1032
+ "check_phase_gate",
1033
+ "validate_cross_check",
1034
+ "create_skill",
1035
+ "generate_slash_commands",
1036
+ "query_graph",
1037
+ "search_similar",
1038
+ "find_context_for",
1039
+ "get_relationships",
1040
+ "get_impact",
1041
+ "ingest_source",
1042
+ "generate_agent_definitions",
1043
+ "run_security_scan",
1044
+ "check_performance",
1045
+ "get_perf_baselines",
1046
+ "update_perf_baselines",
1047
+ "get_critical_paths",
1048
+ "list_streams",
1049
+ "manage_roadmap",
1050
+ "emit_interaction",
1051
+ "run_code_review",
1052
+ "gather_context",
1053
+ "assess_project",
1054
+ "review_changes",
1055
+ "detect_anomalies",
1056
+ "ask_graph",
1057
+ "check_task_independence",
1058
+ "predict_conflicts",
1059
+ "detect_stale_constraints",
1060
+ "search_skills",
1061
+ "code_outline",
1062
+ "code_search",
1063
+ "code_unfold"
1064
+ ];
1065
+ async function runCursorToolPicker() {
1066
+ try {
1067
+ const selected = await clack.multiselect({
1068
+ message: "Select tools to register for Cursor (25 recommended; Cursor supports ~40 across all servers)",
1069
+ options: ALL_MCP_TOOLS.map((tool) => {
1070
+ const opt = { value: tool, label: tool };
1071
+ if (CURSOR_CURATED_TOOLS.includes(tool)) opt.hint = "recommended";
1072
+ return opt;
1073
+ }),
1074
+ initialValues: CURSOR_CURATED_TOOLS
1075
+ });
1076
+ if (clack.isCancel(selected)) {
1077
+ return CURSOR_CURATED_TOOLS;
1078
+ }
1079
+ return selected;
1080
+ } catch {
1081
+ return CURSOR_CURATED_TOOLS;
1082
+ }
1083
+ }
1084
+ function writeCursorMcpEntryWithTools(configPath, tools) {
1085
+ writeMcpEntry(configPath, "harness", {
1086
+ command: "harness",
1087
+ args: ["mcp", "--tools", ...tools]
1088
+ });
1089
+ }
1090
+ function readJsonFile(filePath) {
1091
+ if (!fs3.existsSync(filePath)) return null;
1092
+ try {
1093
+ return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
1094
+ } catch {
1095
+ fs3.copyFileSync(filePath, filePath + ".bak");
1096
+ return null;
1097
+ }
1098
+ }
1099
+ function writeJsonFile(filePath, data) {
1100
+ const dir = path9.dirname(filePath);
1101
+ if (!fs3.existsSync(dir)) {
1102
+ fs3.mkdirSync(dir, { recursive: true });
1103
+ }
1104
+ fs3.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
882
1105
  }
883
1106
  function configureMcpServer(configPath) {
884
1107
  const config = readJsonFile(configPath) ?? {};
@@ -893,7 +1116,7 @@ function configureMcpServer(configPath) {
893
1116
  return true;
894
1117
  }
895
1118
  function addGeminiTrustedFolder(cwd) {
896
- const trustedPath = path7.join(os.homedir(), ".gemini", "trustedFolders.json");
1119
+ const trustedPath = path9.join(os.homedir(), ".gemini", "trustedFolders.json");
897
1120
  const folders = readJsonFile(trustedPath) ?? {};
898
1121
  if (folders[cwd] === "TRUST_FOLDER") {
899
1122
  return false;
@@ -907,7 +1130,7 @@ function setupMcp(cwd, client) {
907
1130
  const skipped = [];
908
1131
  let trustedFolder = false;
909
1132
  if (client === "all" || client === "claude") {
910
- const configPath = path7.join(cwd, ".mcp.json");
1133
+ const configPath = path9.join(cwd, ".mcp.json");
911
1134
  if (configureMcpServer(configPath)) {
912
1135
  configured.push("Claude Code");
913
1136
  } else {
@@ -915,7 +1138,7 @@ function setupMcp(cwd, client) {
915
1138
  }
916
1139
  }
917
1140
  if (client === "all" || client === "gemini") {
918
- const configPath = path7.join(cwd, ".gemini", "settings.json");
1141
+ const configPath = path9.join(cwd, ".gemini", "settings.json");
919
1142
  if (configureMcpServer(configPath)) {
920
1143
  configured.push("Gemini CLI");
921
1144
  } else {
@@ -923,44 +1146,96 @@ function setupMcp(cwd, client) {
923
1146
  }
924
1147
  trustedFolder = addGeminiTrustedFolder(cwd);
925
1148
  }
1149
+ if (client === "all" || client === "codex") {
1150
+ const configPath = path9.join(cwd, ".codex", "config.toml");
1151
+ const alreadyConfigured = (() => {
1152
+ if (!fs3.existsSync(configPath)) return false;
1153
+ const content = fs3.readFileSync(configPath, "utf-8");
1154
+ return content.includes("[mcp_servers.harness]");
1155
+ })();
1156
+ if (alreadyConfigured) {
1157
+ skipped.push("Codex CLI");
1158
+ } else {
1159
+ writeTomlMcpEntry(configPath, "harness", {
1160
+ command: "harness",
1161
+ args: ["mcp"],
1162
+ enabled: true
1163
+ });
1164
+ configured.push("Codex CLI");
1165
+ }
1166
+ }
1167
+ if (client === "all" || client === "cursor") {
1168
+ const configPath = path9.join(cwd, ".cursor", "mcp.json");
1169
+ const existing = readJsonFile(configPath);
1170
+ if (existing?.mcpServers?.["harness"]) {
1171
+ skipped.push("Cursor");
1172
+ } else {
1173
+ writeMcpEntry(configPath, "harness", { command: "harness", args: ["mcp"] });
1174
+ configured.push("Cursor");
1175
+ }
1176
+ }
926
1177
  return { configured, skipped, trustedFolder };
927
1178
  }
1179
+ async function resolveCursorWithPicker(cwd, pick) {
1180
+ const configured = [];
1181
+ const skipped = [];
1182
+ const cursorConfigPath = path9.join(cwd, ".cursor", "mcp.json");
1183
+ const existing = readJsonFile(cursorConfigPath);
1184
+ if (existing?.mcpServers?.["harness"] && !pick) {
1185
+ skipped.push("Cursor");
1186
+ } else {
1187
+ const tools = pick ? await runCursorToolPicker() : CURSOR_CURATED_TOOLS;
1188
+ writeCursorMcpEntryWithTools(cursorConfigPath, tools);
1189
+ configured.push("Cursor");
1190
+ }
1191
+ return { configured, skipped };
1192
+ }
1193
+ function printMcpResult(configured, skipped, trustedFolder) {
1194
+ console.log("");
1195
+ if (configured.length > 0) {
1196
+ logger.success("MCP server configured!");
1197
+ console.log("");
1198
+ for (const name of configured) {
1199
+ console.log(` ${chalk.green("+")} ${name}`);
1200
+ }
1201
+ }
1202
+ if (trustedFolder) {
1203
+ console.log("");
1204
+ logger.info("Added project to Gemini trusted folders (~/.gemini/trustedFolders.json)");
1205
+ }
1206
+ if (skipped.length > 0) {
1207
+ console.log("");
1208
+ logger.info("Already configured:");
1209
+ for (const name of skipped) {
1210
+ console.log(` ${chalk.dim("-")} ${name}`);
1211
+ }
1212
+ }
1213
+ console.log("");
1214
+ console.log(chalk.bold("The harness MCP server provides:"));
1215
+ console.log(
1216
+ ` - ${ALL_MCP_TOOLS.length} tools for validation, entropy detection, skill execution, graph querying, and more`
1217
+ );
1218
+ console.log(
1219
+ " - 8 resources for project context, skills, rules, learnings, state, and graph data"
1220
+ );
1221
+ console.log("");
1222
+ console.log(`Run ${chalk.cyan("harness skill list")} to see available skills.`);
1223
+ console.log("");
1224
+ }
928
1225
  function createSetupMcpCommand() {
929
- return new Command7("setup-mcp").description("Configure MCP server for AI agent integration").option("--client <client>", "Client to configure (claude, gemini, all)", "all").action(async (opts, cmd) => {
1226
+ return new Command7("setup-mcp").description("Configure MCP server for AI agent integration").option("--client <client>", "Client to configure (claude, gemini, codex, cursor, all)", "all").option("--pick", "Launch interactive tool picker (Cursor only)").option("--yes", "Bypass interactive picker and use curated 25-tool set (Cursor only)").action(async (opts, cmd) => {
930
1227
  const globalOpts = cmd.optsWithGlobals();
931
1228
  const cwd = process.cwd();
932
- const { configured, skipped, trustedFolder } = setupMcp(cwd, opts.client);
1229
+ let configured;
1230
+ let skipped;
1231
+ let trustedFolder = false;
1232
+ if (opts.client === "cursor" && (opts.pick || opts.yes)) {
1233
+ ({ configured, skipped } = await resolveCursorWithPicker(cwd, opts.pick));
1234
+ } else {
1235
+ ({ configured, skipped, trustedFolder } = setupMcp(cwd, opts.client));
1236
+ }
933
1237
  if (!globalOpts.quiet) {
934
- console.log("");
935
- if (configured.length > 0) {
936
- logger.success("MCP server configured!");
937
- console.log("");
938
- for (const name of configured) {
939
- console.log(` ${chalk.green("+")} ${name}`);
940
- }
941
- }
942
- if (trustedFolder) {
943
- console.log("");
944
- logger.info("Added project to Gemini trusted folders (~/.gemini/trustedFolders.json)");
945
- }
946
- if (skipped.length > 0) {
947
- console.log("");
948
- logger.info("Already configured:");
949
- for (const name of skipped) {
950
- console.log(` ${chalk.dim("-")} ${name}`);
951
- }
952
- }
953
- console.log("");
954
- console.log(chalk.bold("The harness MCP server provides:"));
955
- console.log(
956
- " - 31 tools for validation, entropy detection, skill execution, graph querying, and more"
957
- );
958
- console.log(
959
- " - 8 resources for project context, skills, rules, learnings, state, and graph data"
960
- );
961
- console.log("");
962
- console.log(`Run ${chalk.cyan("harness skill list")} to see available skills.`);
963
- console.log("");
1238
+ printMcpResult(configured, skipped, trustedFolder);
964
1239
  }
965
1240
  process.exit(ExitCode.SUCCESS);
966
1241
  });
@@ -969,10 +1244,10 @@ function createSetupMcpCommand() {
969
1244
  // src/commands/init.ts
970
1245
  async function runInit(options) {
971
1246
  const cwd = options.cwd ?? process.cwd();
972
- const name = options.name ?? path8.basename(cwd);
1247
+ const name = options.name ?? path10.basename(cwd);
973
1248
  const force = options.force ?? false;
974
- const configPath = path8.join(cwd, "harness.config.json");
975
- if (!force && fs2.existsSync(configPath)) {
1249
+ const configPath = path10.join(cwd, "harness.config.json");
1250
+ if (!force && fs4.existsSync(configPath)) {
976
1251
  return Err(
977
1252
  new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
978
1253
  );
@@ -1090,7 +1365,7 @@ function createInitCommand() {
1090
1365
 
1091
1366
  // src/commands/cleanup.ts
1092
1367
  import { Command as Command9 } from "commander";
1093
- import * as path9 from "path";
1368
+ import * as path11 from "path";
1094
1369
  async function runCleanup(options) {
1095
1370
  const cwd = options.cwd ?? process.cwd();
1096
1371
  const type = options.type ?? "all";
@@ -1105,11 +1380,11 @@ async function runCleanup(options) {
1105
1380
  patternViolations: [],
1106
1381
  totalIssues: 0
1107
1382
  };
1108
- const rootDir = path9.resolve(cwd, config.rootDir);
1109
- const docsDir = path9.resolve(cwd, config.docsDir);
1383
+ const rootDir = path11.resolve(cwd, config.rootDir);
1384
+ const docsDir = path11.resolve(cwd, config.docsDir);
1110
1385
  const entropyConfig = {
1111
1386
  rootDir,
1112
- entryPoints: [path9.join(rootDir, "src/index.ts")],
1387
+ entryPoints: [path11.join(rootDir, "src/index.ts")],
1113
1388
  docPaths: [docsDir],
1114
1389
  analyze: {
1115
1390
  drift: type === "all" || type === "drift",
@@ -1209,7 +1484,7 @@ function createCleanupCommand() {
1209
1484
 
1210
1485
  // src/commands/fix-drift.ts
1211
1486
  import { Command as Command10 } from "commander";
1212
- import * as path10 from "path";
1487
+ import * as path12 from "path";
1213
1488
  async function runFixDrift(options) {
1214
1489
  const cwd = options.cwd ?? process.cwd();
1215
1490
  const dryRun = options.dryRun !== false;
@@ -1218,11 +1493,11 @@ async function runFixDrift(options) {
1218
1493
  return Err(configResult.error);
1219
1494
  }
1220
1495
  const config = configResult.value;
1221
- const rootDir = path10.resolve(cwd, config.rootDir);
1222
- const docsDir = path10.resolve(cwd, config.docsDir);
1496
+ const rootDir = path12.resolve(cwd, config.rootDir);
1497
+ const docsDir = path12.resolve(cwd, config.docsDir);
1223
1498
  const entropyConfig = {
1224
1499
  rootDir,
1225
- entryPoints: [path10.join(rootDir, "src/index.ts")],
1500
+ entryPoints: [path12.join(rootDir, "src/index.ts")],
1226
1501
  docPaths: [docsDir],
1227
1502
  analyze: {
1228
1503
  drift: true,
@@ -1375,7 +1650,7 @@ import { Command as Command13 } from "commander";
1375
1650
 
1376
1651
  // src/commands/agent/run.ts
1377
1652
  import { Command as Command11 } from "commander";
1378
- import * as path11 from "path";
1653
+ import * as path13 from "path";
1379
1654
  import * as childProcess from "child_process";
1380
1655
  async function runAgentTask(task, options) {
1381
1656
  const configResult = resolveConfig(options.configPath);
@@ -1447,7 +1722,7 @@ function createCommandExecutor() {
1447
1722
  }
1448
1723
  async function runPersonaMode(opts, quiet) {
1449
1724
  const personasDir = resolvePersonasDir();
1450
- const filePath = path11.join(personasDir, `${opts.persona}.yaml`);
1725
+ const filePath = path13.join(personasDir, `${opts.persona}.yaml`);
1451
1726
  const personaResult = loadPersona(filePath);
1452
1727
  if (!personaResult.ok) {
1453
1728
  logger.error(personaResult.error.message);
@@ -1619,8 +1894,8 @@ function createAgentCommand() {
1619
1894
 
1620
1895
  // src/commands/add.ts
1621
1896
  import { Command as Command14 } from "commander";
1622
- import * as fs3 from "fs";
1623
- import * as path12 from "path";
1897
+ import * as fs5 from "fs";
1898
+ import * as path14 from "path";
1624
1899
  var LAYER_INDEX_TEMPLATE = (name) => `// ${name} layer
1625
1900
  // Add your ${name} exports here
1626
1901
 
@@ -1664,62 +1939,62 @@ async function runAdd(componentType, name, options) {
1664
1939
  try {
1665
1940
  switch (componentType) {
1666
1941
  case "layer": {
1667
- const layerDir = path12.join(cwd, "src", name);
1668
- if (!fs3.existsSync(layerDir)) {
1669
- fs3.mkdirSync(layerDir, { recursive: true });
1942
+ const layerDir = path14.join(cwd, "src", name);
1943
+ if (!fs5.existsSync(layerDir)) {
1944
+ fs5.mkdirSync(layerDir, { recursive: true });
1670
1945
  created.push(`src/${name}/`);
1671
1946
  }
1672
- const indexPath = path12.join(layerDir, "index.ts");
1673
- if (!fs3.existsSync(indexPath)) {
1674
- fs3.writeFileSync(indexPath, LAYER_INDEX_TEMPLATE(name));
1947
+ const indexPath = path14.join(layerDir, "index.ts");
1948
+ if (!fs5.existsSync(indexPath)) {
1949
+ fs5.writeFileSync(indexPath, LAYER_INDEX_TEMPLATE(name));
1675
1950
  created.push(`src/${name}/index.ts`);
1676
1951
  }
1677
1952
  break;
1678
1953
  }
1679
1954
  case "module": {
1680
- const modulePath = path12.join(cwd, "src", `${name}.ts`);
1681
- if (fs3.existsSync(modulePath)) {
1955
+ const modulePath = path14.join(cwd, "src", `${name}.ts`);
1956
+ if (fs5.existsSync(modulePath)) {
1682
1957
  return Err(new CLIError(`Module ${name} already exists`, ExitCode.ERROR));
1683
1958
  }
1684
- fs3.writeFileSync(modulePath, MODULE_TEMPLATE(name));
1959
+ fs5.writeFileSync(modulePath, MODULE_TEMPLATE(name));
1685
1960
  created.push(`src/${name}.ts`);
1686
1961
  break;
1687
1962
  }
1688
1963
  case "doc": {
1689
1964
  const configDocsDir = configResult.ok ? configResult.value.docsDir : "./docs";
1690
- const docsDir = path12.resolve(cwd, configDocsDir);
1691
- if (!fs3.existsSync(docsDir)) {
1692
- fs3.mkdirSync(docsDir, { recursive: true });
1965
+ const docsDir = path14.resolve(cwd, configDocsDir);
1966
+ if (!fs5.existsSync(docsDir)) {
1967
+ fs5.mkdirSync(docsDir, { recursive: true });
1693
1968
  }
1694
- const docPath = path12.join(docsDir, `${name}.md`);
1695
- if (fs3.existsSync(docPath)) {
1969
+ const docPath = path14.join(docsDir, `${name}.md`);
1970
+ if (fs5.existsSync(docPath)) {
1696
1971
  return Err(new CLIError(`Doc ${name} already exists`, ExitCode.ERROR));
1697
1972
  }
1698
- fs3.writeFileSync(docPath, DOC_TEMPLATE(name));
1973
+ fs5.writeFileSync(docPath, DOC_TEMPLATE(name));
1699
1974
  created.push(`${configDocsDir.replace(/^\.[\\/]/, "")}/${name}.md`);
1700
1975
  break;
1701
1976
  }
1702
1977
  case "skill": {
1703
- const { generateSkillFiles: generateSkillFiles2 } = await import("./create-skill-XSWHMSM5.js");
1978
+ const { generateSkillFiles: generateSkillFiles2 } = await import("./create-skill-NDXQSTIK.js");
1704
1979
  generateSkillFiles2({
1705
1980
  name,
1706
1981
  description: `${name} skill`,
1707
- outputDir: path12.join(cwd, "agents", "skills", "claude-code")
1982
+ outputDir: path14.join(cwd, "agents", "skills", "claude-code")
1708
1983
  });
1709
1984
  created.push(`agents/skills/claude-code/${name}/skill.yaml`);
1710
1985
  created.push(`agents/skills/claude-code/${name}/SKILL.md`);
1711
1986
  break;
1712
1987
  }
1713
1988
  case "persona": {
1714
- const personasDir = path12.join(cwd, "agents", "personas");
1715
- if (!fs3.existsSync(personasDir)) {
1716
- fs3.mkdirSync(personasDir, { recursive: true });
1989
+ const personasDir = path14.join(cwd, "agents", "personas");
1990
+ if (!fs5.existsSync(personasDir)) {
1991
+ fs5.mkdirSync(personasDir, { recursive: true });
1717
1992
  }
1718
- const personaPath = path12.join(personasDir, `${name}.yaml`);
1719
- if (fs3.existsSync(personaPath)) {
1993
+ const personaPath = path14.join(personasDir, `${name}.yaml`);
1994
+ if (fs5.existsSync(personaPath)) {
1720
1995
  return Err(new CLIError(`Persona ${name} already exists`, ExitCode.ERROR));
1721
1996
  }
1722
- fs3.writeFileSync(
1997
+ fs5.writeFileSync(
1723
1998
  personaPath,
1724
1999
  `name: ${name}
1725
2000
  description: ${name} persona
@@ -1905,46 +2180,46 @@ function createListCommand() {
1905
2180
 
1906
2181
  // src/commands/persona/generate.ts
1907
2182
  import { Command as Command19 } from "commander";
1908
- import * as fs4 from "fs";
1909
- import * as path13 from "path";
2183
+ import * as fs6 from "fs";
2184
+ import * as path15 from "path";
1910
2185
  function createGenerateCommand2() {
1911
2186
  return new Command19("generate").description("Generate artifacts from a persona config").argument("<name>", "Persona name (e.g., architecture-enforcer)").option("--output-dir <dir>", "Output directory", ".").option("--only <type>", "Generate only: ci, agents-md, runtime").action(async (name, opts, cmd) => {
1912
2187
  const globalOpts = cmd.optsWithGlobals();
1913
2188
  const personasDir = resolvePersonasDir();
1914
- const filePath = path13.join(personasDir, `${name}.yaml`);
2189
+ const filePath = path15.join(personasDir, `${name}.yaml`);
1915
2190
  const personaResult = loadPersona(filePath);
1916
2191
  if (!personaResult.ok) {
1917
2192
  logger.error(personaResult.error.message);
1918
2193
  process.exit(ExitCode.ERROR);
1919
2194
  }
1920
2195
  const persona = personaResult.value;
1921
- const outputDir = path13.resolve(opts.outputDir);
2196
+ const outputDir = path15.resolve(opts.outputDir);
1922
2197
  const slug = toKebabCase(persona.name);
1923
2198
  const only = opts.only;
1924
2199
  const generated = [];
1925
2200
  if (!only || only === "runtime") {
1926
2201
  const result = generateRuntime(persona);
1927
2202
  if (result.ok) {
1928
- const outPath = path13.join(outputDir, `${slug}.runtime.json`);
1929
- fs4.mkdirSync(path13.dirname(outPath), { recursive: true });
1930
- fs4.writeFileSync(outPath, result.value);
2203
+ const outPath = path15.join(outputDir, `${slug}.runtime.json`);
2204
+ fs6.mkdirSync(path15.dirname(outPath), { recursive: true });
2205
+ fs6.writeFileSync(outPath, result.value);
1931
2206
  generated.push(outPath);
1932
2207
  }
1933
2208
  }
1934
2209
  if (!only || only === "agents-md") {
1935
2210
  const result = generateAgentsMd(persona);
1936
2211
  if (result.ok) {
1937
- const outPath = path13.join(outputDir, `${slug}.agents.md`);
1938
- fs4.writeFileSync(outPath, result.value);
2212
+ const outPath = path15.join(outputDir, `${slug}.agents.md`);
2213
+ fs6.writeFileSync(outPath, result.value);
1939
2214
  generated.push(outPath);
1940
2215
  }
1941
2216
  }
1942
2217
  if (!only || only === "ci") {
1943
2218
  const result = generateCIWorkflow(persona, "github");
1944
2219
  if (result.ok) {
1945
- const outPath = path13.join(outputDir, ".github", "workflows", `${slug}.yml`);
1946
- fs4.mkdirSync(path13.dirname(outPath), { recursive: true });
1947
- fs4.writeFileSync(outPath, result.value);
2220
+ const outPath = path15.join(outputDir, ".github", "workflows", `${slug}.yml`);
2221
+ fs6.mkdirSync(path15.dirname(outPath), { recursive: true });
2222
+ fs6.writeFileSync(outPath, result.value);
1948
2223
  generated.push(outPath);
1949
2224
  }
1950
2225
  }
@@ -1969,13 +2244,13 @@ import { Command as Command28 } from "commander";
1969
2244
 
1970
2245
  // src/commands/skill/list.ts
1971
2246
  import { Command as Command21 } from "commander";
1972
- import * as fs6 from "fs";
1973
- import * as path15 from "path";
2247
+ import * as fs8 from "fs";
2248
+ import * as path17 from "path";
1974
2249
  import { parse } from "yaml";
1975
2250
 
1976
2251
  // src/registry/lockfile.ts
1977
- import * as fs5 from "fs";
1978
- import * as path14 from "path";
2252
+ import * as fs7 from "fs";
2253
+ import * as path16 from "path";
1979
2254
  function createEmptyLockfile() {
1980
2255
  return { version: 1, skills: {} };
1981
2256
  }
@@ -1995,10 +2270,10 @@ function sortedStringify(obj) {
1995
2270
  );
1996
2271
  }
1997
2272
  function readLockfile2(filePath) {
1998
- if (!fs5.existsSync(filePath)) {
2273
+ if (!fs7.existsSync(filePath)) {
1999
2274
  return createEmptyLockfile();
2000
2275
  }
2001
- const raw = fs5.readFileSync(filePath, "utf-8");
2276
+ const raw = fs7.readFileSync(filePath, "utf-8");
2002
2277
  let parsed;
2003
2278
  try {
2004
2279
  parsed = JSON.parse(raw);
@@ -2015,9 +2290,9 @@ function readLockfile2(filePath) {
2015
2290
  return parsed;
2016
2291
  }
2017
2292
  function writeLockfile2(filePath, lockfile) {
2018
- const dir = path14.dirname(filePath);
2019
- fs5.mkdirSync(dir, { recursive: true });
2020
- fs5.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
2293
+ const dir = path16.dirname(filePath);
2294
+ fs7.mkdirSync(dir, { recursive: true });
2295
+ fs7.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
2021
2296
  }
2022
2297
  function updateLockfileEntry(lockfile, name, entry) {
2023
2298
  return {
@@ -2041,14 +2316,14 @@ function removeLockfileEntry(lockfile, name) {
2041
2316
 
2042
2317
  // src/commands/skill/list.ts
2043
2318
  function scanDirectory(dirPath, source) {
2044
- if (!fs6.existsSync(dirPath)) return [];
2045
- const entries = fs6.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2319
+ if (!fs8.existsSync(dirPath)) return [];
2320
+ const entries = fs8.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2046
2321
  const skills = [];
2047
2322
  for (const name of entries) {
2048
- const yamlPath = path15.join(dirPath, name, "skill.yaml");
2049
- if (!fs6.existsSync(yamlPath)) continue;
2323
+ const yamlPath = path17.join(dirPath, name, "skill.yaml");
2324
+ if (!fs8.existsSync(yamlPath)) continue;
2050
2325
  try {
2051
- const raw = fs6.readFileSync(yamlPath, "utf-8");
2326
+ const raw = fs8.readFileSync(yamlPath, "utf-8");
2052
2327
  const parsed = parse(raw);
2053
2328
  const result = SkillMetadataSchema.safeParse(parsed);
2054
2329
  if (result.success) {
@@ -2066,10 +2341,10 @@ function scanDirectory(dirPath, source) {
2066
2341
  }
2067
2342
  function collectCommunitySkills(seen, allSkills) {
2068
2343
  const globalDir = resolveGlobalSkillsDir();
2069
- const skillsDir = path15.dirname(globalDir);
2070
- const communityBase = path15.join(skillsDir, "community");
2071
- const communityPlatformDir = path15.join(communityBase, "claude-code");
2072
- const lockfilePath = path15.join(communityBase, "skills-lock.json");
2344
+ const skillsDir = path17.dirname(globalDir);
2345
+ const communityBase = path17.join(skillsDir, "community");
2346
+ const communityPlatformDir = path17.join(communityBase, "claude-code");
2347
+ const lockfilePath = path17.join(communityBase, "skills-lock.json");
2073
2348
  const lockfile = readLockfile2(lockfilePath);
2074
2349
  const communitySkills = scanDirectory(communityPlatformDir, "community");
2075
2350
  for (const skill of communitySkills) {
@@ -2155,20 +2430,20 @@ function createListCommand2() {
2155
2430
 
2156
2431
  // src/commands/skill/run.ts
2157
2432
  import { Command as Command22 } from "commander";
2158
- import * as fs7 from "fs";
2159
- import * as path16 from "path";
2433
+ import * as fs9 from "fs";
2434
+ import * as path18 from "path";
2160
2435
  import { parse as parse2 } from "yaml";
2161
2436
 
2162
2437
  // src/skill/complexity.ts
2163
2438
  import { execFileSync as execFileSync2 } from "child_process";
2164
2439
  function evaluateSignals(signals) {
2165
- if (signals.fileCount >= 3) return "full";
2166
- if (signals.newDir) return "full";
2167
- if (signals.newDep) return "full";
2168
- if (signals.fileCount <= 1) return "light";
2169
- if (signals.testOnly) return "light";
2170
- if (signals.docsOnly) return "light";
2171
- return "full";
2440
+ if (signals.fileCount >= 3) return "thorough";
2441
+ if (signals.newDir) return "thorough";
2442
+ if (signals.newDep) return "thorough";
2443
+ if (signals.fileCount <= 1) return "fast";
2444
+ if (signals.testOnly) return "fast";
2445
+ if (signals.docsOnly) return "fast";
2446
+ return "thorough";
2172
2447
  }
2173
2448
  function detectComplexity(projectPath) {
2174
2449
  try {
@@ -2201,7 +2476,7 @@ function detectComplexity(projectPath) {
2201
2476
  };
2202
2477
  return evaluateSignals(signals);
2203
2478
  } catch {
2204
- return "full";
2479
+ return "thorough";
2205
2480
  }
2206
2481
  }
2207
2482
 
@@ -2211,8 +2486,8 @@ function buildPreamble(options) {
2211
2486
  if (options.complexity && options.phases && options.phases.length > 0) {
2212
2487
  const lines = [`## Active Phases (complexity: ${options.complexity})`];
2213
2488
  for (const phase of options.phases) {
2214
- if (options.complexity === "light" && !phase.required) {
2215
- lines.push(`- ~~${phase.name.toUpperCase()}~~ (skipped in light mode)`);
2489
+ if (options.complexity === "fast" && !phase.required) {
2490
+ lines.push(`- ~~${phase.name.toUpperCase()}~~ (skipped in fast mode)`);
2216
2491
  } else {
2217
2492
  lines.push(`- ${phase.name.toUpperCase()} (${phase.required ? "required" : "optional"})`);
2218
2493
  }
@@ -2244,10 +2519,10 @@ ${options.priorState}`);
2244
2519
 
2245
2520
  // src/commands/skill/run.ts
2246
2521
  function loadSkillMetadata(skillDir) {
2247
- const yamlPath = path16.join(skillDir, "skill.yaml");
2248
- if (!fs7.existsSync(yamlPath)) return null;
2522
+ const yamlPath = path18.join(skillDir, "skill.yaml");
2523
+ if (!fs9.existsSync(yamlPath)) return null;
2249
2524
  try {
2250
- const result = SkillMetadataSchema.safeParse(parse2(fs7.readFileSync(yamlPath, "utf-8")));
2525
+ const result = SkillMetadataSchema.safeParse(parse2(fs9.readFileSync(yamlPath, "utf-8")));
2251
2526
  return result.success ? result.data : null;
2252
2527
  } catch {
2253
2528
  return null;
@@ -2255,26 +2530,26 @@ function loadSkillMetadata(skillDir) {
2255
2530
  }
2256
2531
  function resolveComplexity(metadata, requested, projectPath) {
2257
2532
  if (!metadata?.phases || metadata.phases.length === 0) return void 0;
2258
- if (requested === "auto") return detectComplexity(projectPath);
2533
+ if (requested === "standard") return detectComplexity(projectPath);
2259
2534
  return requested;
2260
2535
  }
2261
2536
  function loadPrinciples(projectPath) {
2262
- const principlesPath = path16.join(projectPath, "docs", "principles.md");
2263
- return fs7.existsSync(principlesPath) ? fs7.readFileSync(principlesPath, "utf-8") : void 0;
2537
+ const principlesPath = path18.join(projectPath, "docs", "principles.md");
2538
+ return fs9.existsSync(principlesPath) ? fs9.readFileSync(principlesPath, "utf-8") : void 0;
2264
2539
  }
2265
2540
  function readMostRecentFileInDir(dirPath) {
2266
- const files = fs7.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs7.statSync(path16.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
2267
- if (files.length > 0) return fs7.readFileSync(path16.join(dirPath, files[0].name), "utf-8");
2541
+ const files = fs9.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs9.statSync(path18.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
2542
+ if (files.length > 0) return fs9.readFileSync(path18.join(dirPath, files[0].name), "utf-8");
2268
2543
  return void 0;
2269
2544
  }
2270
2545
  function loadPriorState(metadata, projectPath) {
2271
2546
  if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
2272
2547
  for (const stateFilePath of metadata.state.files) {
2273
- const fullPath = path16.join(projectPath, stateFilePath);
2274
- if (!fs7.existsSync(fullPath)) continue;
2275
- const stat = fs7.statSync(fullPath);
2548
+ const fullPath = path18.join(projectPath, stateFilePath);
2549
+ if (!fs9.existsSync(fullPath)) continue;
2550
+ const stat = fs9.statSync(fullPath);
2276
2551
  if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
2277
- return fs7.readFileSync(fullPath, "utf-8");
2552
+ return fs9.readFileSync(fullPath, "utf-8");
2278
2553
  }
2279
2554
  return void 0;
2280
2555
  }
@@ -2294,9 +2569,9 @@ function resolvePhaseState(metadata, projectPath, phase) {
2294
2569
  }
2295
2570
  function appendProjectState(content, metadata, projectPath, hasPathOpt) {
2296
2571
  if (!metadata?.state.persistent || !hasPathOpt) return content;
2297
- const stateFile = path16.join(projectPath, ".harness", "state.json");
2298
- if (!fs7.existsSync(stateFile)) return content;
2299
- const stateContent = fs7.readFileSync(stateFile, "utf-8");
2572
+ const stateFile = path18.join(projectPath, ".harness", "state.json");
2573
+ if (!fs9.existsSync(stateFile)) return content;
2574
+ const stateContent = fs9.readFileSync(stateFile, "utf-8");
2300
2575
  return content + `
2301
2576
 
2302
2577
  ---
@@ -2307,19 +2582,19 @@ ${stateContent}
2307
2582
  `;
2308
2583
  }
2309
2584
  function createRunCommand2() {
2310
- return new Command22("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Complexity: auto, light, full", "auto").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").action(async (name, opts, _cmd) => {
2585
+ return new Command22("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Rigor level: fast, standard, thorough", "standard").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").action(async (name, opts, _cmd) => {
2311
2586
  const skillsDir = resolveSkillsDir();
2312
- const skillDir = path16.join(skillsDir, name);
2313
- if (!fs7.existsSync(skillDir)) {
2587
+ const skillDir = path18.join(skillsDir, name);
2588
+ if (!fs9.existsSync(skillDir)) {
2314
2589
  logger.error(`Skill not found: ${name}`);
2315
2590
  process.exit(ExitCode.ERROR);
2316
2591
  return;
2317
2592
  }
2318
2593
  const metadata = loadSkillMetadata(skillDir);
2319
- const projectPath = opts.path ? path16.resolve(opts.path) : process.cwd();
2594
+ const projectPath = opts.path ? path18.resolve(opts.path) : process.cwd();
2320
2595
  const complexity = resolveComplexity(
2321
2596
  metadata,
2322
- opts.complexity ?? "auto",
2597
+ opts.complexity ?? "standard",
2323
2598
  projectPath
2324
2599
  );
2325
2600
  const principles = loadPrinciples(projectPath);
@@ -2343,14 +2618,14 @@ function createRunCommand2() {
2343
2618
  ...stateWarning !== void 0 && { stateWarning },
2344
2619
  party: opts.party
2345
2620
  });
2346
- const skillMdPath = path16.join(skillDir, "SKILL.md");
2347
- if (!fs7.existsSync(skillMdPath)) {
2621
+ const skillMdPath = path18.join(skillDir, "SKILL.md");
2622
+ if (!fs9.existsSync(skillMdPath)) {
2348
2623
  logger.error(`SKILL.md not found for skill: ${name}`);
2349
2624
  process.exit(ExitCode.ERROR);
2350
2625
  return;
2351
2626
  }
2352
2627
  const content = appendProjectState(
2353
- fs7.readFileSync(skillMdPath, "utf-8"),
2628
+ fs9.readFileSync(skillMdPath, "utf-8"),
2354
2629
  metadata,
2355
2630
  projectPath,
2356
2631
  !!opts.path
@@ -2362,8 +2637,8 @@ function createRunCommand2() {
2362
2637
 
2363
2638
  // src/commands/skill/validate.ts
2364
2639
  import { Command as Command23 } from "commander";
2365
- import * as fs8 from "fs";
2366
- import * as path17 from "path";
2640
+ import * as fs10 from "fs";
2641
+ import * as path19 from "path";
2367
2642
  import { parse as parse3 } from "yaml";
2368
2643
  var REQUIRED_SECTIONS = [
2369
2644
  "## When to Use",
@@ -2373,11 +2648,11 @@ var REQUIRED_SECTIONS = [
2373
2648
  "## Examples"
2374
2649
  ];
2375
2650
  function validateSkillMd(name, skillMdPath, skillType, errors) {
2376
- if (!fs8.existsSync(skillMdPath)) {
2651
+ if (!fs10.existsSync(skillMdPath)) {
2377
2652
  errors.push(`${name}: missing SKILL.md`);
2378
2653
  return;
2379
2654
  }
2380
- const mdContent = fs8.readFileSync(skillMdPath, "utf-8");
2655
+ const mdContent = fs10.readFileSync(skillMdPath, "utf-8");
2381
2656
  for (const section of REQUIRED_SECTIONS) {
2382
2657
  if (!mdContent.includes(section)) {
2383
2658
  errors.push(`${name}/SKILL.md: missing section "${section}"`);
@@ -2394,20 +2669,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
2394
2669
  }
2395
2670
  }
2396
2671
  function validateSkillEntry(name, skillsDir, errors) {
2397
- const skillDir = path17.join(skillsDir, name);
2398
- const yamlPath = path17.join(skillDir, "skill.yaml");
2399
- if (!fs8.existsSync(yamlPath)) {
2672
+ const skillDir = path19.join(skillsDir, name);
2673
+ const yamlPath = path19.join(skillDir, "skill.yaml");
2674
+ if (!fs10.existsSync(yamlPath)) {
2400
2675
  errors.push(`${name}: missing skill.yaml`);
2401
2676
  return false;
2402
2677
  }
2403
2678
  try {
2404
- const raw = fs8.readFileSync(yamlPath, "utf-8");
2679
+ const raw = fs10.readFileSync(yamlPath, "utf-8");
2405
2680
  const result = SkillMetadataSchema.safeParse(parse3(raw));
2406
2681
  if (!result.success) {
2407
2682
  errors.push(`${name}/skill.yaml: ${result.error.message}`);
2408
2683
  return false;
2409
2684
  }
2410
- validateSkillMd(name, path17.join(skillDir, "SKILL.md"), result.data.type, errors);
2685
+ validateSkillMd(name, path19.join(skillDir, "SKILL.md"), result.data.type, errors);
2411
2686
  return true;
2412
2687
  } catch (e) {
2413
2688
  errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
@@ -2418,12 +2693,12 @@ function createValidateCommand3() {
2418
2693
  return new Command23("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
2419
2694
  const globalOpts = cmd.optsWithGlobals();
2420
2695
  const skillsDir = resolveSkillsDir();
2421
- if (!fs8.existsSync(skillsDir)) {
2696
+ if (!fs10.existsSync(skillsDir)) {
2422
2697
  logger.info("No skills directory found.");
2423
2698
  process.exit(ExitCode.SUCCESS);
2424
2699
  return;
2425
2700
  }
2426
- const entries = fs8.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2701
+ const entries = fs10.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2427
2702
  const errors = [];
2428
2703
  let validated = 0;
2429
2704
  for (const name of entries) {
@@ -2446,27 +2721,27 @@ function createValidateCommand3() {
2446
2721
 
2447
2722
  // src/commands/skill/info.ts
2448
2723
  import { Command as Command24 } from "commander";
2449
- import * as fs9 from "fs";
2450
- import * as path18 from "path";
2724
+ import * as fs11 from "fs";
2725
+ import * as path20 from "path";
2451
2726
  import { parse as parse4 } from "yaml";
2452
2727
  function createInfoCommand() {
2453
2728
  return new Command24("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
2454
2729
  const globalOpts = cmd.optsWithGlobals();
2455
2730
  const skillsDir = resolveSkillsDir();
2456
- const skillDir = path18.join(skillsDir, name);
2457
- if (!fs9.existsSync(skillDir)) {
2731
+ const skillDir = path20.join(skillsDir, name);
2732
+ if (!fs11.existsSync(skillDir)) {
2458
2733
  logger.error(`Skill not found: ${name}`);
2459
2734
  process.exit(ExitCode.ERROR);
2460
2735
  return;
2461
2736
  }
2462
- const yamlPath = path18.join(skillDir, "skill.yaml");
2463
- if (!fs9.existsSync(yamlPath)) {
2737
+ const yamlPath = path20.join(skillDir, "skill.yaml");
2738
+ if (!fs11.existsSync(yamlPath)) {
2464
2739
  logger.error(`skill.yaml not found for skill: ${name}`);
2465
2740
  process.exit(ExitCode.ERROR);
2466
2741
  return;
2467
2742
  }
2468
2743
  try {
2469
- const raw = fs9.readFileSync(yamlPath, "utf-8");
2744
+ const raw = fs11.readFileSync(yamlPath, "utf-8");
2470
2745
  const parsed = parse4(raw);
2471
2746
  const result = SkillMetadataSchema.safeParse(parsed);
2472
2747
  if (!result.success) {
@@ -2509,8 +2784,8 @@ function createInfoCommand() {
2509
2784
  import { Command as Command25 } from "commander";
2510
2785
 
2511
2786
  // src/registry/npm-client.ts
2512
- import * as fs10 from "fs";
2513
- import * as path19 from "path";
2787
+ import * as fs12 from "fs";
2788
+ import * as path21 from "path";
2514
2789
  import * as os2 from "os";
2515
2790
  var NPM_REGISTRY = "https://registry.npmjs.org";
2516
2791
  var FETCH_TIMEOUT_MS = 3e4;
@@ -2533,10 +2808,10 @@ function extractSkillName(packageName) {
2533
2808
  function readNpmrcToken(registryUrl) {
2534
2809
  const { hostname, pathname } = new URL(registryUrl);
2535
2810
  const registryPath = `//${hostname}${pathname.replace(/\/$/, "")}/:_authToken=`;
2536
- const candidates = [path19.join(process.cwd(), ".npmrc"), path19.join(os2.homedir(), ".npmrc")];
2811
+ const candidates = [path21.join(process.cwd(), ".npmrc"), path21.join(os2.homedir(), ".npmrc")];
2537
2812
  for (const npmrcPath of candidates) {
2538
2813
  try {
2539
- const content = fs10.readFileSync(npmrcPath, "utf-8");
2814
+ const content = fs12.readFileSync(npmrcPath, "utf-8");
2540
2815
  for (const line of content.split("\n")) {
2541
2816
  const trimmed = line.trim();
2542
2817
  if (trimmed.startsWith(registryPath)) {
@@ -2681,8 +2956,8 @@ Found ${results.length} skill(s):
2681
2956
 
2682
2957
  // src/commands/skill/create.ts
2683
2958
  import { Command as Command26 } from "commander";
2684
- import * as path20 from "path";
2685
- import * as fs11 from "fs";
2959
+ import * as path22 from "path";
2960
+ import * as fs13 from "fs";
2686
2961
  import YAML from "yaml";
2687
2962
  var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
2688
2963
  function buildReadme(name, description) {
@@ -2766,22 +3041,22 @@ function runCreate(name, opts) {
2766
3041
  if (!KEBAB_CASE_RE.test(name)) {
2767
3042
  throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
2768
3043
  }
2769
- const baseDir = opts.outputDir ?? path20.join(process.cwd(), "agents", "skills", "claude-code");
2770
- const skillDir = path20.join(baseDir, name);
2771
- if (fs11.existsSync(skillDir)) {
3044
+ const baseDir = opts.outputDir ?? path22.join(process.cwd(), "agents", "skills", "claude-code");
3045
+ const skillDir = path22.join(baseDir, name);
3046
+ if (fs13.existsSync(skillDir)) {
2772
3047
  throw new Error(`Skill directory already exists: ${skillDir}`);
2773
3048
  }
2774
- fs11.mkdirSync(skillDir, { recursive: true });
3049
+ fs13.mkdirSync(skillDir, { recursive: true });
2775
3050
  const description = opts.description || `A community skill: ${name}`;
2776
3051
  const skillYaml = buildSkillYaml(name, opts);
2777
- const skillYamlPath = path20.join(skillDir, "skill.yaml");
2778
- fs11.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
3052
+ const skillYamlPath = path22.join(skillDir, "skill.yaml");
3053
+ fs13.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
2779
3054
  const skillMd = buildSkillMd(name, description);
2780
- const skillMdPath = path20.join(skillDir, "SKILL.md");
2781
- fs11.writeFileSync(skillMdPath, skillMd);
3055
+ const skillMdPath = path22.join(skillDir, "SKILL.md");
3056
+ fs13.writeFileSync(skillMdPath, skillMd);
2782
3057
  const readme = buildReadme(name, description);
2783
- const readmePath = path20.join(skillDir, "README.md");
2784
- fs11.writeFileSync(readmePath, readme);
3058
+ const readmePath = path22.join(skillDir, "README.md");
3059
+ fs13.writeFileSync(readmePath, readme);
2785
3060
  return {
2786
3061
  name,
2787
3062
  directory: skillDir,
@@ -2809,7 +3084,7 @@ function createCreateCommand() {
2809
3084
  logger.info(`
2810
3085
  Next steps:`);
2811
3086
  logger.info(
2812
- ` 1. Edit ${path20.join(result.directory, "SKILL.md")} with your skill content`
3087
+ ` 1. Edit ${path22.join(result.directory, "SKILL.md")} with your skill content`
2813
3088
  );
2814
3089
  logger.info(` 2. Run: harness skill validate ${name}`);
2815
3090
  logger.info(` 3. Run: harness skills publish`);
@@ -2823,28 +3098,28 @@ Next steps:`);
2823
3098
 
2824
3099
  // src/commands/skill/publish.ts
2825
3100
  import { Command as Command27 } from "commander";
2826
- import * as fs14 from "fs";
2827
- import * as path23 from "path";
3101
+ import * as fs16 from "fs";
3102
+ import * as path25 from "path";
2828
3103
  import { execFileSync as execFileSync3 } from "child_process";
2829
3104
 
2830
3105
  // src/registry/validator.ts
2831
- import * as fs13 from "fs";
2832
- import * as path22 from "path";
3106
+ import * as fs15 from "fs";
3107
+ import * as path24 from "path";
2833
3108
  import { parse as parse5 } from "yaml";
2834
3109
  import semver from "semver";
2835
3110
 
2836
3111
  // src/registry/bundled-skills.ts
2837
- import * as fs12 from "fs";
2838
- import * as path21 from "path";
3112
+ import * as fs14 from "fs";
3113
+ import * as path23 from "path";
2839
3114
  function getBundledSkillNames(bundledSkillsDir) {
2840
- if (!fs12.existsSync(bundledSkillsDir)) {
3115
+ if (!fs14.existsSync(bundledSkillsDir)) {
2841
3116
  return /* @__PURE__ */ new Set();
2842
3117
  }
2843
- const entries = fs12.readdirSync(bundledSkillsDir);
3118
+ const entries = fs14.readdirSync(bundledSkillsDir);
2844
3119
  const names = /* @__PURE__ */ new Set();
2845
3120
  for (const entry of entries) {
2846
3121
  try {
2847
- const stat = fs12.statSync(path21.join(bundledSkillsDir, String(entry)));
3122
+ const stat = fs14.statSync(path23.join(bundledSkillsDir, String(entry)));
2848
3123
  if (stat.isDirectory()) {
2849
3124
  names.add(String(entry));
2850
3125
  }
@@ -2857,14 +3132,14 @@ function getBundledSkillNames(bundledSkillsDir) {
2857
3132
  // src/registry/validator.ts
2858
3133
  async function validateForPublish(skillDir, registryUrl) {
2859
3134
  const errors = [];
2860
- const skillYamlPath = path22.join(skillDir, "skill.yaml");
2861
- if (!fs13.existsSync(skillYamlPath)) {
3135
+ const skillYamlPath = path24.join(skillDir, "skill.yaml");
3136
+ if (!fs15.existsSync(skillYamlPath)) {
2862
3137
  errors.push("skill.yaml not found. Create one with: harness skill create <name>");
2863
3138
  return { valid: false, errors };
2864
3139
  }
2865
3140
  let skillMeta;
2866
3141
  try {
2867
- const raw = fs13.readFileSync(skillYamlPath, "utf-8");
3142
+ const raw = fs15.readFileSync(skillYamlPath, "utf-8");
2868
3143
  const parsed = parse5(raw);
2869
3144
  const result = SkillMetadataSchema.safeParse(parsed);
2870
3145
  if (!result.success) {
@@ -2886,11 +3161,11 @@ async function validateForPublish(skillDir, registryUrl) {
2886
3161
  if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
2887
3162
  errors.push("At least one trigger is required. Add triggers to skill.yaml.");
2888
3163
  }
2889
- const skillMdPath = path22.join(skillDir, "SKILL.md");
2890
- if (!fs13.existsSync(skillMdPath)) {
3164
+ const skillMdPath = path24.join(skillDir, "SKILL.md");
3165
+ if (!fs15.existsSync(skillMdPath)) {
2891
3166
  errors.push("SKILL.md not found. Create it with content describing your skill.");
2892
3167
  } else {
2893
- const content = fs13.readFileSync(skillMdPath, "utf-8");
3168
+ const content = fs15.readFileSync(skillMdPath, "utf-8");
2894
3169
  if (!content.includes("## When to Use")) {
2895
3170
  errors.push('SKILL.md must contain a "## When to Use" section.');
2896
3171
  }
@@ -2970,11 +3245,11 @@ ${errorList}`);
2970
3245
  }
2971
3246
  const meta = validation.skillMeta;
2972
3247
  const pkg = derivePackageJson(meta);
2973
- const pkgPath = path23.join(skillDir, "package.json");
2974
- fs14.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
2975
- const readmePath = path23.join(skillDir, "README.md");
2976
- if (!fs14.existsSync(readmePath)) {
2977
- const skillMdContent = fs14.readFileSync(path23.join(skillDir, "SKILL.md"), "utf-8");
3248
+ const pkgPath = path25.join(skillDir, "package.json");
3249
+ fs16.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
3250
+ const readmePath = path25.join(skillDir, "README.md");
3251
+ if (!fs16.existsSync(readmePath)) {
3252
+ const skillMdContent = fs16.readFileSync(path25.join(skillDir, "SKILL.md"), "utf-8");
2978
3253
  const readme = `# ${pkg.name}
2979
3254
 
2980
3255
  ${meta.description}
@@ -2988,7 +3263,7 @@ harness install ${meta.name}
2988
3263
  ---
2989
3264
 
2990
3265
  ${skillMdContent}`;
2991
- fs14.writeFileSync(readmePath, readme);
3266
+ fs16.writeFileSync(readmePath, readme);
2992
3267
  }
2993
3268
  if (opts.dryRun) {
2994
3269
  return {
@@ -3055,11 +3330,11 @@ import { Command as Command33 } from "commander";
3055
3330
 
3056
3331
  // src/commands/state/show.ts
3057
3332
  import { Command as Command29 } from "commander";
3058
- import * as path24 from "path";
3333
+ import * as path26 from "path";
3059
3334
  function createShowCommand() {
3060
3335
  return new Command29("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
3061
3336
  const globalOpts = cmd.optsWithGlobals();
3062
- const projectPath = path24.resolve(opts.path);
3337
+ const projectPath = path26.resolve(opts.path);
3063
3338
  const result = await loadState(projectPath, opts.stream);
3064
3339
  if (!result.ok) {
3065
3340
  logger.error(result.error.message);
@@ -3100,12 +3375,12 @@ Decisions: ${state.decisions.length}`);
3100
3375
 
3101
3376
  // src/commands/state/reset.ts
3102
3377
  import { Command as Command30 } from "commander";
3103
- import * as fs15 from "fs";
3104
- import * as path25 from "path";
3378
+ import * as fs17 from "fs";
3379
+ import * as path27 from "path";
3105
3380
  import * as readline from "readline";
3106
3381
  function createResetCommand() {
3107
3382
  return new Command30("reset").description("Reset project state (deletes .harness/state.json)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts, _cmd) => {
3108
- const projectPath = path25.resolve(opts.path);
3383
+ const projectPath = path27.resolve(opts.path);
3109
3384
  let statePath;
3110
3385
  if (opts.stream) {
3111
3386
  const streamResult = await resolveStreamPath(projectPath, { stream: opts.stream });
@@ -3114,19 +3389,19 @@ function createResetCommand() {
3114
3389
  process.exit(ExitCode.ERROR);
3115
3390
  return;
3116
3391
  }
3117
- statePath = path25.join(streamResult.value, "state.json");
3392
+ statePath = path27.join(streamResult.value, "state.json");
3118
3393
  } else {
3119
- statePath = path25.join(projectPath, ".harness", "state.json");
3394
+ statePath = path27.join(projectPath, ".harness", "state.json");
3120
3395
  }
3121
- if (!fs15.existsSync(statePath)) {
3396
+ if (!fs17.existsSync(statePath)) {
3122
3397
  logger.info("No state file found. Nothing to reset.");
3123
3398
  process.exit(ExitCode.SUCCESS);
3124
3399
  return;
3125
3400
  }
3126
3401
  if (!opts.yes) {
3127
3402
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3128
- const answer = await new Promise((resolve30) => {
3129
- rl.question("Reset project state? This cannot be undone. [y/N] ", resolve30);
3403
+ const answer = await new Promise((resolve31) => {
3404
+ rl.question("Reset project state? This cannot be undone. [y/N] ", resolve31);
3130
3405
  });
3131
3406
  rl.close();
3132
3407
  if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
@@ -3136,7 +3411,7 @@ function createResetCommand() {
3136
3411
  }
3137
3412
  }
3138
3413
  try {
3139
- fs15.unlinkSync(statePath);
3414
+ fs17.unlinkSync(statePath);
3140
3415
  logger.success("Project state reset.");
3141
3416
  } catch (e) {
3142
3417
  logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
@@ -3149,10 +3424,10 @@ function createResetCommand() {
3149
3424
 
3150
3425
  // src/commands/state/learn.ts
3151
3426
  import { Command as Command31 } from "commander";
3152
- import * as path26 from "path";
3427
+ import * as path28 from "path";
3153
3428
  function createLearnCommand() {
3154
3429
  return new Command31("learn").description("Append a learning to .harness/learnings.md").argument("<message>", "The learning to record").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (message, opts, _cmd) => {
3155
- const projectPath = path26.resolve(opts.path);
3430
+ const projectPath = path28.resolve(opts.path);
3156
3431
  const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
3157
3432
  if (!result.ok) {
3158
3433
  logger.error(result.error.message);
@@ -3166,12 +3441,12 @@ function createLearnCommand() {
3166
3441
 
3167
3442
  // src/commands/state/streams.ts
3168
3443
  import { Command as Command32 } from "commander";
3169
- import * as path27 from "path";
3444
+ import * as path29 from "path";
3170
3445
  function createStreamsCommand() {
3171
3446
  const command = new Command32("streams").description("Manage state streams");
3172
3447
  command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => {
3173
3448
  const globalOpts = cmd.optsWithGlobals();
3174
- const projectPath = path27.resolve(opts.path);
3449
+ const projectPath = path29.resolve(opts.path);
3175
3450
  const indexResult = await loadStreamIndex(projectPath);
3176
3451
  const result = await listStreams(projectPath);
3177
3452
  if (!result.ok) {
@@ -3195,7 +3470,7 @@ function createStreamsCommand() {
3195
3470
  process.exit(ExitCode.SUCCESS);
3196
3471
  });
3197
3472
  command.command("create <name>").description("Create a new stream").option("--path <path>", "Project root path", ".").option("--branch <branch>", "Associate with a git branch").action(async (name, opts) => {
3198
- const projectPath = path27.resolve(opts.path);
3473
+ const projectPath = path29.resolve(opts.path);
3199
3474
  const result = await createStream(projectPath, name, opts.branch);
3200
3475
  if (!result.ok) {
3201
3476
  logger.error(result.error.message);
@@ -3206,7 +3481,7 @@ function createStreamsCommand() {
3206
3481
  process.exit(ExitCode.SUCCESS);
3207
3482
  });
3208
3483
  command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => {
3209
- const projectPath = path27.resolve(opts.path);
3484
+ const projectPath = path29.resolve(opts.path);
3210
3485
  const result = await archiveStream(projectPath, name);
3211
3486
  if (!result.ok) {
3212
3487
  logger.error(result.error.message);
@@ -3217,7 +3492,7 @@ function createStreamsCommand() {
3217
3492
  process.exit(ExitCode.SUCCESS);
3218
3493
  });
3219
3494
  command.command("activate <name>").description("Set the active stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => {
3220
- const projectPath = path27.resolve(opts.path);
3495
+ const projectPath = path29.resolve(opts.path);
3221
3496
  const result = await setActiveStream(projectPath, name);
3222
3497
  if (!result.ok) {
3223
3498
  logger.error(result.error.message);
@@ -3242,23 +3517,23 @@ function createStateCommand() {
3242
3517
 
3243
3518
  // src/commands/setup.ts
3244
3519
  import { Command as Command34 } from "commander";
3245
- import * as fs17 from "fs";
3520
+ import * as fs19 from "fs";
3246
3521
  import * as os4 from "os";
3247
- import * as path29 from "path";
3522
+ import * as path31 from "path";
3248
3523
  import chalk3 from "chalk";
3249
3524
 
3250
3525
  // src/utils/first-run.ts
3251
- import * as fs16 from "fs";
3526
+ import * as fs18 from "fs";
3252
3527
  import * as os3 from "os";
3253
- import * as path28 from "path";
3254
- var HARNESS_DIR = path28.join(os3.homedir(), ".harness");
3255
- var MARKER_FILE = path28.join(HARNESS_DIR, ".setup-complete");
3528
+ import * as path30 from "path";
3529
+ var HARNESS_DIR = path30.join(os3.homedir(), ".harness");
3530
+ var MARKER_FILE = path30.join(HARNESS_DIR, ".setup-complete");
3256
3531
  function isFirstRun() {
3257
- return !fs16.existsSync(MARKER_FILE);
3532
+ return !fs18.existsSync(MARKER_FILE);
3258
3533
  }
3259
3534
  function markSetupComplete() {
3260
- fs16.mkdirSync(HARNESS_DIR, { recursive: true });
3261
- fs16.writeFileSync(MARKER_FILE, "", "utf-8");
3535
+ fs18.mkdirSync(HARNESS_DIR, { recursive: true });
3536
+ fs18.writeFileSync(MARKER_FILE, "", "utf-8");
3262
3537
  }
3263
3538
  function printFirstRunWelcome() {
3264
3539
  try {
@@ -3281,6 +3556,74 @@ function checkNodeVersion() {
3281
3556
  };
3282
3557
  }
3283
3558
 
3559
+ // src/integrations/registry.ts
3560
+ var INTEGRATION_REGISTRY = [
3561
+ // --- Tier 0: zero-config (free, no API key) ---
3562
+ {
3563
+ name: "context7",
3564
+ displayName: "Context7",
3565
+ description: "Live version-pinned docs for 9,000+ libraries",
3566
+ tier: 0,
3567
+ mcpConfig: {
3568
+ command: "npx",
3569
+ args: ["-y", "@upstash/context7-mcp"]
3570
+ },
3571
+ platforms: ["claude-code", "gemini-cli"]
3572
+ },
3573
+ {
3574
+ name: "sequential-thinking",
3575
+ displayName: "Sequential Thinking",
3576
+ description: "Structured multi-step reasoning",
3577
+ tier: 0,
3578
+ mcpConfig: {
3579
+ command: "npx",
3580
+ args: ["-y", "@modelcontextprotocol/server-sequential-thinking"]
3581
+ },
3582
+ platforms: ["claude-code", "gemini-cli"]
3583
+ },
3584
+ {
3585
+ name: "playwright",
3586
+ displayName: "Playwright",
3587
+ description: "Browser automation for E2E testing",
3588
+ tier: 0,
3589
+ mcpConfig: {
3590
+ command: "npx",
3591
+ args: ["-y", "@playwright/mcp"]
3592
+ },
3593
+ platforms: ["claude-code", "gemini-cli"]
3594
+ },
3595
+ // --- Tier 1: API-key required ---
3596
+ {
3597
+ name: "perplexity",
3598
+ displayName: "Perplexity",
3599
+ description: "Real-time web search and deep research",
3600
+ tier: 1,
3601
+ envVar: "PERPLEXITY_API_KEY",
3602
+ mcpConfig: {
3603
+ command: "npx",
3604
+ args: ["-y", "perplexity-mcp"],
3605
+ env: { PERPLEXITY_API_KEY: "${PERPLEXITY_API_KEY}" }
3606
+ // harness-ignore SEC-SEC-002: env var placeholder, not a hardcoded secret
3607
+ },
3608
+ installHint: "Get an API key at https://perplexity.ai/settings/api",
3609
+ platforms: ["claude-code", "gemini-cli"]
3610
+ },
3611
+ {
3612
+ name: "augment-code",
3613
+ displayName: "Augment Code",
3614
+ description: "Semantic code search across codebase",
3615
+ tier: 1,
3616
+ envVar: "AUGMENT_SESSION_AUTH",
3617
+ mcpConfig: {
3618
+ command: "auggie",
3619
+ args: ["--mcp", "--mcp-auto-workspace"],
3620
+ env: { AUGMENT_SESSION_AUTH: "${AUGMENT_SESSION_AUTH}" }
3621
+ },
3622
+ installHint: "Install auggie CLI: npm i -g @augmentcode/auggie@latest && auggie login",
3623
+ platforms: ["claude-code", "gemini-cli"]
3624
+ }
3625
+ ];
3626
+
3284
3627
  // src/commands/setup.ts
3285
3628
  function checkNodeVersion2() {
3286
3629
  const result = checkNodeVersion();
@@ -3293,7 +3636,7 @@ function runSlashCommandGeneration() {
3293
3636
  try {
3294
3637
  const results = generateSlashCommands({
3295
3638
  global: true,
3296
- platforms: ["claude-code", "gemini-cli"],
3639
+ platforms: ["claude-code", "gemini-cli", "codex", "cursor"],
3297
3640
  yes: true,
3298
3641
  includeGlobal: false,
3299
3642
  skillsDir: "",
@@ -3307,9 +3650,9 @@ function runSlashCommandGeneration() {
3307
3650
  }
3308
3651
  }
3309
3652
  function detectClient(dirName) {
3310
- return fs17.existsSync(path29.join(os4.homedir(), dirName));
3653
+ return fs19.existsSync(path31.join(os4.homedir(), dirName));
3311
3654
  }
3312
- function runMcpSetup(cwd) {
3655
+ async function runMcpSetup(cwd) {
3313
3656
  const results = [];
3314
3657
  const clients = [
3315
3658
  { name: "Claude Code", dir: ".claude", client: "claude", configTarget: ".mcp.json" },
@@ -3318,7 +3661,9 @@ function runMcpSetup(cwd) {
3318
3661
  dir: ".gemini",
3319
3662
  client: "gemini",
3320
3663
  configTarget: ".gemini/settings.json"
3321
- }
3664
+ },
3665
+ { name: "Codex CLI", dir: ".codex", client: "codex", configTarget: ".codex/config.toml" },
3666
+ { name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" }
3322
3667
  ];
3323
3668
  for (const { name, dir, client, configTarget } of clients) {
3324
3669
  if (!detectClient(dir)) {
@@ -3345,7 +3690,40 @@ function formatStep(result) {
3345
3690
  const icon = result.status === "pass" ? chalk3.green("\u2713") : result.status === "warn" ? chalk3.yellow("\u26A0") : chalk3.red("\u2717");
3346
3691
  return ` ${icon} ${result.message}`;
3347
3692
  }
3348
- function runSetup(cwd) {
3693
+ function configureTier0Integrations(cwd) {
3694
+ try {
3695
+ const mcpPath = path31.join(cwd, ".mcp.json");
3696
+ const config = readMcpConfig(mcpPath);
3697
+ const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
3698
+ const added = [];
3699
+ for (const integration of tier0) {
3700
+ if (config.mcpServers[integration.name]) continue;
3701
+ writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
3702
+ added.push(integration.displayName);
3703
+ }
3704
+ const geminiDir = path31.join(cwd, ".gemini");
3705
+ if (fs19.existsSync(geminiDir)) {
3706
+ const geminiPath = path31.join(geminiDir, "settings.json");
3707
+ const geminiConfig = readMcpConfig(geminiPath);
3708
+ for (const integration of tier0) {
3709
+ if (!geminiConfig.mcpServers[integration.name]) {
3710
+ writeMcpEntry(geminiPath, integration.name, integration.mcpConfig);
3711
+ }
3712
+ }
3713
+ }
3714
+ if (added.length === 0) {
3715
+ return { status: "pass", message: "Tier 0 MCP integrations already configured" };
3716
+ }
3717
+ return {
3718
+ status: "pass",
3719
+ message: `Configured ${added.length} MCP integrations: ${added.join(", ")}`
3720
+ };
3721
+ } catch (error) {
3722
+ const msg = error instanceof Error ? error.message : String(error);
3723
+ return { status: "fail", message: `Tier 0 integration configuration failed \u2014 ${msg}` };
3724
+ }
3725
+ }
3726
+ async function runSetup(cwd) {
3349
3727
  const steps = [];
3350
3728
  const nodeResult = checkNodeVersion2();
3351
3729
  steps.push(nodeResult);
@@ -3354,8 +3732,10 @@ function runSetup(cwd) {
3354
3732
  }
3355
3733
  const slashResult = runSlashCommandGeneration();
3356
3734
  steps.push(slashResult);
3357
- const mcpResults = runMcpSetup(cwd);
3735
+ const mcpResults = await runMcpSetup(cwd);
3358
3736
  steps.push(...mcpResults);
3737
+ const tier0Result = configureTier0Integrations(cwd);
3738
+ steps.push(tier0Result);
3359
3739
  const success = steps.every((s) => s.status !== "fail");
3360
3740
  if (success) {
3361
3741
  markSetupComplete();
@@ -3363,12 +3743,12 @@ function runSetup(cwd) {
3363
3743
  return { steps, success };
3364
3744
  }
3365
3745
  function createSetupCommand() {
3366
- return new Command34("setup").description("Configure harness environment: slash commands, MCP, and more").action(() => {
3746
+ return new Command34("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
3367
3747
  const cwd = process.cwd();
3368
3748
  console.log("");
3369
3749
  console.log(` ${chalk3.bold("harness setup")}`);
3370
3750
  console.log("");
3371
- const { steps, success } = runSetup(cwd);
3751
+ const { steps, success } = await runSetup(cwd);
3372
3752
  for (const step of steps) {
3373
3753
  console.log(formatStep(step));
3374
3754
  }
@@ -3386,9 +3766,9 @@ function createSetupCommand() {
3386
3766
 
3387
3767
  // src/commands/doctor.ts
3388
3768
  import { Command as Command35 } from "commander";
3389
- import * as fs18 from "fs";
3769
+ import * as fs20 from "fs";
3390
3770
  import * as os5 from "os";
3391
- import * as path30 from "path";
3771
+ import * as path32 from "path";
3392
3772
  import chalk4 from "chalk";
3393
3773
  function checkNodeVersion3() {
3394
3774
  const result = checkNodeVersion();
@@ -3408,7 +3788,7 @@ function checkNodeVersion3() {
3408
3788
  }
3409
3789
  function countCommandFiles(dir, ext) {
3410
3790
  try {
3411
- return fs18.readdirSync(dir).filter((f) => f.endsWith(ext)).length;
3791
+ return fs20.readdirSync(dir).filter((f) => f.endsWith(ext)).length;
3412
3792
  } catch {
3413
3793
  return 0;
3414
3794
  }
@@ -3417,13 +3797,13 @@ function checkSlashCommands() {
3417
3797
  const platforms = [
3418
3798
  {
3419
3799
  name: "Claude Code",
3420
- dir: path30.join(os5.homedir(), ".claude", "commands", "harness"),
3800
+ dir: path32.join(os5.homedir(), ".claude", "commands", "harness"),
3421
3801
  ext: ".md",
3422
3802
  client: "claude-code"
3423
3803
  },
3424
3804
  {
3425
3805
  name: "Gemini CLI",
3426
- dir: path30.join(os5.homedir(), ".gemini", "commands", "harness"),
3806
+ dir: path32.join(os5.homedir(), ".gemini", "commands", "harness"),
3427
3807
  ext: ".toml",
3428
3808
  client: "gemini-cli"
3429
3809
  }
@@ -3445,19 +3825,10 @@ function checkSlashCommands() {
3445
3825
  };
3446
3826
  });
3447
3827
  }
3448
- function readJsonSafe(filePath) {
3449
- try {
3450
- if (!fs18.existsSync(filePath)) return null;
3451
- return JSON.parse(fs18.readFileSync(filePath, "utf-8"));
3452
- } catch {
3453
- return null;
3454
- }
3455
- }
3456
3828
  function checkMcpConfig(cwd) {
3457
3829
  const results = [];
3458
- const claudeConfigPath = path30.join(cwd, ".mcp.json");
3459
- const claudeConfig = readJsonSafe(claudeConfigPath);
3460
- if (claudeConfig?.mcpServers?.["harness"]) {
3830
+ const claudeConfig = readMcpConfig(path32.join(cwd, ".mcp.json"));
3831
+ if (claudeConfig.mcpServers?.["harness"]) {
3461
3832
  results.push({
3462
3833
  name: "mcp-claude",
3463
3834
  status: "pass",
@@ -3471,40 +3842,112 @@ function checkMcpConfig(cwd) {
3471
3842
  fix: "Run: harness setup-mcp --client claude"
3472
3843
  });
3473
3844
  }
3474
- const geminiConfigPath = path30.join(os5.homedir(), ".gemini", "settings.json");
3475
- const geminiConfig = readJsonSafe(geminiConfigPath);
3476
- if (geminiConfig?.mcpServers?.["harness"]) {
3477
- results.push({
3478
- name: "mcp-gemini",
3479
- status: "pass",
3480
- message: "MCP configured for Gemini CLI"
3481
- });
3482
- } else {
3483
- results.push({
3484
- name: "mcp-gemini",
3485
- status: "fail",
3486
- message: "MCP not configured for Gemini CLI",
3487
- fix: "Run: harness setup-mcp --client gemini"
3488
- });
3845
+ const geminiDir = path32.join(cwd, ".gemini");
3846
+ if (fs20.existsSync(geminiDir)) {
3847
+ const geminiConfig = readMcpConfig(path32.join(geminiDir, "settings.json"));
3848
+ if (geminiConfig.mcpServers?.["harness"]) {
3849
+ results.push({
3850
+ name: "mcp-gemini",
3851
+ status: "pass",
3852
+ message: "MCP configured for Gemini CLI"
3853
+ });
3854
+ } else {
3855
+ results.push({
3856
+ name: "mcp-gemini",
3857
+ status: "fail",
3858
+ message: "MCP not configured for Gemini CLI",
3859
+ fix: "Run: harness setup-mcp --client gemini"
3860
+ });
3861
+ }
3489
3862
  }
3490
3863
  return results;
3491
3864
  }
3492
- function runDoctor(cwd) {
3493
- const checks = [];
3494
- checks.push(checkNodeVersion3());
3495
- checks.push(...checkSlashCommands());
3496
- checks.push(...checkMcpConfig(cwd));
3497
- const allPassed = checks.every((c) => c.status === "pass");
3498
- return { checks, allPassed };
3499
- }
3500
- function formatCheck(check) {
3501
- const icon = check.status === "pass" ? chalk4.green("\u2713") : chalk4.red("\u2717");
3502
- let line = ` ${icon} ${check.message}`;
3503
- if (check.status === "fail" && check.fix) {
3504
- line += `
3505
- -> ${check.fix}`;
3506
- }
3507
- return line;
3865
+ function loadMcpPresence(cwd) {
3866
+ const mcpPath = path32.join(cwd, ".mcp.json");
3867
+ const geminiDir = path32.join(cwd, ".gemini");
3868
+ const hasGemini = fs20.existsSync(geminiDir);
3869
+ return {
3870
+ mcpConfig: readMcpConfig(mcpPath),
3871
+ geminiConfig: hasGemini ? readMcpConfig(path32.join(geminiDir, "settings.json")) : null,
3872
+ hasGemini
3873
+ };
3874
+ }
3875
+ function checkTier0Presence(def, presence) {
3876
+ const inClaude = !!presence.mcpConfig.mcpServers?.[def.name];
3877
+ const inGemini = !!presence.geminiConfig?.mcpServers?.[def.name];
3878
+ if (!inClaude) {
3879
+ return {
3880
+ name: `integration-${def.name}`,
3881
+ status: "fail",
3882
+ message: `${def.displayName} not configured. Run \`harness setup\` to fix.`,
3883
+ fix: "Run: harness setup"
3884
+ };
3885
+ }
3886
+ if (presence.hasGemini && !inGemini) {
3887
+ return {
3888
+ name: `integration-${def.name}`,
3889
+ status: "warn",
3890
+ message: `${def.displayName} missing from Gemini CLI config. Run \`harness setup\` to fix.`,
3891
+ fix: "Run: harness setup"
3892
+ };
3893
+ }
3894
+ return {
3895
+ name: `integration-${def.name}`,
3896
+ status: "pass",
3897
+ message: `${def.displayName} configured`
3898
+ };
3899
+ }
3900
+ function checkIntegrations(cwd) {
3901
+ const results = [];
3902
+ const presence = loadMcpPresence(cwd);
3903
+ const configPath = path32.join(cwd, "harness.config.json");
3904
+ const integrationsConfig = readIntegrationsConfig(configPath);
3905
+ for (const def of INTEGRATION_REGISTRY.filter((d) => d.tier === 0)) {
3906
+ results.push(checkTier0Presence(def, presence));
3907
+ }
3908
+ for (const def of INTEGRATION_REGISTRY.filter((d) => d.tier === 1)) {
3909
+ const enabled = integrationsConfig.enabled.includes(def.name);
3910
+ const dismissed = integrationsConfig.dismissed.includes(def.name);
3911
+ if (enabled && def.envVar && !process.env[def.envVar]) {
3912
+ results.push({
3913
+ name: `integration-${def.name}-env`,
3914
+ status: "warn",
3915
+ message: `${def.displayName} enabled but ${def.envVar} not set.`,
3916
+ ...def.installHint !== void 0 && { fix: def.installHint }
3917
+ });
3918
+ } else if (!enabled && !dismissed) {
3919
+ results.push({
3920
+ name: `integration-${def.name}`,
3921
+ status: "info",
3922
+ message: `${def.displayName} enables ${def.description.toLowerCase()}. Run \`harness integrations add ${def.name}\`.`
3923
+ });
3924
+ }
3925
+ }
3926
+ return results;
3927
+ }
3928
+ function runDoctor(cwd) {
3929
+ const checks = [];
3930
+ checks.push(checkNodeVersion3());
3931
+ checks.push(...checkSlashCommands());
3932
+ checks.push(...checkMcpConfig(cwd));
3933
+ checks.push(...checkIntegrations(cwd));
3934
+ const allPassed = checks.every((c) => c.status !== "fail");
3935
+ return { checks, allPassed };
3936
+ }
3937
+ function formatCheck(check) {
3938
+ const icons = {
3939
+ pass: chalk4.green("\u2713"),
3940
+ fail: chalk4.red("\u2717"),
3941
+ warn: chalk4.yellow("!"),
3942
+ info: chalk4.blue("\u2139")
3943
+ };
3944
+ const icon = icons[check.status];
3945
+ let line = ` ${icon} ${check.message}`;
3946
+ if ((check.status === "fail" || check.status === "warn") && check.fix) {
3947
+ line += `
3948
+ -> ${check.fix}`;
3949
+ }
3950
+ return line;
3508
3951
  }
3509
3952
  function createDoctorCommand() {
3510
3953
  return new Command35("doctor").description("Check environment health: Node version, slash commands, MCP configuration").action((_opts, cmd) => {
@@ -3620,8 +4063,8 @@ function createCheckCommand() {
3620
4063
 
3621
4064
  // src/commands/ci/init.ts
3622
4065
  import { Command as Command37 } from "commander";
3623
- import * as fs19 from "fs";
3624
- import * as path31 from "path";
4066
+ import * as fs21 from "fs";
4067
+ import * as path33 from "path";
3625
4068
  var ALL_CHECKS = [
3626
4069
  "validate",
3627
4070
  "deps",
@@ -3722,8 +4165,8 @@ function generateCIConfig(options) {
3722
4165
  });
3723
4166
  }
3724
4167
  function detectPlatform() {
3725
- if (fs19.existsSync(".github")) return "github";
3726
- if (fs19.existsSync(".gitlab-ci.yml")) return "gitlab";
4168
+ if (fs21.existsSync(".github")) return "github";
4169
+ if (fs21.existsSync(".gitlab-ci.yml")) return "gitlab";
3727
4170
  return null;
3728
4171
  }
3729
4172
  function createInitCommand2() {
@@ -3739,12 +4182,12 @@ function createInitCommand2() {
3739
4182
  process.exit(result.error.exitCode);
3740
4183
  }
3741
4184
  const { filename, content } = result.value;
3742
- const targetPath = path31.resolve(filename);
3743
- const dir = path31.dirname(targetPath);
3744
- fs19.mkdirSync(dir, { recursive: true });
3745
- fs19.writeFileSync(targetPath, content);
4185
+ const targetPath = path33.resolve(filename);
4186
+ const dir = path33.dirname(targetPath);
4187
+ fs21.mkdirSync(dir, { recursive: true });
4188
+ fs21.writeFileSync(targetPath, content);
3746
4189
  if (platform === "generic" && process.platform !== "win32") {
3747
- fs19.chmodSync(targetPath, "755");
4190
+ fs21.chmodSync(targetPath, "755");
3748
4191
  }
3749
4192
  if (globalOpts.json) {
3750
4193
  console.log(JSON.stringify({ file: filename, platform }));
@@ -3764,12 +4207,12 @@ function createCICommand() {
3764
4207
  }
3765
4208
 
3766
4209
  // src/commands/hooks/index.ts
3767
- import { Command as Command42 } from "commander";
4210
+ import { Command as Command43 } from "commander";
3768
4211
 
3769
4212
  // src/commands/hooks/init.ts
3770
4213
  import { Command as Command39 } from "commander";
3771
- import * as fs20 from "fs";
3772
- import * as path32 from "path";
4214
+ import * as fs22 from "fs";
4215
+ import * as path34 from "path";
3773
4216
  import { fileURLToPath } from "url";
3774
4217
 
3775
4218
  // src/hooks/profiles.ts
@@ -3778,7 +4221,9 @@ var HOOK_SCRIPTS = [
3778
4221
  { name: "protect-config", event: "PreToolUse", matcher: "Write|Edit", minProfile: "standard" },
3779
4222
  { name: "quality-gate", event: "PostToolUse", matcher: "Edit|Write", minProfile: "standard" },
3780
4223
  { name: "pre-compact-state", event: "PreCompact", matcher: "*", minProfile: "standard" },
3781
- { name: "cost-tracker", event: "Stop", matcher: "*", minProfile: "strict" }
4224
+ { name: "cost-tracker", event: "Stop", matcher: "*", minProfile: "strict" },
4225
+ { name: "sentinel-pre", event: "PreToolUse", matcher: "*", minProfile: "strict" },
4226
+ { name: "sentinel-post", event: "PostToolUse", matcher: "*", minProfile: "strict" }
3782
4227
  ];
3783
4228
  var PROFILE_ORDER = ["minimal", "standard", "strict"];
3784
4229
  function hooksForProfile(profile) {
@@ -3795,11 +4240,11 @@ var PROFILES = {
3795
4240
 
3796
4241
  // src/commands/hooks/init.ts
3797
4242
  var __filename = fileURLToPath(import.meta.url);
3798
- var __dirname = path32.dirname(__filename);
4243
+ var __dirname = path34.dirname(__filename);
3799
4244
  var VALID_PROFILES = ["minimal", "standard", "strict"];
3800
4245
  function resolveHookSourceDir() {
3801
- const candidate = path32.resolve(__dirname, "..", "..", "hooks");
3802
- if (fs20.existsSync(candidate)) {
4246
+ const candidate = path34.resolve(__dirname, "..", "..", "hooks");
4247
+ if (fs22.existsSync(candidate)) {
3803
4248
  return candidate;
3804
4249
  }
3805
4250
  throw new Error(`Cannot locate hook scripts directory. Expected at: ${candidate}`);
@@ -3827,12 +4272,12 @@ function mergeSettings(existing, hooksConfig) {
3827
4272
  }
3828
4273
  function initHooks(options) {
3829
4274
  const { profile, projectDir } = options;
3830
- const hooksDestDir = path32.join(projectDir, ".harness", "hooks");
3831
- fs20.mkdirSync(hooksDestDir, { recursive: true });
3832
- if (fs20.existsSync(hooksDestDir)) {
3833
- for (const entry of fs20.readdirSync(hooksDestDir)) {
4275
+ const hooksDestDir = path34.join(projectDir, ".harness", "hooks");
4276
+ fs22.mkdirSync(hooksDestDir, { recursive: true });
4277
+ if (fs22.existsSync(hooksDestDir)) {
4278
+ for (const entry of fs22.readdirSync(hooksDestDir)) {
3834
4279
  if (entry.endsWith(".js")) {
3835
- fs20.unlinkSync(path32.join(hooksDestDir, entry));
4280
+ fs22.unlinkSync(path34.join(hooksDestDir, entry));
3836
4281
  }
3837
4282
  }
3838
4283
  }
@@ -3841,22 +4286,22 @@ function initHooks(options) {
3841
4286
  const activeNames = PROFILES[profile];
3842
4287
  const activeScripts = HOOK_SCRIPTS.filter((h) => activeNames.includes(h.name));
3843
4288
  for (const script of activeScripts) {
3844
- const srcFile = path32.join(sourceDir, `${script.name}.js`);
3845
- const destFile = path32.join(hooksDestDir, `${script.name}.js`);
3846
- if (fs20.existsSync(srcFile)) {
3847
- fs20.copyFileSync(srcFile, destFile);
4289
+ const srcFile = path34.join(sourceDir, `${script.name}.js`);
4290
+ const destFile = path34.join(hooksDestDir, `${script.name}.js`);
4291
+ if (fs22.existsSync(srcFile)) {
4292
+ fs22.copyFileSync(srcFile, destFile);
3848
4293
  copiedScripts.push(script.name);
3849
4294
  }
3850
4295
  }
3851
- const profilePath = path32.join(hooksDestDir, "profile.json");
3852
- fs20.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
3853
- const claudeDir = path32.join(projectDir, ".claude");
3854
- fs20.mkdirSync(claudeDir, { recursive: true });
3855
- const settingsPath = path32.join(claudeDir, "settings.json");
4296
+ const profilePath = path34.join(hooksDestDir, "profile.json");
4297
+ fs22.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
4298
+ const claudeDir = path34.join(projectDir, ".claude");
4299
+ fs22.mkdirSync(claudeDir, { recursive: true });
4300
+ const settingsPath = path34.join(claudeDir, "settings.json");
3856
4301
  let existing = {};
3857
- if (fs20.existsSync(settingsPath)) {
4302
+ if (fs22.existsSync(settingsPath)) {
3858
4303
  try {
3859
- existing = JSON.parse(fs20.readFileSync(settingsPath, "utf-8"));
4304
+ existing = JSON.parse(fs22.readFileSync(settingsPath, "utf-8"));
3860
4305
  } catch (e) {
3861
4306
  throw new Error(
3862
4307
  `Malformed .claude/settings.json \u2014 fix the JSON syntax before running hooks init. Parse error: ${e instanceof Error ? e.message : String(e)}`,
@@ -3866,7 +4311,7 @@ function initHooks(options) {
3866
4311
  }
3867
4312
  const hooksConfig = buildSettingsHooks(profile);
3868
4313
  const merged = mergeSettings(existing, hooksConfig);
3869
- fs20.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
4314
+ fs22.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
3870
4315
  return { copiedScripts, settingsPath, profilePath };
3871
4316
  }
3872
4317
  function createInitCommand3() {
@@ -3894,7 +4339,7 @@ function createInitCommand3() {
3894
4339
  `Installed ${result.copiedScripts.length} hook scripts to .harness/hooks/`
3895
4340
  );
3896
4341
  logger.info(`Profile: ${profile}`);
3897
- logger.info(`Settings: ${path32.relative(projectDir, result.settingsPath)}`);
4342
+ logger.info(`Settings: ${path34.relative(projectDir, result.settingsPath)}`);
3898
4343
  logger.dim("Run 'harness hooks list' to see installed hooks");
3899
4344
  }
3900
4345
  } catch (err) {
@@ -3907,18 +4352,18 @@ function createInitCommand3() {
3907
4352
 
3908
4353
  // src/commands/hooks/list.ts
3909
4354
  import { Command as Command40 } from "commander";
3910
- import * as fs21 from "fs";
3911
- import * as path33 from "path";
4355
+ import * as fs23 from "fs";
4356
+ import * as path35 from "path";
3912
4357
  function listHooks(projectDir) {
3913
- const hooksDir = path33.join(projectDir, ".harness", "hooks");
3914
- const profilePath = path33.join(hooksDir, "profile.json");
3915
- if (!fs21.existsSync(profilePath)) {
4358
+ const hooksDir = path35.join(projectDir, ".harness", "hooks");
4359
+ const profilePath = path35.join(hooksDir, "profile.json");
4360
+ if (!fs23.existsSync(profilePath)) {
3916
4361
  return { installed: false, profile: null, hooks: [] };
3917
4362
  }
3918
4363
  let profile = "standard";
3919
4364
  let warning;
3920
4365
  try {
3921
- const data = JSON.parse(fs21.readFileSync(profilePath, "utf-8"));
4366
+ const data = JSON.parse(fs23.readFileSync(profilePath, "utf-8"));
3922
4367
  if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
3923
4368
  profile = data.profile;
3924
4369
  }
@@ -3930,7 +4375,7 @@ function listHooks(projectDir) {
3930
4375
  name: h.name,
3931
4376
  event: h.event,
3932
4377
  matcher: h.matcher,
3933
- scriptPath: path33.join(".harness", "hooks", `${h.name}.js`)
4378
+ scriptPath: path35.join(".harness", "hooks", `${h.name}.js`)
3934
4379
  }));
3935
4380
  const result = { installed: true, profile, hooks };
3936
4381
  if (warning) {
@@ -3961,27 +4406,27 @@ function createListCommand3() {
3961
4406
 
3962
4407
  // src/commands/hooks/remove.ts
3963
4408
  import { Command as Command41 } from "commander";
3964
- import * as fs22 from "fs";
3965
- import * as path34 from "path";
4409
+ import * as fs24 from "fs";
4410
+ import * as path36 from "path";
3966
4411
  function removeHooks(projectDir) {
3967
- const hooksDir = path34.join(projectDir, ".harness", "hooks");
3968
- const settingsPath = path34.join(projectDir, ".claude", "settings.json");
4412
+ const hooksDir = path36.join(projectDir, ".harness", "hooks");
4413
+ const settingsPath = path36.join(projectDir, ".claude", "settings.json");
3969
4414
  let removed = false;
3970
4415
  let settingsCleaned = false;
3971
- if (fs22.existsSync(hooksDir)) {
3972
- fs22.rmSync(hooksDir, { recursive: true, force: true });
4416
+ if (fs24.existsSync(hooksDir)) {
4417
+ fs24.rmSync(hooksDir, { recursive: true, force: true });
3973
4418
  removed = true;
3974
4419
  }
3975
- if (fs22.existsSync(settingsPath)) {
4420
+ if (fs24.existsSync(settingsPath)) {
3976
4421
  try {
3977
- const settings = JSON.parse(fs22.readFileSync(settingsPath, "utf-8"));
4422
+ const settings = JSON.parse(fs24.readFileSync(settingsPath, "utf-8"));
3978
4423
  if (settings.hooks !== void 0) {
3979
4424
  delete settings.hooks;
3980
4425
  settingsCleaned = true;
3981
4426
  if (Object.keys(settings).length === 0) {
3982
- fs22.unlinkSync(settingsPath);
4427
+ fs24.unlinkSync(settingsPath);
3983
4428
  } else {
3984
- fs22.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
4429
+ fs24.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3985
4430
  }
3986
4431
  }
3987
4432
  } catch {
@@ -4011,17 +4456,99 @@ function createRemoveCommand() {
4011
4456
  });
4012
4457
  }
4013
4458
 
4459
+ // src/commands/hooks/add.ts
4460
+ import { Command as Command42 } from "commander";
4461
+ import * as fs25 from "fs";
4462
+ import * as path37 from "path";
4463
+ import { fileURLToPath as fileURLToPath2 } from "url";
4464
+ var __dirname2 = path37.dirname(fileURLToPath2(import.meta.url));
4465
+ var ALIASES = {
4466
+ sentinel: ["sentinel-pre", "sentinel-post"]
4467
+ };
4468
+ function hookSourceDir() {
4469
+ const d = path37.resolve(__dirname2, "..", "..", "hooks");
4470
+ if (fs25.existsSync(d)) return d;
4471
+ throw new Error(`Hook scripts not found: ${d}`);
4472
+ }
4473
+ function readJson(p) {
4474
+ return fs25.existsSync(p) ? JSON.parse(fs25.readFileSync(p, "utf-8")) : {};
4475
+ }
4476
+ function registerHook(s, ev, matcher, name) {
4477
+ if (!s.hooks[ev]) s.hooks[ev] = [];
4478
+ const cmd = `node .harness/hooks/${name}.js`;
4479
+ if (!s.hooks[ev].some((e) => e.hooks?.some((h) => h.command === cmd))) {
4480
+ s.hooks[ev].push({ matcher, hooks: [{ type: "command", command: cmd }] });
4481
+ }
4482
+ }
4483
+ function addHooks(hookName, projectDir) {
4484
+ const names = ALIASES[hookName] ?? [hookName];
4485
+ const result = { added: [], alreadyInstalled: [], notFound: [] };
4486
+ const srcDir = hookSourceDir();
4487
+ const destDir = path37.join(projectDir, ".harness", "hooks");
4488
+ fs25.mkdirSync(destDir, { recursive: true });
4489
+ const claudeDir = path37.join(projectDir, ".claude");
4490
+ fs25.mkdirSync(claudeDir, { recursive: true });
4491
+ const settingsPath = path37.join(claudeDir, "settings.json");
4492
+ const settings = readJson(settingsPath);
4493
+ if (!settings.hooks) settings.hooks = {};
4494
+ for (const name of names) {
4495
+ const def = HOOK_SCRIPTS.find((h) => h.name === name);
4496
+ if (!def) {
4497
+ result.notFound.push(name);
4498
+ continue;
4499
+ }
4500
+ const src = path37.join(srcDir, `${name}.js`);
4501
+ const dest = path37.join(destDir, `${name}.js`);
4502
+ if (fs25.existsSync(dest)) {
4503
+ result.alreadyInstalled.push(name);
4504
+ } else if (!fs25.existsSync(src)) {
4505
+ result.notFound.push(name);
4506
+ continue;
4507
+ } else {
4508
+ fs25.copyFileSync(src, dest);
4509
+ result.added.push(name);
4510
+ }
4511
+ registerHook(settings, def.event, def.matcher, name);
4512
+ }
4513
+ fs25.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
4514
+ return result;
4515
+ }
4516
+ function createAddCommand2() {
4517
+ return new Command42("add").argument("<hook-name>", "Hook name or alias (e.g., sentinel)").description("Add a hook without changing the profile").action(async (hookName, _opts, cmd) => {
4518
+ const projectDir = process.cwd();
4519
+ try {
4520
+ const res = addHooks(hookName, projectDir);
4521
+ if (cmd.optsWithGlobals().json) {
4522
+ console.log(JSON.stringify(res));
4523
+ return;
4524
+ }
4525
+ if (res.notFound.length > 0) {
4526
+ logger.error(`Unknown hook(s): ${res.notFound.join(", ")}`);
4527
+ logger.info(`Available: ${HOOK_SCRIPTS.map((h) => h.name).join(", ")}`);
4528
+ logger.info(`Aliases: ${Object.keys(ALIASES).join(", ")}`);
4529
+ process.exit(2);
4530
+ }
4531
+ for (const n of res.added) logger.success(`Added hook: ${n}`);
4532
+ for (const n of res.alreadyInstalled) logger.info(`Already installed: ${n}`);
4533
+ } catch (err) {
4534
+ logger.error(`Failed to add hook: ${err instanceof Error ? err.message : String(err)}`);
4535
+ process.exit(2);
4536
+ }
4537
+ });
4538
+ }
4539
+
4014
4540
  // src/commands/hooks/index.ts
4015
4541
  function createHooksCommand() {
4016
- const command = new Command42("hooks").description("Manage Claude Code hook configurations");
4542
+ const command = new Command43("hooks").description("Manage Claude Code hook configurations");
4017
4543
  command.addCommand(createInitCommand3());
4018
4544
  command.addCommand(createListCommand3());
4019
4545
  command.addCommand(createRemoveCommand());
4546
+ command.addCommand(createAddCommand2());
4020
4547
  return command;
4021
4548
  }
4022
4549
 
4023
4550
  // src/commands/update.ts
4024
- import { Command as Command43 } from "commander";
4551
+ import { Command as Command44 } from "commander";
4025
4552
  import { execFileSync as execFileSync4 } from "child_process";
4026
4553
  import { realpathSync } from "fs";
4027
4554
  import readline2 from "readline";
@@ -4081,10 +4608,10 @@ function prompt(question) {
4081
4608
  input: process.stdin,
4082
4609
  output: process.stdout
4083
4610
  });
4084
- return new Promise((resolve30) => {
4611
+ return new Promise((resolve31) => {
4085
4612
  rl.question(question, (answer) => {
4086
4613
  rl.close();
4087
- resolve30(answer.trim().toLowerCase());
4614
+ resolve31(answer.trim().toLowerCase());
4088
4615
  });
4089
4616
  });
4090
4617
  }
@@ -4104,7 +4631,7 @@ async function offerRegeneration() {
4104
4631
  }
4105
4632
  }
4106
4633
  function createUpdateCommand() {
4107
- return new Command43("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Pin @harness-engineering/cli to a specific version").action(async (opts, cmd) => {
4634
+ return new Command44("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Pin @harness-engineering/cli to a specific version").action(async (opts, cmd) => {
4108
4635
  const globalOpts = cmd.optsWithGlobals();
4109
4636
  const pm = detectPackageManager();
4110
4637
  if (globalOpts.verbose) {
@@ -4162,9 +4689,9 @@ function createUpdateCommand() {
4162
4689
  }
4163
4690
 
4164
4691
  // src/commands/generate.ts
4165
- import { Command as Command44 } from "commander";
4692
+ import { Command as Command45 } from "commander";
4166
4693
  function createGenerateCommand3() {
4167
- return new Command44("generate").description("Generate all platform integrations (slash commands + agent definitions)").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global directories", false).option("--include-global", "Include built-in global skills", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
4694
+ return new Command45("generate").description("Generate all platform integrations (slash commands + agent definitions)").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global directories", false).option("--include-global", "Include built-in global skills", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
4168
4695
  const globalOpts = cmd.optsWithGlobals();
4169
4696
  const platforms = opts.platforms.split(",").map((p) => p.trim());
4170
4697
  for (const p of platforms) {
@@ -4223,8 +4750,8 @@ function createGenerateCommand3() {
4223
4750
  }
4224
4751
 
4225
4752
  // src/commands/graph/scan.ts
4226
- import { Command as Command45 } from "commander";
4227
- import * as path35 from "path";
4753
+ import { Command as Command46 } from "commander";
4754
+ import * as path38 from "path";
4228
4755
  async function runScan(projectPath) {
4229
4756
  const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-B26DFXMP.js");
4230
4757
  const store = new GraphStore();
@@ -4237,13 +4764,13 @@ async function runScan(projectPath) {
4237
4764
  await new GitIngestor(store).ingest(projectPath);
4238
4765
  } catch {
4239
4766
  }
4240
- const graphDir = path35.join(projectPath, ".harness", "graph");
4767
+ const graphDir = path38.join(projectPath, ".harness", "graph");
4241
4768
  await store.save(graphDir);
4242
4769
  return { nodeCount: store.nodeCount, edgeCount: store.edgeCount, durationMs: Date.now() - start };
4243
4770
  }
4244
4771
  function createScanCommand() {
4245
- return new Command45("scan").description("Scan project and build knowledge graph").argument("[path]", "Project root path", ".").action(async (inputPath, _opts, cmd) => {
4246
- const projectPath = path35.resolve(inputPath);
4772
+ return new Command46("scan").description("Scan project and build knowledge graph").argument("[path]", "Project root path", ".").action(async (inputPath, _opts, cmd) => {
4773
+ const projectPath = path38.resolve(inputPath);
4247
4774
  const globalOpts = cmd.optsWithGlobals();
4248
4775
  try {
4249
4776
  const result = await runScan(projectPath);
@@ -4262,13 +4789,13 @@ function createScanCommand() {
4262
4789
  }
4263
4790
 
4264
4791
  // src/commands/graph/ingest.ts
4265
- import { Command as Command46 } from "commander";
4266
- import * as path36 from "path";
4792
+ import { Command as Command47 } from "commander";
4793
+ import * as path39 from "path";
4267
4794
  async function loadConnectorConfig(projectPath, source) {
4268
4795
  try {
4269
- const fs29 = await import("fs/promises");
4270
- const configPath = path36.join(projectPath, "harness.config.json");
4271
- const config = JSON.parse(await fs29.readFile(configPath, "utf-8"));
4796
+ const fs34 = await import("fs/promises");
4797
+ const configPath = path39.join(projectPath, "harness.config.json");
4798
+ const config = JSON.parse(await fs34.readFile(configPath, "utf-8"));
4272
4799
  const connector = config.graph?.connectors?.find(
4273
4800
  (c) => c.source === source
4274
4801
  );
@@ -4308,7 +4835,7 @@ async function runIngest(projectPath, source, opts) {
4308
4835
  JiraConnector,
4309
4836
  SlackConnector
4310
4837
  } = await import("./dist-B26DFXMP.js");
4311
- const graphDir = path36.join(projectPath, ".harness", "graph");
4838
+ const graphDir = path39.join(projectPath, ".harness", "graph");
4312
4839
  const store = new GraphStore();
4313
4840
  await store.load(graphDir);
4314
4841
  if (opts?.all) {
@@ -4369,13 +4896,13 @@ async function runIngest(projectPath, source, opts) {
4369
4896
  return result;
4370
4897
  }
4371
4898
  function createIngestCommand() {
4372
- return new Command46("ingest").description("Ingest data into the knowledge graph").option("--source <name>", "Source to ingest (code, knowledge, git, jira, slack)").option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(async (opts, cmd) => {
4899
+ return new Command47("ingest").description("Ingest data into the knowledge graph").option("--source <name>", "Source to ingest (code, knowledge, git, jira, slack)").option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(async (opts, cmd) => {
4373
4900
  if (!opts.source && !opts.all) {
4374
4901
  console.error("Error: --source or --all is required");
4375
4902
  process.exit(1);
4376
4903
  }
4377
4904
  const globalOpts = cmd.optsWithGlobals();
4378
- const projectPath = path36.resolve(globalOpts.config ? path36.dirname(globalOpts.config) : ".");
4905
+ const projectPath = path39.resolve(globalOpts.config ? path39.dirname(globalOpts.config) : ".");
4379
4906
  try {
4380
4907
  const result = await runIngest(projectPath, opts.source ?? "", {
4381
4908
  full: opts.full,
@@ -4397,12 +4924,12 @@ function createIngestCommand() {
4397
4924
  }
4398
4925
 
4399
4926
  // src/commands/graph/query.ts
4400
- import { Command as Command47 } from "commander";
4401
- import * as path37 from "path";
4927
+ import { Command as Command48 } from "commander";
4928
+ import * as path40 from "path";
4402
4929
  async function runQuery(projectPath, rootNodeId, opts) {
4403
4930
  const { GraphStore, ContextQL } = await import("./dist-B26DFXMP.js");
4404
4931
  const store = new GraphStore();
4405
- const graphDir = path37.join(projectPath, ".harness", "graph");
4932
+ const graphDir = path40.join(projectPath, ".harness", "graph");
4406
4933
  const loaded = await store.load(graphDir);
4407
4934
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
4408
4935
  const params = {
@@ -4416,9 +4943,9 @@ async function runQuery(projectPath, rootNodeId, opts) {
4416
4943
  return cql.execute(params);
4417
4944
  }
4418
4945
  function createQueryCommand() {
4419
- return new Command47("query").description("Query the knowledge graph").argument("<rootNodeId>", "Starting node ID").option("--depth <n>", "Max traversal depth", "3").option("--types <types>", "Comma-separated node types to include").option("--edges <edges>", "Comma-separated edge types to include").option("--bidirectional", "Traverse both directions").action(async (rootNodeId, opts, cmd) => {
4946
+ return new Command48("query").description("Query the knowledge graph").argument("<rootNodeId>", "Starting node ID").option("--depth <n>", "Max traversal depth", "3").option("--types <types>", "Comma-separated node types to include").option("--edges <edges>", "Comma-separated edge types to include").option("--bidirectional", "Traverse both directions").action(async (rootNodeId, opts, cmd) => {
4420
4947
  const globalOpts = cmd.optsWithGlobals();
4421
- const projectPath = path37.resolve(globalOpts.config ? path37.dirname(globalOpts.config) : ".");
4948
+ const projectPath = path40.resolve(globalOpts.config ? path40.dirname(globalOpts.config) : ".");
4422
4949
  try {
4423
4950
  const result = await runQuery(projectPath, rootNodeId, {
4424
4951
  depth: parseInt(opts.depth),
@@ -4444,21 +4971,21 @@ function createQueryCommand() {
4444
4971
  }
4445
4972
 
4446
4973
  // src/commands/graph/index.ts
4447
- import { Command as Command48 } from "commander";
4974
+ import { Command as Command49 } from "commander";
4448
4975
 
4449
4976
  // src/commands/graph/status.ts
4450
- import * as path38 from "path";
4977
+ import * as path41 from "path";
4451
4978
  async function runGraphStatus(projectPath) {
4452
4979
  const { GraphStore } = await import("./dist-B26DFXMP.js");
4453
- const graphDir = path38.join(projectPath, ".harness", "graph");
4980
+ const graphDir = path41.join(projectPath, ".harness", "graph");
4454
4981
  const store = new GraphStore();
4455
4982
  const loaded = await store.load(graphDir);
4456
4983
  if (!loaded) return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
4457
- const fs29 = await import("fs/promises");
4458
- const metaPath = path38.join(graphDir, "metadata.json");
4984
+ const fs34 = await import("fs/promises");
4985
+ const metaPath = path41.join(graphDir, "metadata.json");
4459
4986
  let lastScan = "unknown";
4460
4987
  try {
4461
- const meta = JSON.parse(await fs29.readFile(metaPath, "utf-8"));
4988
+ const meta = JSON.parse(await fs34.readFile(metaPath, "utf-8"));
4462
4989
  lastScan = meta.lastScanTimestamp;
4463
4990
  } catch {
4464
4991
  }
@@ -4469,8 +4996,8 @@ async function runGraphStatus(projectPath) {
4469
4996
  }
4470
4997
  let connectorSyncStatus = {};
4471
4998
  try {
4472
- const syncMetaPath = path38.join(graphDir, "sync-metadata.json");
4473
- const syncMeta = JSON.parse(await fs29.readFile(syncMetaPath, "utf-8"));
4999
+ const syncMetaPath = path41.join(graphDir, "sync-metadata.json");
5000
+ const syncMeta = JSON.parse(await fs34.readFile(syncMetaPath, "utf-8"));
4474
5001
  for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
4475
5002
  connectorSyncStatus[name] = data.lastSyncTimestamp;
4476
5003
  }
@@ -4487,10 +5014,10 @@ async function runGraphStatus(projectPath) {
4487
5014
  }
4488
5015
 
4489
5016
  // src/commands/graph/export.ts
4490
- import * as path39 from "path";
5017
+ import * as path42 from "path";
4491
5018
  async function runGraphExport(projectPath, format) {
4492
5019
  const { GraphStore } = await import("./dist-B26DFXMP.js");
4493
- const graphDir = path39.join(projectPath, ".harness", "graph");
5020
+ const graphDir = path42.join(projectPath, ".harness", "graph");
4494
5021
  const store = new GraphStore();
4495
5022
  const loaded = await store.load(graphDir);
4496
5023
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
@@ -4519,13 +5046,13 @@ async function runGraphExport(projectPath, format) {
4519
5046
  }
4520
5047
 
4521
5048
  // src/commands/graph/index.ts
4522
- import * as path40 from "path";
5049
+ import * as path43 from "path";
4523
5050
  function createGraphCommand() {
4524
- const graph = new Command48("graph").description("Knowledge graph management");
5051
+ const graph = new Command49("graph").description("Knowledge graph management");
4525
5052
  graph.command("status").description("Show graph statistics").action(async (_opts, cmd) => {
4526
5053
  try {
4527
5054
  const globalOpts = cmd.optsWithGlobals();
4528
- const projectPath = path40.resolve(globalOpts.config ? path40.dirname(globalOpts.config) : ".");
5055
+ const projectPath = path43.resolve(globalOpts.config ? path43.dirname(globalOpts.config) : ".");
4529
5056
  const result = await runGraphStatus(projectPath);
4530
5057
  if (globalOpts.json) {
4531
5058
  console.log(JSON.stringify(result, null, 2));
@@ -4552,7 +5079,7 @@ function createGraphCommand() {
4552
5079
  });
4553
5080
  graph.command("export").description("Export graph").requiredOption("--format <format>", "Output format (json, mermaid)").action(async (opts, cmd) => {
4554
5081
  const globalOpts = cmd.optsWithGlobals();
4555
- const projectPath = path40.resolve(globalOpts.config ? path40.dirname(globalOpts.config) : ".");
5082
+ const projectPath = path43.resolve(globalOpts.config ? path43.dirname(globalOpts.config) : ".");
4556
5083
  try {
4557
5084
  const output = await runGraphExport(projectPath, opts.format);
4558
5085
  console.log(output);
@@ -4565,19 +5092,19 @@ function createGraphCommand() {
4565
5092
  }
4566
5093
 
4567
5094
  // src/commands/mcp.ts
4568
- import { Command as Command49 } from "commander";
5095
+ import { Command as Command50 } from "commander";
4569
5096
  function createMcpCommand() {
4570
- return new Command49("mcp").description("Start the MCP (Model Context Protocol) server on stdio").action(async () => {
4571
- const { startServer: startServer2 } = await import("./mcp-VU5FMO52.js");
4572
- await startServer2();
5097
+ return new Command50("mcp").description("Start the MCP (Model Context Protocol) server on stdio").option("--tools <tools...>", "Only register the specified tools (used by Cursor integration)").action(async (opts) => {
5098
+ const { startServer: startServer2 } = await import("./mcp-YENEPHBW.js");
5099
+ await startServer2(opts.tools);
4573
5100
  });
4574
5101
  }
4575
5102
 
4576
5103
  // src/commands/impact-preview.ts
4577
- import { Command as Command50 } from "commander";
5104
+ import { Command as Command51 } from "commander";
4578
5105
  import { execSync as execSync3 } from "child_process";
4579
- import * as path41 from "path";
4580
- import * as fs23 from "fs";
5106
+ import * as path44 from "path";
5107
+ import * as fs26 from "fs";
4581
5108
  function getStagedFiles(cwd) {
4582
5109
  try {
4583
5110
  const output = execSync3("git diff --cached --name-only", {
@@ -4591,7 +5118,7 @@ function getStagedFiles(cwd) {
4591
5118
  }
4592
5119
  function graphExists(projectPath) {
4593
5120
  try {
4594
- return fs23.existsSync(path41.join(projectPath, ".harness", "graph", "graph.json"));
5121
+ return fs26.existsSync(path44.join(projectPath, ".harness", "graph", "graph.json"));
4595
5122
  } catch {
4596
5123
  return false;
4597
5124
  }
@@ -4600,7 +5127,7 @@ function extractNodeName(id) {
4600
5127
  const parts = id.split(":");
4601
5128
  if (parts.length > 1) {
4602
5129
  const fullPath = parts.slice(1).join(":");
4603
- return path41.basename(fullPath);
5130
+ return path44.basename(fullPath);
4604
5131
  }
4605
5132
  return id;
4606
5133
  }
@@ -4723,7 +5250,7 @@ function formatPerFile(perFileResults) {
4723
5250
  return lines.join("\n");
4724
5251
  }
4725
5252
  async function runImpactPreview(options) {
4726
- const projectPath = path41.resolve(options.path ?? process.cwd());
5253
+ const projectPath = path44.resolve(options.path ?? process.cwd());
4727
5254
  const stagedFiles = getStagedFiles(projectPath);
4728
5255
  if (stagedFiles.length === 0) {
4729
5256
  return "Impact Preview: no staged changes";
@@ -4770,7 +5297,7 @@ async function runImpactPreview(options) {
4770
5297
  return formatCompact(stagedFiles.length, merged, aggregateCounts);
4771
5298
  }
4772
5299
  function createImpactPreviewCommand() {
4773
- const command = new Command50("impact-preview").description("Show blast radius of staged changes using the knowledge graph").option("--detailed", "Show all affected files instead of top items").option("--per-file", "Show impact per staged file instead of aggregate").option("--path <dir>", "Project root (default: cwd)").action(async (opts) => {
5300
+ const command = new Command51("impact-preview").description("Show blast radius of staged changes using the knowledge graph").option("--detailed", "Show all affected files instead of top items").option("--per-file", "Show impact per staged file instead of aggregate").option("--path <dir>", "Project root (default: cwd)").action(async (opts) => {
4774
5301
  const output = await runImpactPreview({
4775
5302
  detailed: opts.detailed,
4776
5303
  perFile: opts.perFile,
@@ -4783,7 +5310,7 @@ function createImpactPreviewCommand() {
4783
5310
  }
4784
5311
 
4785
5312
  // src/commands/check-arch.ts
4786
- import { Command as Command51 } from "commander";
5313
+ import { Command as Command52 } from "commander";
4787
5314
  import { execSync as execSync4 } from "child_process";
4788
5315
  function getCommitHash2(cwd) {
4789
5316
  try {
@@ -4878,7 +5405,7 @@ async function runCheckArch(options) {
4878
5405
  });
4879
5406
  }
4880
5407
  function createCheckArchCommand() {
4881
- const command = new Command51("check-arch").description("Check architecture assertions against baseline and thresholds").option("--update-baseline", "Capture current state as new baseline").option("--module <path>", "Check a single module").action(async (opts, cmd) => {
5408
+ const command = new Command52("check-arch").description("Check architecture assertions against baseline and thresholds").option("--update-baseline", "Capture current state as new baseline").option("--module <path>", "Check a single module").action(async (opts, cmd) => {
4882
5409
  const globalOpts = cmd.optsWithGlobals();
4883
5410
  const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
4884
5411
  const formatter = new OutputFormatter(mode);
@@ -4944,20 +5471,20 @@ function createCheckArchCommand() {
4944
5471
  }
4945
5472
 
4946
5473
  // src/commands/blueprint.ts
4947
- import { Command as Command52 } from "commander";
4948
- import * as path42 from "path";
5474
+ import { Command as Command53 } from "commander";
5475
+ import * as path45 from "path";
4949
5476
  function createBlueprintCommand() {
4950
- return new Command52("blueprint").description("Generate a self-contained, interactive blueprint of the codebase").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory", "docs/blueprint").action(async (projectPath, options) => {
5477
+ return new Command53("blueprint").description("Generate a self-contained, interactive blueprint of the codebase").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory", "docs/blueprint").action(async (projectPath, options) => {
4951
5478
  try {
4952
- const rootDir = path42.resolve(projectPath);
4953
- const outputDir = path42.resolve(options.output);
5479
+ const rootDir = path45.resolve(projectPath);
5480
+ const outputDir = path45.resolve(options.output);
4954
5481
  logger.info(`Scanning project at ${rootDir}...`);
4955
5482
  const scanner = new ProjectScanner(rootDir);
4956
5483
  const data = await scanner.scan();
4957
5484
  logger.info(`Generating blueprint to ${outputDir}...`);
4958
5485
  const generator = new BlueprintGenerator();
4959
5486
  await generator.generate(data, { outputDir });
4960
- logger.success(`Blueprint generated successfully at ${path42.join(outputDir, "index.html")}`);
5487
+ logger.success(`Blueprint generated successfully at ${path45.join(outputDir, "index.html")}`);
4961
5488
  } catch (error) {
4962
5489
  logger.error(
4963
5490
  `Failed to generate blueprint: ${error instanceof Error ? error.message : String(error)}`
@@ -4968,16 +5495,16 @@ function createBlueprintCommand() {
4968
5495
  }
4969
5496
 
4970
5497
  // src/commands/share.ts
4971
- import { Command as Command53 } from "commander";
4972
- import * as fs24 from "fs";
4973
- import * as path43 from "path";
5498
+ import { Command as Command54 } from "commander";
5499
+ import * as fs27 from "fs";
5500
+ import * as path46 from "path";
4974
5501
  import { parse as parseYaml } from "yaml";
4975
5502
  var MANIFEST_FILENAME = "constraints.yaml";
4976
5503
  function createShareCommand() {
4977
- return new Command53("share").description("Extract and publish a constraints bundle from constraints.yaml").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory for the bundle", ".").action(async (projectPath, options) => {
4978
- const rootDir = path43.resolve(projectPath);
4979
- const manifestPath = path43.join(rootDir, MANIFEST_FILENAME);
4980
- if (!fs24.existsSync(manifestPath)) {
5504
+ return new Command54("share").description("Extract and publish a constraints bundle from constraints.yaml").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory for the bundle", ".").action(async (projectPath, options) => {
5505
+ const rootDir = path46.resolve(projectPath);
5506
+ const manifestPath = path46.join(rootDir, MANIFEST_FILENAME);
5507
+ if (!fs27.existsSync(manifestPath)) {
4981
5508
  logger.error(
4982
5509
  `No ${MANIFEST_FILENAME} found at ${manifestPath}.
4983
5510
  Create a constraints.yaml in your project root to define what to share.`
@@ -4986,7 +5513,7 @@ Create a constraints.yaml in your project root to define what to share.`
4986
5513
  }
4987
5514
  let parsed;
4988
5515
  try {
4989
- const raw = fs24.readFileSync(manifestPath, "utf-8");
5516
+ const raw = fs27.readFileSync(manifestPath, "utf-8");
4990
5517
  parsed = parseYaml(raw);
4991
5518
  } catch (err) {
4992
5519
  logger.error(
@@ -5000,7 +5527,7 @@ Create a constraints.yaml in your project root to define what to share.`
5000
5527
  process.exit(1);
5001
5528
  }
5002
5529
  const manifest = manifestResult.value;
5003
- const configResult = resolveConfig(path43.join(rootDir, "harness.config.json"));
5530
+ const configResult = resolveConfig(path46.join(rootDir, "harness.config.json"));
5004
5531
  if (!configResult.ok) {
5005
5532
  logger.error(configResult.error.message);
5006
5533
  process.exit(1);
@@ -5018,8 +5545,8 @@ Create a constraints.yaml in your project root to define what to share.`
5018
5545
  );
5019
5546
  process.exit(1);
5020
5547
  }
5021
- const outputDir = path43.resolve(options.output);
5022
- const outputPath = path43.join(outputDir, `${manifest.name}.harness-constraints.json`);
5548
+ const outputDir = path46.resolve(options.output);
5549
+ const outputPath = path46.join(outputDir, `${manifest.name}.harness-constraints.json`);
5023
5550
  const writeResult = await writeConfig(outputPath, bundle);
5024
5551
  if (!writeResult.ok) {
5025
5552
  logger.error(`Failed to write bundle: ${writeResult.error.message}`);
@@ -5030,25 +5557,25 @@ Create a constraints.yaml in your project root to define what to share.`
5030
5557
  }
5031
5558
 
5032
5559
  // src/commands/install.ts
5033
- import * as fs26 from "fs";
5034
- import * as path45 from "path";
5035
- import { Command as Command54 } from "commander";
5560
+ import * as fs29 from "fs";
5561
+ import * as path48 from "path";
5562
+ import { Command as Command55 } from "commander";
5036
5563
  import { parse as yamlParse } from "yaml";
5037
5564
 
5038
5565
  // src/registry/tarball.ts
5039
- import * as fs25 from "fs";
5040
- import * as path44 from "path";
5566
+ import * as fs28 from "fs";
5567
+ import * as path47 from "path";
5041
5568
  import * as os6 from "os";
5042
5569
  import { execFileSync as execFileSync5 } from "child_process";
5043
5570
  function extractTarball(tarballBuffer) {
5044
- const tmpDir = fs25.mkdtempSync(path44.join(os6.tmpdir(), "harness-skill-install-"));
5045
- const tarballPath = path44.join(tmpDir, "package.tgz");
5571
+ const tmpDir = fs28.mkdtempSync(path47.join(os6.tmpdir(), "harness-skill-install-"));
5572
+ const tarballPath = path47.join(tmpDir, "package.tgz");
5046
5573
  try {
5047
- fs25.writeFileSync(tarballPath, tarballBuffer);
5574
+ fs28.writeFileSync(tarballPath, tarballBuffer);
5048
5575
  execFileSync5("tar", ["-xzf", tarballPath, "-C", tmpDir], {
5049
5576
  timeout: 3e4
5050
5577
  });
5051
- fs25.unlinkSync(tarballPath);
5578
+ fs28.unlinkSync(tarballPath);
5052
5579
  } catch (err) {
5053
5580
  cleanupTempDir(tmpDir);
5054
5581
  throw new Error(
@@ -5059,37 +5586,37 @@ function extractTarball(tarballBuffer) {
5059
5586
  return tmpDir;
5060
5587
  }
5061
5588
  function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platforms) {
5062
- const files = fs25.readdirSync(extractedPkgDir);
5589
+ const files = fs28.readdirSync(extractedPkgDir);
5063
5590
  for (const platform of platforms) {
5064
- const targetDir = path44.join(communityBaseDir, platform, skillName);
5065
- if (fs25.existsSync(targetDir)) {
5066
- fs25.rmSync(targetDir, { recursive: true, force: true });
5591
+ const targetDir = path47.join(communityBaseDir, platform, skillName);
5592
+ if (fs28.existsSync(targetDir)) {
5593
+ fs28.rmSync(targetDir, { recursive: true, force: true });
5067
5594
  }
5068
- fs25.mkdirSync(targetDir, { recursive: true });
5595
+ fs28.mkdirSync(targetDir, { recursive: true });
5069
5596
  for (const file of files) {
5070
5597
  if (file === "package.json" || file === "node_modules") continue;
5071
- const srcPath = path44.join(extractedPkgDir, file);
5072
- const destPath = path44.join(targetDir, file);
5073
- const stat = fs25.statSync(srcPath);
5598
+ const srcPath = path47.join(extractedPkgDir, file);
5599
+ const destPath = path47.join(targetDir, file);
5600
+ const stat = fs28.statSync(srcPath);
5074
5601
  if (stat.isDirectory()) {
5075
- fs25.cpSync(srcPath, destPath, { recursive: true });
5602
+ fs28.cpSync(srcPath, destPath, { recursive: true });
5076
5603
  } else {
5077
- fs25.copyFileSync(srcPath, destPath);
5604
+ fs28.copyFileSync(srcPath, destPath);
5078
5605
  }
5079
5606
  }
5080
5607
  }
5081
5608
  }
5082
5609
  function removeSkillContent(communityBaseDir, skillName, platforms) {
5083
5610
  for (const platform of platforms) {
5084
- const targetDir = path44.join(communityBaseDir, platform, skillName);
5085
- if (fs25.existsSync(targetDir)) {
5086
- fs25.rmSync(targetDir, { recursive: true, force: true });
5611
+ const targetDir = path47.join(communityBaseDir, platform, skillName);
5612
+ if (fs28.existsSync(targetDir)) {
5613
+ fs28.rmSync(targetDir, { recursive: true, force: true });
5087
5614
  }
5088
5615
  }
5089
5616
  }
5090
5617
  function cleanupTempDir(dirPath) {
5091
5618
  try {
5092
- fs25.rmSync(dirPath, { recursive: true, force: true });
5619
+ fs28.rmSync(dirPath, { recursive: true, force: true });
5093
5620
  } catch {
5094
5621
  }
5095
5622
  }
@@ -5145,35 +5672,35 @@ function validateSkillYaml(parsed) {
5145
5672
  };
5146
5673
  }
5147
5674
  async function runLocalInstall(fromPath, options) {
5148
- const resolvedPath = path45.resolve(fromPath);
5149
- if (!fs26.existsSync(resolvedPath)) {
5675
+ const resolvedPath = path48.resolve(fromPath);
5676
+ if (!fs29.existsSync(resolvedPath)) {
5150
5677
  throw new Error(`--from path does not exist: ${resolvedPath}`);
5151
5678
  }
5152
- const stat = fs26.statSync(resolvedPath);
5679
+ const stat = fs29.statSync(resolvedPath);
5153
5680
  let extractDir = null;
5154
5681
  let pkgDir;
5155
5682
  if (stat.isDirectory()) {
5156
5683
  pkgDir = resolvedPath;
5157
5684
  } else if (resolvedPath.endsWith(".tgz") || resolvedPath.endsWith(".tar.gz")) {
5158
- const tarballBuffer = fs26.readFileSync(resolvedPath);
5685
+ const tarballBuffer = fs29.readFileSync(resolvedPath);
5159
5686
  extractDir = extractTarball(tarballBuffer);
5160
- pkgDir = path45.join(extractDir, "package");
5687
+ pkgDir = path48.join(extractDir, "package");
5161
5688
  } else {
5162
5689
  throw new Error(`--from path must be a directory or .tgz file. Got: ${resolvedPath}`);
5163
5690
  }
5164
5691
  try {
5165
- const skillYamlPath = path45.join(pkgDir, "skill.yaml");
5166
- if (!fs26.existsSync(skillYamlPath)) {
5692
+ const skillYamlPath = path48.join(pkgDir, "skill.yaml");
5693
+ if (!fs29.existsSync(skillYamlPath)) {
5167
5694
  throw new Error(`No skill.yaml found at ${skillYamlPath}`);
5168
5695
  }
5169
- const rawYaml = fs26.readFileSync(skillYamlPath, "utf-8");
5696
+ const rawYaml = fs29.readFileSync(skillYamlPath, "utf-8");
5170
5697
  const parsed = yamlParse(rawYaml);
5171
5698
  const skillYaml = validateSkillYaml(parsed);
5172
5699
  const shortName = skillYaml.name;
5173
5700
  const globalDir = resolveGlobalSkillsDir();
5174
- const skillsDir = path45.dirname(globalDir);
5175
- const communityBase = path45.join(skillsDir, "community");
5176
- const lockfilePath = path45.join(communityBase, "skills-lock.json");
5701
+ const skillsDir = path48.dirname(globalDir);
5702
+ const communityBase = path48.join(skillsDir, "community");
5703
+ const lockfilePath = path48.join(communityBase, "skills-lock.json");
5177
5704
  const bundledNames = getBundledSkillNames(globalDir);
5178
5705
  if (bundledNames.has(shortName)) {
5179
5706
  throw new Error(
@@ -5214,9 +5741,9 @@ async function runInstall(skillName, options) {
5214
5741
  const packageName = resolvePackageName(skillName);
5215
5742
  const shortName = extractSkillName(packageName);
5216
5743
  const globalDir = resolveGlobalSkillsDir();
5217
- const skillsDir = path45.dirname(globalDir);
5218
- const communityBase = path45.join(skillsDir, "community");
5219
- const lockfilePath = path45.join(communityBase, "skills-lock.json");
5744
+ const skillsDir = path48.dirname(globalDir);
5745
+ const communityBase = path48.join(skillsDir, "community");
5746
+ const lockfilePath = path48.join(communityBase, "skills-lock.json");
5220
5747
  const bundledNames = getBundledSkillNames(globalDir);
5221
5748
  if (bundledNames.has(shortName)) {
5222
5749
  throw new Error(
@@ -5242,12 +5769,12 @@ async function runInstall(skillName, options) {
5242
5769
  const extractDir = extractTarball(tarballBuffer);
5243
5770
  let skillYaml;
5244
5771
  try {
5245
- const extractedPkgDir = path45.join(extractDir, "package");
5246
- const skillYamlPath = path45.join(extractedPkgDir, "skill.yaml");
5247
- if (!fs26.existsSync(skillYamlPath)) {
5772
+ const extractedPkgDir = path48.join(extractDir, "package");
5773
+ const skillYamlPath = path48.join(extractedPkgDir, "skill.yaml");
5774
+ if (!fs29.existsSync(skillYamlPath)) {
5248
5775
  throw new Error(`contains invalid skill.yaml: file not found in package`);
5249
5776
  }
5250
- const rawYaml = fs26.readFileSync(skillYamlPath, "utf-8");
5777
+ const rawYaml = fs29.readFileSync(skillYamlPath, "utf-8");
5251
5778
  const parsed = yamlParse(rawYaml);
5252
5779
  skillYaml = validateSkillYaml(parsed);
5253
5780
  placeSkillContent(extractedPkgDir, communityBase, shortName, skillYaml.platforms);
@@ -5286,7 +5813,7 @@ async function runInstall(skillName, options) {
5286
5813
  return result;
5287
5814
  }
5288
5815
  function createInstallCommand() {
5289
- const cmd = new Command54("install");
5816
+ const cmd = new Command55("install");
5290
5817
  cmd.description("Install a community skill from the @harness-skills registry").argument("<skill>", "Skill name or @harness-skills/scoped package name").option("--version <range>", "Semver range or exact version to install").option("--force", "Force reinstall even if same version is already installed").option("--from <path>", "Install from a local directory or .tgz file").option("--registry <url>", "Use a custom npm registry URL").action(async (skill, opts) => {
5291
5818
  try {
5292
5819
  const result = await runInstall(skill, opts);
@@ -5310,15 +5837,15 @@ function createInstallCommand() {
5310
5837
  }
5311
5838
 
5312
5839
  // src/commands/install-constraints.ts
5313
- import * as fs27 from "fs/promises";
5314
- import * as path46 from "path";
5315
- import { Command as Command55 } from "commander";
5840
+ import * as fs30 from "fs/promises";
5841
+ import * as path49 from "path";
5842
+ import { Command as Command56 } from "commander";
5316
5843
  import semver4 from "semver";
5317
5844
  async function runInstallConstraints(options) {
5318
5845
  const { source, configPath, lockfilePath } = options;
5319
5846
  let rawBundle;
5320
5847
  try {
5321
- rawBundle = await fs27.readFile(source, "utf-8");
5848
+ rawBundle = await fs30.readFile(source, "utf-8");
5322
5849
  } catch (err) {
5323
5850
  if (isNodeError(err) && err.code === "ENOENT") {
5324
5851
  return { ok: false, error: `Bundle file not found: ${source}` };
@@ -5361,7 +5888,7 @@ async function runInstallConstraints(options) {
5361
5888
  }
5362
5889
  let localConfig;
5363
5890
  try {
5364
- const raw = await fs27.readFile(configPath, "utf-8");
5891
+ const raw = await fs30.readFile(configPath, "utf-8");
5365
5892
  localConfig = JSON.parse(raw);
5366
5893
  } catch (err) {
5367
5894
  return {
@@ -5509,7 +6036,7 @@ function isNodeError(err) {
5509
6036
  return err instanceof Error && "code" in err;
5510
6037
  }
5511
6038
  function resolveConfigPath(opts) {
5512
- if (opts.config) return path46.resolve(opts.config);
6039
+ if (opts.config) return path49.resolve(opts.config);
5513
6040
  const found = findConfigFile();
5514
6041
  if (!found.ok) {
5515
6042
  logger.error(found.error.message);
@@ -5544,9 +6071,9 @@ function logInstallResult(val, opts) {
5544
6071
  }
5545
6072
  async function handleInstallConstraints(source, opts) {
5546
6073
  const configPath = resolveConfigPath(opts);
5547
- const projectRoot = path46.dirname(configPath);
5548
- const lockfilePath = path46.join(projectRoot, ".harness", "constraints.lock.json");
5549
- const resolvedSource = path46.resolve(source);
6074
+ const projectRoot = path49.dirname(configPath);
6075
+ const lockfilePath = path49.join(projectRoot, ".harness", "constraints.lock.json");
6076
+ const resolvedSource = path49.resolve(source);
5550
6077
  if (opts.forceLocal && opts.forcePackage) {
5551
6078
  logger.error("Cannot use both --force-local and --force-package.");
5552
6079
  process.exit(1);
@@ -5566,15 +6093,15 @@ async function handleInstallConstraints(source, opts) {
5566
6093
  logInstallResult(result.value, opts);
5567
6094
  }
5568
6095
  function createInstallConstraintsCommand() {
5569
- const cmd = new Command55("install-constraints");
6096
+ const cmd = new Command56("install-constraints");
5570
6097
  cmd.description("Install a constraints bundle into the local harness config").argument("<source>", "Path to a .harness-constraints.json bundle file").option("--force-local", "Resolve all conflicts by keeping local values").option("--force-package", "Resolve all conflicts by using package values").option("--dry-run", "Show what would change without writing files").option("-c, --config <path>", "Path to harness.config.json").action(handleInstallConstraints);
5571
6098
  return cmd;
5572
6099
  }
5573
6100
 
5574
6101
  // src/commands/uninstall-constraints.ts
5575
- import * as fs28 from "fs/promises";
5576
- import * as path47 from "path";
5577
- import { Command as Command56 } from "commander";
6102
+ import * as fs31 from "fs/promises";
6103
+ import * as path50 from "path";
6104
+ import { Command as Command57 } from "commander";
5578
6105
  async function runUninstallConstraints(options) {
5579
6106
  const { packageName, configPath, lockfilePath } = options;
5580
6107
  const lockfileResult = await readLockfile(lockfilePath);
@@ -5594,7 +6121,7 @@ async function runUninstallConstraints(options) {
5594
6121
  }
5595
6122
  let localConfig;
5596
6123
  try {
5597
- const raw = await fs28.readFile(configPath, "utf-8");
6124
+ const raw = await fs31.readFile(configPath, "utf-8");
5598
6125
  localConfig = JSON.parse(raw);
5599
6126
  } catch (err) {
5600
6127
  return {
@@ -5631,11 +6158,11 @@ async function runUninstallConstraints(options) {
5631
6158
  };
5632
6159
  }
5633
6160
  function createUninstallConstraintsCommand() {
5634
- const cmd = new Command56("uninstall-constraints");
6161
+ const cmd = new Command57("uninstall-constraints");
5635
6162
  cmd.description("Remove a previously installed constraints package").argument("<name>", "Name of the constraint package to uninstall").option("-c, --config <path>", "Path to harness.config.json").action(async (name, opts) => {
5636
6163
  let configPath;
5637
6164
  if (opts.config) {
5638
- configPath = path47.resolve(opts.config);
6165
+ configPath = path50.resolve(opts.config);
5639
6166
  } else {
5640
6167
  const found = findConfigFile();
5641
6168
  if (!found.ok) {
@@ -5644,8 +6171,8 @@ function createUninstallConstraintsCommand() {
5644
6171
  }
5645
6172
  configPath = found.value;
5646
6173
  }
5647
- const projectRoot = path47.dirname(configPath);
5648
- const lockfilePath = path47.join(projectRoot, ".harness", "constraints.lock.json");
6174
+ const projectRoot = path50.dirname(configPath);
6175
+ const lockfilePath = path50.join(projectRoot, ".harness", "constraints.lock.json");
5649
6176
  const result = await runUninstallConstraints({
5650
6177
  packageName: name,
5651
6178
  configPath,
@@ -5670,15 +6197,15 @@ function createUninstallConstraintsCommand() {
5670
6197
  }
5671
6198
 
5672
6199
  // src/commands/uninstall.ts
5673
- import * as path48 from "path";
5674
- import { Command as Command57 } from "commander";
6200
+ import * as path51 from "path";
6201
+ import { Command as Command58 } from "commander";
5675
6202
  async function runUninstall(skillName, options) {
5676
6203
  const packageName = resolvePackageName(skillName);
5677
6204
  const shortName = extractSkillName(packageName);
5678
6205
  const globalDir = resolveGlobalSkillsDir();
5679
- const skillsDir = path48.dirname(globalDir);
5680
- const communityBase = path48.join(skillsDir, "community");
5681
- const lockfilePath = path48.join(communityBase, "skills-lock.json");
6206
+ const skillsDir = path51.dirname(globalDir);
6207
+ const communityBase = path51.join(skillsDir, "community");
6208
+ const lockfilePath = path51.join(communityBase, "skills-lock.json");
5682
6209
  const lockfile = readLockfile2(lockfilePath);
5683
6210
  const entry = lockfile.skills[packageName];
5684
6211
  if (!entry) {
@@ -5708,7 +6235,7 @@ async function runUninstall(skillName, options) {
5708
6235
  return result;
5709
6236
  }
5710
6237
  function createUninstallCommand() {
5711
- const cmd = new Command57("uninstall");
6238
+ const cmd = new Command58("uninstall");
5712
6239
  cmd.description("Uninstall a community skill").argument("<skill>", "Skill name or @harness-skills/scoped package name").option("--force", "Remove even if other skills depend on this one").action(async (skill, opts) => {
5713
6240
  try {
5714
6241
  const result = await runUninstall(skill, opts);
@@ -5727,13 +6254,13 @@ function createUninstallCommand() {
5727
6254
  }
5728
6255
 
5729
6256
  // src/commands/orchestrator.ts
5730
- import { Command as Command58 } from "commander";
5731
- import * as path49 from "path";
6257
+ import { Command as Command59 } from "commander";
6258
+ import * as path52 from "path";
5732
6259
  import { Orchestrator, WorkflowLoader, launchTUI } from "@harness-engineering/orchestrator";
5733
6260
  function createOrchestratorCommand() {
5734
- const orchestrator = new Command58("orchestrator");
6261
+ const orchestrator = new Command59("orchestrator");
5735
6262
  orchestrator.command("run").description("Run the orchestrator daemon").option("-w, --workflow <path>", "Path to WORKFLOW.md", "WORKFLOW.md").action(async (opts) => {
5736
- const workflowPath = path49.resolve(process.cwd(), opts.workflow);
6263
+ const workflowPath = path52.resolve(process.cwd(), opts.workflow);
5737
6264
  const loader = new WorkflowLoader();
5738
6265
  const result = await loader.loadWorkflow(workflowPath);
5739
6266
  if (!result.ok) {
@@ -5757,13 +6284,13 @@ function createOrchestratorCommand() {
5757
6284
  }
5758
6285
 
5759
6286
  // src/commands/learnings/index.ts
5760
- import { Command as Command60 } from "commander";
6287
+ import { Command as Command61 } from "commander";
5761
6288
 
5762
6289
  // src/commands/learnings/prune.ts
5763
- import { Command as Command59 } from "commander";
5764
- import * as path50 from "path";
6290
+ import { Command as Command60 } from "commander";
6291
+ import * as path53 from "path";
5765
6292
  async function handlePrune(opts) {
5766
- const projectPath = path50.resolve(opts.path);
6293
+ const projectPath = path53.resolve(opts.path);
5767
6294
  const result = await pruneLearnings(projectPath, opts.stream);
5768
6295
  if (!result.ok) {
5769
6296
  logger.error(result.error.message);
@@ -5802,21 +6329,666 @@ function printPatternProposals(patterns) {
5802
6329
  );
5803
6330
  }
5804
6331
  function createPruneCommand() {
5805
- return new Command59("prune").description(
6332
+ return new Command60("prune").description(
5806
6333
  "Analyze global learnings for patterns, present improvement proposals, and archive old entries"
5807
6334
  ).option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(handlePrune);
5808
6335
  }
5809
6336
 
5810
6337
  // src/commands/learnings/index.ts
5811
6338
  function createLearningsCommand() {
5812
- const command = new Command60("learnings").description("Learnings management commands");
6339
+ const command = new Command61("learnings").description("Learnings management commands");
5813
6340
  command.addCommand(createPruneCommand());
5814
6341
  return command;
5815
6342
  }
5816
6343
 
6344
+ // src/commands/integrations/index.ts
6345
+ import { Command as Command66 } from "commander";
6346
+
6347
+ // src/commands/integrations/add.ts
6348
+ import { Command as Command62 } from "commander";
6349
+ import * as fs32 from "fs";
6350
+ import * as path54 from "path";
6351
+ import chalk6 from "chalk";
6352
+ function addIntegration(cwd, name) {
6353
+ const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
6354
+ if (!def) {
6355
+ return Err(
6356
+ new CLIError(
6357
+ `Integration '${name}' not found in registry. Run 'harness integrations list' to see available integrations.`,
6358
+ ExitCode.ERROR
6359
+ )
6360
+ );
6361
+ }
6362
+ if (def.tier === 0) {
6363
+ return Err(
6364
+ new CLIError(
6365
+ `${def.displayName} is a Tier 0 integration, already configured by 'harness setup'. Run 'harness setup' if missing.`,
6366
+ ExitCode.ERROR
6367
+ )
6368
+ );
6369
+ }
6370
+ const mcpPath = path54.join(cwd, ".mcp.json");
6371
+ const mcpEntry = {
6372
+ command: def.mcpConfig.command
6373
+ };
6374
+ if (def.mcpConfig.args.length > 0) mcpEntry.args = def.mcpConfig.args;
6375
+ if (def.mcpConfig.env) mcpEntry.env = def.mcpConfig.env;
6376
+ writeMcpEntry(mcpPath, def.name, mcpEntry);
6377
+ const geminiDir = path54.join(cwd, ".gemini");
6378
+ if (fs32.existsSync(geminiDir)) {
6379
+ const geminiPath = path54.join(geminiDir, "settings.json");
6380
+ writeMcpEntry(geminiPath, def.name, mcpEntry);
6381
+ }
6382
+ const configPath = path54.join(cwd, "harness.config.json");
6383
+ const integConfig = readIntegrationsConfig(configPath);
6384
+ if (!integConfig.enabled.includes(def.name)) {
6385
+ integConfig.enabled.push(def.name);
6386
+ }
6387
+ integConfig.dismissed = integConfig.dismissed.filter((d) => d !== def.name);
6388
+ writeIntegrationsConfig(configPath, integConfig);
6389
+ const envVarMissing = !!def.envVar && !process.env[def.envVar];
6390
+ return Ok({
6391
+ name: def.name,
6392
+ displayName: def.displayName,
6393
+ envVarMissing,
6394
+ ...def.envVar !== void 0 && { envVar: def.envVar },
6395
+ ...def.installHint !== void 0 && { installHint: def.installHint }
6396
+ });
6397
+ }
6398
+ function createAddIntegrationCommand() {
6399
+ return new Command62("add").description("Enable an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
6400
+ const globalOpts = cmd.optsWithGlobals();
6401
+ const cwd = process.cwd();
6402
+ const result = addIntegration(cwd, name);
6403
+ if (!result.ok) {
6404
+ if (!globalOpts.quiet) {
6405
+ logger.error(result.error.message);
6406
+ }
6407
+ process.exit(result.error.exitCode);
6408
+ return;
6409
+ }
6410
+ const { displayName, envVarMissing, envVar, installHint } = result.value;
6411
+ if (!globalOpts.quiet) {
6412
+ console.log("");
6413
+ logger.success(`${displayName} integration enabled.`);
6414
+ console.log("");
6415
+ if (envVarMissing && envVar) {
6416
+ logger.warn(`Set ${chalk6.bold(envVar)} in your environment to activate.`);
6417
+ if (installHint) {
6418
+ console.log(` ${chalk6.dim(installHint)}`);
6419
+ }
6420
+ console.log("");
6421
+ }
6422
+ }
6423
+ process.exit(ExitCode.SUCCESS);
6424
+ });
6425
+ }
6426
+
6427
+ // src/commands/integrations/list.ts
6428
+ import { Command as Command63 } from "commander";
6429
+ import * as path55 from "path";
6430
+ import chalk7 from "chalk";
6431
+ function createListIntegrationsCommand() {
6432
+ return new Command63("list").description("Show all MCP integrations with status").action(async (_opts, cmd) => {
6433
+ const globalOpts = cmd.optsWithGlobals();
6434
+ const cwd = process.cwd();
6435
+ const mcpPath = path55.join(cwd, ".mcp.json");
6436
+ const configPath = path55.join(cwd, "harness.config.json");
6437
+ const mcpConfig = readMcpConfig(mcpPath);
6438
+ const integConfig = readIntegrationsConfig(configPath);
6439
+ const mcpServers = mcpConfig.mcpServers ?? {};
6440
+ const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
6441
+ const tier1 = INTEGRATION_REGISTRY.filter((i) => i.tier === 1);
6442
+ if (globalOpts.json) {
6443
+ const entries = INTEGRATION_REGISTRY.map((i) => ({
6444
+ name: i.name,
6445
+ tier: i.tier,
6446
+ configured: i.name in mcpServers,
6447
+ enabled: integConfig.enabled.includes(i.name),
6448
+ dismissed: integConfig.dismissed.includes(i.name),
6449
+ envVar: i.envVar ?? null,
6450
+ envVarSet: i.envVar ? !!process.env[i.envVar] : null
6451
+ }));
6452
+ console.log(JSON.stringify(entries, null, 2));
6453
+ process.exit(ExitCode.SUCCESS);
6454
+ return;
6455
+ }
6456
+ console.log("");
6457
+ console.log("MCP Integrations:");
6458
+ console.log("");
6459
+ console.log(" Tier 0 (zero-config):");
6460
+ for (const i of tier0) {
6461
+ const configured = i.name in mcpServers;
6462
+ const icon = configured ? chalk7.green("\u2713") : chalk7.dim("\u25CB");
6463
+ console.log(` ${icon} ${i.name.padEnd(22)} ${i.description}`);
6464
+ }
6465
+ console.log("");
6466
+ console.log(" Tier 1 (API key required):");
6467
+ for (const i of tier1) {
6468
+ const configured = i.name in mcpServers;
6469
+ const dismissed = integConfig.dismissed.includes(i.name);
6470
+ const icon = configured ? chalk7.green("\u2713") : chalk7.dim("\u25CB");
6471
+ let suffix = "";
6472
+ if (dismissed) {
6473
+ suffix = chalk7.dim("[dismissed]");
6474
+ } else if (i.envVar) {
6475
+ const envSet = !!process.env[i.envVar];
6476
+ suffix = `${i.envVar} ${envSet ? chalk7.green("\u2713") : chalk7.yellow("not set")}`;
6477
+ }
6478
+ console.log(` ${icon} ${i.name.padEnd(22)} ${i.description.padEnd(35)} ${suffix}`);
6479
+ }
6480
+ console.log("");
6481
+ console.log(
6482
+ ` Run '${chalk7.cyan("harness integrations add <name>")}' to enable a Tier 1 integration.`
6483
+ );
6484
+ console.log("");
6485
+ process.exit(ExitCode.SUCCESS);
6486
+ });
6487
+ }
6488
+
6489
+ // src/commands/integrations/remove.ts
6490
+ import { Command as Command64 } from "commander";
6491
+ import * as fs33 from "fs";
6492
+ import * as path56 from "path";
6493
+ function removeIntegration(cwd, name) {
6494
+ const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
6495
+ if (!def) {
6496
+ return Err(
6497
+ new CLIError(
6498
+ `Integration '${name}' not found in registry. Run 'harness integrations list' to see available integrations.`,
6499
+ ExitCode.ERROR
6500
+ )
6501
+ );
6502
+ }
6503
+ const mcpPath = path56.join(cwd, ".mcp.json");
6504
+ removeMcpEntry(mcpPath, def.name);
6505
+ const geminiDir = path56.join(cwd, ".gemini");
6506
+ if (fs33.existsSync(geminiDir)) {
6507
+ const geminiPath = path56.join(geminiDir, "settings.json");
6508
+ removeMcpEntry(geminiPath, def.name);
6509
+ }
6510
+ const configPath = path56.join(cwd, "harness.config.json");
6511
+ const integConfig = readIntegrationsConfig(configPath);
6512
+ integConfig.enabled = integConfig.enabled.filter((e) => e !== def.name);
6513
+ writeIntegrationsConfig(configPath, integConfig);
6514
+ return Ok(def.displayName);
6515
+ }
6516
+ function createRemoveIntegrationCommand() {
6517
+ return new Command64("remove").description("Remove an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
6518
+ const globalOpts = cmd.optsWithGlobals();
6519
+ const cwd = process.cwd();
6520
+ const result = removeIntegration(cwd, name);
6521
+ if (!result.ok) {
6522
+ if (!globalOpts.quiet) {
6523
+ logger.error(result.error.message);
6524
+ }
6525
+ process.exit(result.error.exitCode);
6526
+ return;
6527
+ }
6528
+ if (!globalOpts.quiet) {
6529
+ console.log("");
6530
+ logger.success(`${result.value} integration removed.`);
6531
+ console.log("");
6532
+ }
6533
+ process.exit(ExitCode.SUCCESS);
6534
+ });
6535
+ }
6536
+
6537
+ // src/commands/integrations/dismiss.ts
6538
+ import { Command as Command65 } from "commander";
6539
+ import * as path57 from "path";
6540
+ function dismissIntegration(cwd, name) {
6541
+ const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
6542
+ if (!def) {
6543
+ return Err(
6544
+ new CLIError(
6545
+ `Integration '${name}' not found in registry. Run 'harness integrations list' to see available integrations.`,
6546
+ ExitCode.ERROR
6547
+ )
6548
+ );
6549
+ }
6550
+ const configPath = path57.join(cwd, "harness.config.json");
6551
+ const integConfig = readIntegrationsConfig(configPath);
6552
+ if (!integConfig.dismissed.includes(def.name)) {
6553
+ integConfig.dismissed.push(def.name);
6554
+ }
6555
+ integConfig.enabled = integConfig.enabled.filter((e) => e !== def.name);
6556
+ writeIntegrationsConfig(configPath, integConfig);
6557
+ return Ok(def.displayName);
6558
+ }
6559
+ function createDismissIntegrationCommand() {
6560
+ return new Command65("dismiss").description("Suppress doctor recommendations for an integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
6561
+ const globalOpts = cmd.optsWithGlobals();
6562
+ const cwd = process.cwd();
6563
+ const result = dismissIntegration(cwd, name);
6564
+ if (!result.ok) {
6565
+ if (!globalOpts.quiet) {
6566
+ logger.error(result.error.message);
6567
+ }
6568
+ process.exit(result.error.exitCode);
6569
+ return;
6570
+ }
6571
+ if (!globalOpts.quiet) {
6572
+ console.log("");
6573
+ logger.info(
6574
+ `${result.value} dismissed. It will no longer appear in 'harness doctor' suggestions.`
6575
+ );
6576
+ console.log("");
6577
+ }
6578
+ process.exit(ExitCode.SUCCESS);
6579
+ });
6580
+ }
6581
+
6582
+ // src/commands/integrations/index.ts
6583
+ function createIntegrationsCommand() {
6584
+ const command = new Command66("integrations").description(
6585
+ "Manage MCP peer integrations (add, list, remove, dismiss)"
6586
+ );
6587
+ command.addCommand(createListIntegrationsCommand());
6588
+ command.addCommand(createAddIntegrationCommand());
6589
+ command.addCommand(createRemoveIntegrationCommand());
6590
+ command.addCommand(createDismissIntegrationCommand());
6591
+ return command;
6592
+ }
6593
+
6594
+ // src/commands/usage.ts
6595
+ import { Command as Command67 } from "commander";
6596
+ async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
6597
+ const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-4LPXJYVZ.js");
6598
+ const records = readCostRecords(cwd);
6599
+ if (includeClaudeSessions) {
6600
+ const ccRecords = parseCCRecords();
6601
+ records.push(...ccRecords);
6602
+ }
6603
+ if (records.length === 0) return records;
6604
+ const pricingData = await loadPricingData(cwd);
6605
+ for (const record of records) {
6606
+ if (record.model && record.costMicroUSD == null) {
6607
+ const cost = calculateCost(record, pricingData);
6608
+ if (cost != null) record.costMicroUSD = cost;
6609
+ }
6610
+ }
6611
+ return records;
6612
+ }
6613
+ function formatMicroUSD(microUSD) {
6614
+ if (microUSD == null) return "N/A";
6615
+ return "$" + (microUSD / 1e6).toFixed(4);
6616
+ }
6617
+ function formatTokenCount(count) {
6618
+ if (count >= 1e6) return (count / 1e6).toFixed(1) + "M";
6619
+ if (count >= 1e3) return (count / 1e3).toFixed(1) + "K";
6620
+ return String(count);
6621
+ }
6622
+ function formatModels(models) {
6623
+ if (models.length === 0) return "unknown";
6624
+ if (models.length === 1) return models[0] ?? "unknown";
6625
+ return `${models[0] ?? "unknown"} and ${models.length - 1} other${models.length - 1 > 1 ? "s" : ""}`;
6626
+ }
6627
+ function registerDailyCommand(usage) {
6628
+ usage.command("daily").description("Show per-day token usage and cost").option("--days <n>", "Number of days to show (default: 7, max: 90)", "7").action(async (opts, cmd) => {
6629
+ const globalOpts = cmd.optsWithGlobals();
6630
+ const days = Math.min(Math.max(parseInt(opts.days, 10) || 7, 1), 90);
6631
+ const cwd = process.cwd();
6632
+ const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
6633
+ if (records.length === 0) {
6634
+ if (globalOpts.json) {
6635
+ console.log(JSON.stringify([]));
6636
+ } else {
6637
+ logger.info("No usage data found. Run some harness sessions first.");
6638
+ }
6639
+ return;
6640
+ }
6641
+ const { aggregateByDay } = await import("./dist-4LPXJYVZ.js");
6642
+ const dailyData = aggregateByDay(records);
6643
+ const limited = dailyData.slice(0, days);
6644
+ if (globalOpts.json) {
6645
+ console.log(JSON.stringify(limited, null, 2));
6646
+ return;
6647
+ }
6648
+ const header = "Date | Sessions | Input | Output | Model(s) | Cost";
6649
+ const divider = "-------------|----------|-----------|-----------|------------------------------|--------";
6650
+ logger.info(header);
6651
+ logger.info(divider);
6652
+ for (const day of limited) {
6653
+ const date = day.date.padEnd(12);
6654
+ const sessions = String(day.sessionCount).padStart(8);
6655
+ const input = formatTokenCount(day.tokens.inputTokens).padStart(9);
6656
+ const output = formatTokenCount(day.tokens.outputTokens).padStart(9);
6657
+ const models = formatModels(day.models).padEnd(28);
6658
+ const cost = formatMicroUSD(day.costMicroUSD);
6659
+ logger.info(`${date} | ${sessions} | ${input} | ${output} | ${models} | ${cost}`);
6660
+ }
6661
+ });
6662
+ }
6663
+ function registerSessionsCommand(usage) {
6664
+ usage.command("sessions").description("List recent sessions with token usage and cost").option("--limit <n>", "Number of sessions to show (default: 10, max: 100)", "10").action(async (opts, cmd) => {
6665
+ const globalOpts = cmd.optsWithGlobals();
6666
+ const limit = Math.min(Math.max(parseInt(opts.limit, 10) || 10, 1), 100);
6667
+ const cwd = process.cwd();
6668
+ const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
6669
+ if (records.length === 0) {
6670
+ if (globalOpts.json) {
6671
+ console.log(JSON.stringify([]));
6672
+ } else {
6673
+ logger.info("No usage data found. Run some harness sessions first.");
6674
+ }
6675
+ return;
6676
+ }
6677
+ const { aggregateBySession } = await import("./dist-4LPXJYVZ.js");
6678
+ const sessionData = aggregateBySession(records);
6679
+ const limited = sessionData.slice(0, limit);
6680
+ if (globalOpts.json) {
6681
+ console.log(JSON.stringify(limited, null, 2));
6682
+ return;
6683
+ }
6684
+ const header = "Session ID | Started | Duration | Tokens | Model | Cost";
6685
+ const divider = "---------------------|----------------------|-----------|-----------|----------------------|--------";
6686
+ logger.info(header);
6687
+ logger.info(divider);
6688
+ for (const s of limited) {
6689
+ const id = s.sessionId.slice(0, 20).padEnd(20);
6690
+ const started = s.firstTimestamp.slice(0, 19).padEnd(20);
6691
+ const durationMs = new Date(s.lastTimestamp).getTime() - new Date(s.firstTimestamp).getTime();
6692
+ const durationMin = Math.max(1, Math.round(durationMs / 6e4));
6693
+ const duration = `${durationMin}m`.padStart(9);
6694
+ const tokens = formatTokenCount(s.tokens.totalTokens).padStart(9);
6695
+ const model = (s.model ?? "unknown").slice(0, 20).padEnd(20);
6696
+ const cost = formatMicroUSD(s.costMicroUSD);
6697
+ logger.info(`${id} | ${started} | ${duration} | ${tokens} | ${model} | ${cost}`);
6698
+ }
6699
+ });
6700
+ }
6701
+ function registerSessionCommand(usage) {
6702
+ usage.command("session <id>").description("Show detailed token breakdown for a specific session").action(async (id, _opts, cmd) => {
6703
+ const globalOpts = cmd.optsWithGlobals();
6704
+ const cwd = process.cwd();
6705
+ const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
6706
+ const { aggregateBySession } = await import("./dist-4LPXJYVZ.js");
6707
+ const sessionData = aggregateBySession(records);
6708
+ const match = sessionData.find((s) => s.sessionId === id);
6709
+ if (!match) {
6710
+ const fuzzy = sessionData.filter(
6711
+ (s) => s.sessionId.includes(id) || id.includes(s.sessionId.slice(0, 8))
6712
+ );
6713
+ const errMsg = `Session "${id}" not found.`;
6714
+ if (fuzzy.length > 0) {
6715
+ const suggestions = fuzzy.slice(0, 3).map((s) => s.sessionId);
6716
+ if (globalOpts.json) {
6717
+ console.log(JSON.stringify({ error: errMsg, suggestions }));
6718
+ } else {
6719
+ logger.error(errMsg);
6720
+ logger.info("Did you mean:");
6721
+ for (const s of suggestions) {
6722
+ logger.info(` ${s}`);
6723
+ }
6724
+ }
6725
+ } else {
6726
+ if (globalOpts.json) {
6727
+ console.log(JSON.stringify({ error: errMsg, suggestions: [] }));
6728
+ } else {
6729
+ logger.error(errMsg);
6730
+ }
6731
+ }
6732
+ process.exitCode = 1;
6733
+ return;
6734
+ }
6735
+ if (globalOpts.json) {
6736
+ console.log(JSON.stringify(match, null, 2));
6737
+ return;
6738
+ }
6739
+ logger.info(`Session: ${match.sessionId}`);
6740
+ logger.info(`Started: ${match.firstTimestamp}`);
6741
+ logger.info(`Ended: ${match.lastTimestamp}`);
6742
+ logger.info(`Model: ${match.model ?? "unknown"}`);
6743
+ logger.info(`Source: ${match.source}`);
6744
+ logger.info("");
6745
+ logger.info("Token Breakdown:");
6746
+ logger.info(` Input tokens: ${formatTokenCount(match.tokens.inputTokens)}`);
6747
+ logger.info(` Output tokens: ${formatTokenCount(match.tokens.outputTokens)}`);
6748
+ logger.info(` Total tokens: ${formatTokenCount(match.tokens.totalTokens)}`);
6749
+ if (match.cacheReadTokens != null) {
6750
+ logger.info(` Cache read tokens: ${formatTokenCount(match.cacheReadTokens)}`);
6751
+ }
6752
+ if (match.cacheCreationTokens != null) {
6753
+ logger.info(` Cache creation tokens: ${formatTokenCount(match.cacheCreationTokens)}`);
6754
+ }
6755
+ logger.info("");
6756
+ logger.info(`Cost: ${formatMicroUSD(match.costMicroUSD)}`);
6757
+ });
6758
+ }
6759
+ function registerLatestCommand(usage) {
6760
+ usage.command("latest").description("Show the most recently completed session cost summary").action(async (_opts, cmd) => {
6761
+ const globalOpts = cmd.optsWithGlobals();
6762
+ const cwd = process.cwd();
6763
+ const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
6764
+ if (records.length === 0) {
6765
+ if (globalOpts.json) {
6766
+ console.log(JSON.stringify({ error: "No usage data found" }));
6767
+ } else {
6768
+ logger.info("No usage data found. Run some harness sessions first.");
6769
+ }
6770
+ return;
6771
+ }
6772
+ const { aggregateBySession } = await import("./dist-4LPXJYVZ.js");
6773
+ const sessionData = aggregateBySession(records);
6774
+ const latest = sessionData[0];
6775
+ if (!latest) {
6776
+ if (globalOpts.json) {
6777
+ console.log(JSON.stringify({ error: "No session data found" }));
6778
+ } else {
6779
+ logger.info("No session data found.");
6780
+ }
6781
+ return;
6782
+ }
6783
+ if (globalOpts.json) {
6784
+ console.log(JSON.stringify(latest, null, 2));
6785
+ return;
6786
+ }
6787
+ logger.info(`Session: ${latest.sessionId}`);
6788
+ logger.info(`Started: ${latest.firstTimestamp}`);
6789
+ logger.info(`Ended: ${latest.lastTimestamp}`);
6790
+ logger.info(`Model: ${latest.model ?? "unknown"}`);
6791
+ logger.info(
6792
+ `Tokens: ${formatTokenCount(latest.tokens.totalTokens)} (${formatTokenCount(latest.tokens.inputTokens)} in / ${formatTokenCount(latest.tokens.outputTokens)} out)`
6793
+ );
6794
+ logger.info(`Cost: ${formatMicroUSD(latest.costMicroUSD)}`);
6795
+ });
6796
+ }
6797
+ function createUsageCommand() {
6798
+ const usage = new Command67("usage").description("Token usage and cost tracking");
6799
+ usage.option(
6800
+ "--include-claude-sessions",
6801
+ "Include Claude Code session data from ~/.claude/projects/"
6802
+ );
6803
+ registerDailyCommand(usage);
6804
+ registerSessionsCommand(usage);
6805
+ registerSessionCommand(usage);
6806
+ registerLatestCommand(usage);
6807
+ return usage;
6808
+ }
6809
+
6810
+ // src/commands/taint.ts
6811
+ import { Command as Command68 } from "commander";
6812
+ function getProjectRoot() {
6813
+ return process.cwd();
6814
+ }
6815
+ function registerClearCommand(taint) {
6816
+ taint.command("clear [sessionId]").description(
6817
+ "Clear session taint \u2014 removes taint file(s) and re-enables destructive operations"
6818
+ ).action((sessionId) => {
6819
+ const projectRoot = getProjectRoot();
6820
+ const count = clearTaint(projectRoot, sessionId);
6821
+ if (count === 0) {
6822
+ if (sessionId) {
6823
+ logger.info(`No taint found for session "${sessionId}".`);
6824
+ } else {
6825
+ logger.info("No active taint files found.");
6826
+ }
6827
+ } else if (sessionId) {
6828
+ logger.info(
6829
+ `Sentinel: taint cleared for session "${sessionId}". Destructive operations re-enabled.`
6830
+ );
6831
+ } else {
6832
+ logger.info(
6833
+ `Sentinel: cleared ${count} taint file${count === 1 ? "" : "s"}. Destructive operations re-enabled.`
6834
+ );
6835
+ }
6836
+ });
6837
+ }
6838
+ function registerStatusCommand(taint) {
6839
+ taint.command("status [sessionId]").description("Show current taint status for a session or all sessions").action((sessionId) => {
6840
+ const projectRoot = getProjectRoot();
6841
+ if (sessionId) {
6842
+ const result = checkTaint(projectRoot, sessionId);
6843
+ if (result.expired) {
6844
+ logger.info(`Session "${sessionId}": taint expired (cleared).`);
6845
+ } else if (result.tainted && result.state) {
6846
+ const state = result.state;
6847
+ const expiresAt = new Date(state.expiresAt);
6848
+ const remaining = Math.max(0, Math.round((expiresAt.getTime() - Date.now()) / 1e3 / 60));
6849
+ logger.info(`Session "${sessionId}": TAINTED (${state.severity})`);
6850
+ logger.info(` Reason: ${state.reason}`);
6851
+ logger.info(` Expires in: ~${remaining} minute${remaining === 1 ? "" : "s"}`);
6852
+ logger.info(` Findings: ${state.findings.length}`);
6853
+ } else {
6854
+ logger.info(`Session "${sessionId}": clean (no taint).`);
6855
+ }
6856
+ } else {
6857
+ const sessions = listTaintedSessions(projectRoot);
6858
+ if (sessions.length === 0) {
6859
+ logger.info("No active taint sessions.");
6860
+ } else {
6861
+ logger.info(`Active taint sessions (${sessions.length}):`);
6862
+ for (const id of sessions) {
6863
+ const result = checkTaint(projectRoot, id);
6864
+ if (result.tainted && result.state) {
6865
+ const remaining = Math.max(
6866
+ 0,
6867
+ Math.round((new Date(result.state.expiresAt).getTime() - Date.now()) / 1e3 / 60)
6868
+ );
6869
+ logger.info(` ${id}: ${result.state.severity} \u2014 expires in ~${remaining}m`);
6870
+ }
6871
+ }
6872
+ }
6873
+ }
6874
+ });
6875
+ }
6876
+ function createTaintCommand() {
6877
+ const taint = new Command68("taint").description("Manage sentinel session taint state");
6878
+ registerClearCommand(taint);
6879
+ registerStatusCommand(taint);
6880
+ return taint;
6881
+ }
6882
+
6883
+ // src/commands/scan-config.ts
6884
+ import { Command as Command69 } from "commander";
6885
+ import { existsSync as existsSync30, readFileSync as readFileSync18, writeFileSync as writeFileSync15 } from "fs";
6886
+ import { join as join44, relative as relative2 } from "path";
6887
+ var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
6888
+ function stripHighSeverityPatterns(content, injectionFindings) {
6889
+ const highLines = /* @__PURE__ */ new Set();
6890
+ for (const f of injectionFindings) {
6891
+ if (f.severity === "high" && f.line !== void 0) {
6892
+ highLines.add(f.line);
6893
+ }
6894
+ }
6895
+ if (highLines.size === 0) return { cleaned: content, linesStripped: 0 };
6896
+ const lines = content.split("\n");
6897
+ let linesStripped = 0;
6898
+ for (const lineNum of highLines) {
6899
+ const idx = lineNum - 1;
6900
+ if (idx >= 0 && idx < lines.length) {
6901
+ lines[idx] = "";
6902
+ linesStripped++;
6903
+ }
6904
+ }
6905
+ return { cleaned: lines.join("\n"), linesStripped };
6906
+ }
6907
+ function applyFix(filePath, targetDir, content, injectionFindings) {
6908
+ const hasHighSeverity = injectionFindings.some((f) => f.severity === "high");
6909
+ if (!hasHighSeverity) return;
6910
+ const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
6911
+ if (linesStripped > 0) {
6912
+ writeFileSync15(filePath, cleaned);
6913
+ logger.info(
6914
+ `scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative2(targetDir, filePath).replaceAll("\\", "/")}`
6915
+ );
6916
+ }
6917
+ }
6918
+ function scanSingleFile(filePath, targetDir, scanner, options) {
6919
+ if (!existsSync30(filePath)) return null;
6920
+ let content;
6921
+ try {
6922
+ content = readFileSync18(filePath, "utf8");
6923
+ } catch {
6924
+ return null;
6925
+ }
6926
+ const injectionFindings = scanForInjection(content);
6927
+ const findings = mapInjectionFindings(injectionFindings);
6928
+ const secFindings = scanner.scanContent(content, filePath);
6929
+ findings.push(...mapSecurityFindings(secFindings, findings));
6930
+ if (options.fix) {
6931
+ applyFix(filePath, targetDir, content, injectionFindings);
6932
+ }
6933
+ return {
6934
+ file: relative2(targetDir, filePath).replaceAll("\\", "/"),
6935
+ findings,
6936
+ overallSeverity: computeOverallSeverity(findings)
6937
+ };
6938
+ }
6939
+ async function runScanConfig(targetDir, options) {
6940
+ const scanner = new SecurityScanner(parseSecurityConfig({}));
6941
+ const results = [];
6942
+ for (const configFile of CONFIG_FILES) {
6943
+ const result = scanSingleFile(join44(targetDir, configFile), targetDir, scanner, options);
6944
+ if (result) results.push(result);
6945
+ }
6946
+ return { exitCode: computeScanExitCode(results), results };
6947
+ }
6948
+ function formatTextOutput(result) {
6949
+ if (result.results.length === 0) {
6950
+ logger.info("scan-config: no config files found to scan.");
6951
+ return;
6952
+ }
6953
+ for (const fileResult of result.results) {
6954
+ if (fileResult.findings.length === 0) {
6955
+ logger.info(`${fileResult.file}: clean`);
6956
+ continue;
6957
+ }
6958
+ logger.info(
6959
+ `${fileResult.file}: ${fileResult.overallSeverity} (${fileResult.findings.length} finding(s))`
6960
+ );
6961
+ for (const f of fileResult.findings) {
6962
+ const lineInfo = f.line ? ` (line ${f.line})` : "";
6963
+ logger.info(` [${f.ruleId}] ${f.severity.toUpperCase()}: ${f.message}${lineInfo}`);
6964
+ }
6965
+ }
6966
+ if (result.exitCode === 2) {
6967
+ logger.error("scan-config: HIGH severity findings detected. Execution should be blocked.");
6968
+ } else if (result.exitCode === 1) {
6969
+ logger.warn("scan-config: MEDIUM severity findings detected. Session should be tainted.");
6970
+ }
6971
+ }
6972
+ function createScanConfigCommand() {
6973
+ const command = new Command69("scan-config").description(
6974
+ "Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
6975
+ ).option("--path <dir>", "Target directory to scan", process.cwd()).option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
6976
+ const globalOpts = cmd.optsWithGlobals();
6977
+ const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : OutputMode.TEXT;
6978
+ const result = await runScanConfig(opts.path, { fix: opts.fix });
6979
+ if (mode === OutputMode.JSON) {
6980
+ console.log(JSON.stringify(result, null, 2));
6981
+ } else if (mode !== OutputMode.QUIET) {
6982
+ formatTextOutput(result);
6983
+ }
6984
+ process.exit(result.exitCode);
6985
+ });
6986
+ return command;
6987
+ }
6988
+
5817
6989
  // src/index.ts
5818
6990
  function createProgram() {
5819
- const program = new Command61();
6991
+ const program = new Command70();
5820
6992
  program.name("harness").description("CLI for Harness Engineering toolkit").version(CLI_VERSION).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
5821
6993
  program.addCommand(createValidateCommand());
5822
6994
  program.addCommand(createCheckDepsCommand());
@@ -5859,6 +7031,10 @@ function createProgram() {
5859
7031
  program.addCommand(createUninstallConstraintsCommand());
5860
7032
  program.addCommand(createUninstallCommand());
5861
7033
  program.addCommand(createOrchestratorCommand());
7034
+ program.addCommand(createIntegrationsCommand());
7035
+ program.addCommand(createUsageCommand());
7036
+ program.addCommand(createTaintCommand());
7037
+ program.addCommand(createScanConfigCommand());
5862
7038
  return program;
5863
7039
  }
5864
7040