@harness-engineering/cli 1.12.0 → 1.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/dist/agents/skills/claude-code/add-harness-component/skill.yaml +1 -0
  2. package/dist/agents/skills/claude-code/align-documentation/skill.yaml +1 -0
  3. package/dist/agents/skills/claude-code/check-mechanical-constraints/skill.yaml +1 -0
  4. package/dist/agents/skills/claude-code/cleanup-dead-code/skill.yaml +1 -0
  5. package/dist/agents/skills/claude-code/detect-doc-drift/skill.yaml +1 -0
  6. package/dist/agents/skills/claude-code/enforce-architecture/skill.yaml +1 -0
  7. package/dist/agents/skills/claude-code/harness-accessibility/skill.yaml +1 -0
  8. package/dist/agents/skills/claude-code/harness-api-design/SKILL.md +304 -0
  9. package/dist/agents/skills/claude-code/harness-api-design/skill.yaml +74 -0
  10. package/dist/agents/skills/claude-code/harness-architecture-advisor/skill.yaml +1 -0
  11. package/dist/agents/skills/claude-code/harness-auth/SKILL.md +279 -0
  12. package/dist/agents/skills/claude-code/harness-auth/skill.yaml +81 -0
  13. package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +57 -9
  14. package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +1 -0
  15. package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +1 -1
  16. package/dist/agents/skills/claude-code/harness-brainstorming/skill.yaml +1 -0
  17. package/dist/agents/skills/claude-code/harness-caching/SKILL.md +309 -0
  18. package/dist/agents/skills/claude-code/harness-caching/skill.yaml +73 -0
  19. package/dist/agents/skills/claude-code/harness-chaos/SKILL.md +295 -0
  20. package/dist/agents/skills/claude-code/harness-chaos/skill.yaml +72 -0
  21. package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +19 -2
  22. package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +1 -0
  23. package/dist/agents/skills/claude-code/harness-codebase-cleanup/skill.yaml +1 -0
  24. package/dist/agents/skills/claude-code/harness-compliance/SKILL.md +303 -0
  25. package/dist/agents/skills/claude-code/harness-compliance/skill.yaml +78 -0
  26. package/dist/agents/skills/claude-code/harness-containerization/SKILL.md +284 -0
  27. package/dist/agents/skills/claude-code/harness-containerization/skill.yaml +80 -0
  28. package/dist/agents/skills/claude-code/harness-data-pipeline/SKILL.md +274 -0
  29. package/dist/agents/skills/claude-code/harness-data-pipeline/skill.yaml +81 -0
  30. package/dist/agents/skills/claude-code/harness-data-validation/SKILL.md +343 -0
  31. package/dist/agents/skills/claude-code/harness-data-validation/skill.yaml +75 -0
  32. package/dist/agents/skills/claude-code/harness-database/SKILL.md +258 -0
  33. package/dist/agents/skills/claude-code/harness-database/skill.yaml +80 -0
  34. package/dist/agents/skills/claude-code/harness-debugging/skill.yaml +1 -0
  35. package/dist/agents/skills/claude-code/harness-dependency-health/skill.yaml +1 -0
  36. package/dist/agents/skills/claude-code/harness-deployment/SKILL.md +255 -0
  37. package/dist/agents/skills/claude-code/harness-deployment/skill.yaml +77 -0
  38. package/dist/agents/skills/claude-code/harness-design/skill.yaml +1 -0
  39. package/dist/agents/skills/claude-code/harness-design-mobile/skill.yaml +1 -0
  40. package/dist/agents/skills/claude-code/harness-design-system/skill.yaml +1 -0
  41. package/dist/agents/skills/claude-code/harness-design-web/skill.yaml +1 -0
  42. package/dist/agents/skills/claude-code/harness-diagnostics/skill.yaml +1 -0
  43. package/dist/agents/skills/claude-code/harness-docs-pipeline/skill.yaml +1 -0
  44. package/dist/agents/skills/claude-code/harness-dx/SKILL.md +276 -0
  45. package/dist/agents/skills/claude-code/harness-dx/skill.yaml +76 -0
  46. package/dist/agents/skills/claude-code/harness-e2e/SKILL.md +245 -0
  47. package/dist/agents/skills/claude-code/harness-e2e/skill.yaml +78 -0
  48. package/dist/agents/skills/claude-code/harness-event-driven/SKILL.md +280 -0
  49. package/dist/agents/skills/claude-code/harness-event-driven/skill.yaml +77 -0
  50. package/dist/agents/skills/claude-code/harness-execution/SKILL.md +39 -12
  51. package/dist/agents/skills/claude-code/harness-execution/skill.yaml +1 -0
  52. package/dist/agents/skills/claude-code/harness-feature-flags/SKILL.md +287 -0
  53. package/dist/agents/skills/claude-code/harness-feature-flags/skill.yaml +74 -0
  54. package/dist/agents/skills/claude-code/harness-git-workflow/skill.yaml +1 -0
  55. package/dist/agents/skills/claude-code/harness-hotspot-detector/skill.yaml +1 -0
  56. package/dist/agents/skills/claude-code/harness-i18n/skill.yaml +1 -0
  57. package/dist/agents/skills/claude-code/harness-i18n-process/skill.yaml +1 -0
  58. package/dist/agents/skills/claude-code/harness-i18n-workflow/skill.yaml +1 -0
  59. package/dist/agents/skills/claude-code/harness-impact-analysis/skill.yaml +1 -0
  60. package/dist/agents/skills/claude-code/harness-incident-response/SKILL.md +223 -0
  61. package/dist/agents/skills/claude-code/harness-incident-response/skill.yaml +78 -0
  62. package/dist/agents/skills/claude-code/harness-infrastructure-as-code/SKILL.md +279 -0
  63. package/dist/agents/skills/claude-code/harness-infrastructure-as-code/skill.yaml +80 -0
  64. package/dist/agents/skills/claude-code/harness-integration-test/SKILL.md +271 -0
  65. package/dist/agents/skills/claude-code/harness-integration-test/skill.yaml +73 -0
  66. package/dist/agents/skills/claude-code/harness-integrity/skill.yaml +1 -0
  67. package/dist/agents/skills/claude-code/harness-knowledge-mapper/skill.yaml +1 -0
  68. package/dist/agents/skills/claude-code/harness-load-testing/SKILL.md +274 -0
  69. package/dist/agents/skills/claude-code/harness-load-testing/skill.yaml +79 -0
  70. package/dist/agents/skills/claude-code/harness-ml-ops/SKILL.md +341 -0
  71. package/dist/agents/skills/claude-code/harness-ml-ops/skill.yaml +79 -0
  72. package/dist/agents/skills/claude-code/harness-mobile-patterns/SKILL.md +326 -0
  73. package/dist/agents/skills/claude-code/harness-mobile-patterns/skill.yaml +82 -0
  74. package/dist/agents/skills/claude-code/harness-mutation-test/SKILL.md +251 -0
  75. package/dist/agents/skills/claude-code/harness-mutation-test/skill.yaml +70 -0
  76. package/dist/agents/skills/claude-code/harness-observability/SKILL.md +283 -0
  77. package/dist/agents/skills/claude-code/harness-observability/skill.yaml +78 -0
  78. package/dist/agents/skills/claude-code/harness-onboarding/skill.yaml +1 -0
  79. package/dist/agents/skills/claude-code/harness-parallel-agents/skill.yaml +1 -0
  80. package/dist/agents/skills/claude-code/harness-perf/skill.yaml +1 -0
  81. package/dist/agents/skills/claude-code/harness-perf-tdd/skill.yaml +1 -0
  82. package/dist/agents/skills/claude-code/harness-planning/SKILL.md +28 -11
  83. package/dist/agents/skills/claude-code/harness-planning/skill.yaml +1 -0
  84. package/dist/agents/skills/claude-code/harness-pre-commit-review/skill.yaml +1 -0
  85. package/dist/agents/skills/claude-code/harness-product-spec/SKILL.md +285 -0
  86. package/dist/agents/skills/claude-code/harness-product-spec/skill.yaml +72 -0
  87. package/dist/agents/skills/claude-code/harness-property-test/SKILL.md +281 -0
  88. package/dist/agents/skills/claude-code/harness-property-test/skill.yaml +71 -0
  89. package/dist/agents/skills/claude-code/harness-refactoring/skill.yaml +1 -0
  90. package/dist/agents/skills/claude-code/harness-release-readiness/skill.yaml +1 -0
  91. package/dist/agents/skills/claude-code/harness-resilience/SKILL.md +255 -0
  92. package/dist/agents/skills/claude-code/harness-resilience/skill.yaml +76 -0
  93. package/dist/agents/skills/claude-code/harness-roadmap/SKILL.md +34 -0
  94. package/dist/agents/skills/claude-code/harness-roadmap/skill.yaml +1 -0
  95. package/dist/agents/skills/claude-code/harness-secrets/SKILL.md +293 -0
  96. package/dist/agents/skills/claude-code/harness-secrets/skill.yaml +76 -0
  97. package/dist/agents/skills/claude-code/harness-security-review/skill.yaml +1 -0
  98. package/dist/agents/skills/claude-code/harness-security-scan/skill.yaml +1 -0
  99. package/dist/agents/skills/claude-code/harness-skill-authoring/skill.yaml +1 -0
  100. package/dist/agents/skills/claude-code/harness-soundness-review/skill.yaml +1 -0
  101. package/dist/agents/skills/claude-code/harness-sql-review/SKILL.md +315 -0
  102. package/dist/agents/skills/claude-code/harness-sql-review/skill.yaml +74 -0
  103. package/dist/agents/skills/claude-code/harness-state-management/skill.yaml +1 -0
  104. package/dist/agents/skills/claude-code/harness-tdd/skill.yaml +1 -0
  105. package/dist/agents/skills/claude-code/harness-test-advisor/skill.yaml +1 -0
  106. package/dist/agents/skills/claude-code/harness-test-data/SKILL.md +268 -0
  107. package/dist/agents/skills/claude-code/harness-test-data/skill.yaml +74 -0
  108. package/dist/agents/skills/claude-code/harness-ux-copy/SKILL.md +271 -0
  109. package/dist/agents/skills/claude-code/harness-ux-copy/skill.yaml +77 -0
  110. package/dist/agents/skills/claude-code/harness-verification/SKILL.md +42 -0
  111. package/dist/agents/skills/claude-code/harness-verification/skill.yaml +1 -0
  112. package/dist/agents/skills/claude-code/harness-verify/skill.yaml +1 -0
  113. package/dist/agents/skills/claude-code/harness-visual-regression/SKILL.md +257 -0
  114. package/dist/agents/skills/claude-code/harness-visual-regression/skill.yaml +74 -0
  115. package/dist/agents/skills/claude-code/initialize-harness-project/skill.yaml +1 -0
  116. package/dist/agents/skills/claude-code/validate-context-engineering/skill.yaml +1 -0
  117. package/dist/agents/skills/gemini-cli/add-harness-component/skill.yaml +1 -0
  118. package/dist/agents/skills/gemini-cli/align-documentation/skill.yaml +1 -0
  119. package/dist/agents/skills/gemini-cli/check-mechanical-constraints/skill.yaml +1 -0
  120. package/dist/agents/skills/gemini-cli/cleanup-dead-code/skill.yaml +1 -0
  121. package/dist/agents/skills/gemini-cli/detect-doc-drift/skill.yaml +1 -0
  122. package/dist/agents/skills/gemini-cli/enforce-architecture/skill.yaml +1 -0
  123. package/dist/agents/skills/gemini-cli/harness-accessibility/skill.yaml +1 -0
  124. package/dist/agents/skills/gemini-cli/harness-api-design/SKILL.md +304 -0
  125. package/dist/agents/skills/gemini-cli/harness-api-design/skill.yaml +74 -0
  126. package/dist/agents/skills/gemini-cli/harness-architecture-advisor/skill.yaml +1 -0
  127. package/dist/agents/skills/gemini-cli/harness-auth/SKILL.md +279 -0
  128. package/dist/agents/skills/gemini-cli/harness-auth/skill.yaml +81 -0
  129. package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +57 -9
  130. package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +1 -0
  131. package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +1 -1
  132. package/dist/agents/skills/gemini-cli/harness-brainstorming/skill.yaml +1 -0
  133. package/dist/agents/skills/gemini-cli/harness-caching/SKILL.md +309 -0
  134. package/dist/agents/skills/gemini-cli/harness-caching/skill.yaml +73 -0
  135. package/dist/agents/skills/gemini-cli/harness-chaos/SKILL.md +295 -0
  136. package/dist/agents/skills/gemini-cli/harness-chaos/skill.yaml +72 -0
  137. package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +19 -2
  138. package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +1 -0
  139. package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/skill.yaml +1 -0
  140. package/dist/agents/skills/gemini-cli/harness-compliance/SKILL.md +303 -0
  141. package/dist/agents/skills/gemini-cli/harness-compliance/skill.yaml +78 -0
  142. package/dist/agents/skills/gemini-cli/harness-containerization/SKILL.md +284 -0
  143. package/dist/agents/skills/gemini-cli/harness-containerization/skill.yaml +80 -0
  144. package/dist/agents/skills/gemini-cli/harness-data-pipeline/SKILL.md +274 -0
  145. package/dist/agents/skills/gemini-cli/harness-data-pipeline/skill.yaml +81 -0
  146. package/dist/agents/skills/gemini-cli/harness-data-validation/SKILL.md +343 -0
  147. package/dist/agents/skills/gemini-cli/harness-data-validation/skill.yaml +75 -0
  148. package/dist/agents/skills/gemini-cli/harness-database/SKILL.md +258 -0
  149. package/dist/agents/skills/gemini-cli/harness-database/skill.yaml +80 -0
  150. package/dist/agents/skills/gemini-cli/harness-debugging/skill.yaml +1 -0
  151. package/dist/agents/skills/gemini-cli/harness-dependency-health/skill.yaml +1 -0
  152. package/dist/agents/skills/gemini-cli/harness-deployment/SKILL.md +255 -0
  153. package/dist/agents/skills/gemini-cli/harness-deployment/skill.yaml +77 -0
  154. package/dist/agents/skills/gemini-cli/harness-design/skill.yaml +1 -0
  155. package/dist/agents/skills/gemini-cli/harness-design-mobile/skill.yaml +1 -0
  156. package/dist/agents/skills/gemini-cli/harness-design-system/skill.yaml +1 -0
  157. package/dist/agents/skills/gemini-cli/harness-design-web/skill.yaml +1 -0
  158. package/dist/agents/skills/gemini-cli/harness-diagnostics/skill.yaml +1 -0
  159. package/dist/agents/skills/gemini-cli/harness-docs-pipeline/skill.yaml +1 -0
  160. package/dist/agents/skills/gemini-cli/harness-dx/SKILL.md +276 -0
  161. package/dist/agents/skills/gemini-cli/harness-dx/skill.yaml +76 -0
  162. package/dist/agents/skills/gemini-cli/harness-e2e/SKILL.md +245 -0
  163. package/dist/agents/skills/gemini-cli/harness-e2e/skill.yaml +78 -0
  164. package/dist/agents/skills/gemini-cli/harness-event-driven/SKILL.md +280 -0
  165. package/dist/agents/skills/gemini-cli/harness-event-driven/skill.yaml +77 -0
  166. package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +39 -12
  167. package/dist/agents/skills/gemini-cli/harness-execution/skill.yaml +1 -0
  168. package/dist/agents/skills/gemini-cli/harness-feature-flags/SKILL.md +287 -0
  169. package/dist/agents/skills/gemini-cli/harness-feature-flags/skill.yaml +74 -0
  170. package/dist/agents/skills/gemini-cli/harness-git-workflow/skill.yaml +1 -0
  171. package/dist/agents/skills/gemini-cli/harness-hotspot-detector/skill.yaml +1 -0
  172. package/dist/agents/skills/gemini-cli/harness-i18n/skill.yaml +1 -0
  173. package/dist/agents/skills/gemini-cli/harness-i18n-process/skill.yaml +1 -0
  174. package/dist/agents/skills/gemini-cli/harness-i18n-workflow/skill.yaml +1 -0
  175. package/dist/agents/skills/gemini-cli/harness-impact-analysis/skill.yaml +1 -0
  176. package/dist/agents/skills/gemini-cli/harness-incident-response/SKILL.md +223 -0
  177. package/dist/agents/skills/gemini-cli/harness-incident-response/skill.yaml +78 -0
  178. package/dist/agents/skills/gemini-cli/harness-infrastructure-as-code/SKILL.md +279 -0
  179. package/dist/agents/skills/gemini-cli/harness-infrastructure-as-code/skill.yaml +80 -0
  180. package/dist/agents/skills/gemini-cli/harness-integration-test/SKILL.md +271 -0
  181. package/dist/agents/skills/gemini-cli/harness-integration-test/skill.yaml +73 -0
  182. package/dist/agents/skills/gemini-cli/harness-integrity/skill.yaml +1 -0
  183. package/dist/agents/skills/gemini-cli/harness-knowledge-mapper/skill.yaml +1 -0
  184. package/dist/agents/skills/gemini-cli/harness-load-testing/SKILL.md +274 -0
  185. package/dist/agents/skills/gemini-cli/harness-load-testing/skill.yaml +79 -0
  186. package/dist/agents/skills/gemini-cli/harness-ml-ops/SKILL.md +341 -0
  187. package/dist/agents/skills/gemini-cli/harness-ml-ops/skill.yaml +79 -0
  188. package/dist/agents/skills/gemini-cli/harness-mobile-patterns/SKILL.md +326 -0
  189. package/dist/agents/skills/gemini-cli/harness-mobile-patterns/skill.yaml +82 -0
  190. package/dist/agents/skills/gemini-cli/harness-mutation-test/SKILL.md +251 -0
  191. package/dist/agents/skills/gemini-cli/harness-mutation-test/skill.yaml +70 -0
  192. package/dist/agents/skills/gemini-cli/harness-observability/SKILL.md +283 -0
  193. package/dist/agents/skills/gemini-cli/harness-observability/skill.yaml +78 -0
  194. package/dist/agents/skills/gemini-cli/harness-onboarding/skill.yaml +1 -0
  195. package/dist/agents/skills/gemini-cli/harness-parallel-agents/skill.yaml +1 -0
  196. package/dist/agents/skills/gemini-cli/harness-perf/skill.yaml +1 -0
  197. package/dist/agents/skills/gemini-cli/harness-perf-tdd/skill.yaml +1 -0
  198. package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +28 -11
  199. package/dist/agents/skills/gemini-cli/harness-planning/skill.yaml +1 -0
  200. package/dist/agents/skills/gemini-cli/harness-pre-commit-review/skill.yaml +1 -0
  201. package/dist/agents/skills/gemini-cli/harness-product-spec/SKILL.md +285 -0
  202. package/dist/agents/skills/gemini-cli/harness-product-spec/skill.yaml +72 -0
  203. package/dist/agents/skills/gemini-cli/harness-property-test/SKILL.md +281 -0
  204. package/dist/agents/skills/gemini-cli/harness-property-test/skill.yaml +71 -0
  205. package/dist/agents/skills/gemini-cli/harness-refactoring/skill.yaml +1 -0
  206. package/dist/agents/skills/gemini-cli/harness-release-readiness/skill.yaml +1 -0
  207. package/dist/agents/skills/gemini-cli/harness-resilience/SKILL.md +255 -0
  208. package/dist/agents/skills/gemini-cli/harness-resilience/skill.yaml +76 -0
  209. package/dist/agents/skills/gemini-cli/harness-roadmap/SKILL.md +34 -0
  210. package/dist/agents/skills/gemini-cli/harness-roadmap/skill.yaml +1 -0
  211. package/dist/agents/skills/gemini-cli/harness-secrets/SKILL.md +293 -0
  212. package/dist/agents/skills/gemini-cli/harness-secrets/skill.yaml +76 -0
  213. package/dist/agents/skills/gemini-cli/harness-security-review/SKILL.md +240 -0
  214. package/dist/agents/skills/gemini-cli/harness-security-review/skill.yaml +1 -0
  215. package/dist/agents/skills/gemini-cli/harness-security-scan/skill.yaml +1 -0
  216. package/dist/agents/skills/gemini-cli/harness-skill-authoring/skill.yaml +1 -0
  217. package/dist/agents/skills/gemini-cli/harness-soundness-review/skill.yaml +1 -0
  218. package/dist/agents/skills/gemini-cli/harness-sql-review/SKILL.md +315 -0
  219. package/dist/agents/skills/gemini-cli/harness-sql-review/skill.yaml +74 -0
  220. package/dist/agents/skills/gemini-cli/harness-state-management/skill.yaml +1 -0
  221. package/dist/agents/skills/gemini-cli/harness-tdd/skill.yaml +1 -0
  222. package/dist/agents/skills/gemini-cli/harness-test-advisor/skill.yaml +1 -0
  223. package/dist/agents/skills/gemini-cli/harness-test-data/SKILL.md +268 -0
  224. package/dist/agents/skills/gemini-cli/harness-test-data/skill.yaml +74 -0
  225. package/dist/agents/skills/gemini-cli/harness-ux-copy/SKILL.md +271 -0
  226. package/dist/agents/skills/gemini-cli/harness-ux-copy/skill.yaml +77 -0
  227. package/dist/agents/skills/gemini-cli/harness-verification/SKILL.md +42 -0
  228. package/dist/agents/skills/gemini-cli/harness-verification/skill.yaml +1 -0
  229. package/dist/agents/skills/gemini-cli/harness-verify/skill.yaml +1 -0
  230. package/dist/agents/skills/gemini-cli/harness-visual-regression/SKILL.md +257 -0
  231. package/dist/agents/skills/gemini-cli/harness-visual-regression/skill.yaml +74 -0
  232. package/dist/agents/skills/gemini-cli/initialize-harness-project/skill.yaml +1 -0
  233. package/dist/agents/skills/gemini-cli/validate-context-engineering/skill.yaml +1 -0
  234. package/dist/{agents-md-KIS2RSMG.js → agents-md-XU3BHE22.js} +1 -1
  235. package/dist/{architecture-AJAUDRQQ.js → architecture-2R5Z4ZAF.js} +2 -2
  236. package/dist/bin/harness-mcp.js +14 -13
  237. package/dist/bin/harness.js +22 -21
  238. package/dist/{check-phase-gate-K7QCSYRJ.js → check-phase-gate-2OFZ7OWW.js} +3 -2
  239. package/dist/{chunk-TJVVU3HB.js → chunk-4ZMOCPYO.js} +1 -1
  240. package/dist/{chunk-EAURF4LH.js → chunk-65FRIL4D.js} +2 -2
  241. package/dist/{chunk-L2KLU56K.js → chunk-AOZRDOIP.js} +2 -2
  242. package/dist/{chunk-JLXOEO5C.js → chunk-DZS7CJKL.js} +4 -4
  243. package/dist/{chunk-FLOEMHDF.js → chunk-IM32EEDM.js} +9 -9
  244. package/dist/{chunk-2YPZKGAG.js → chunk-IMFVFNJE.js} +1 -1
  245. package/dist/{chunk-HD4IBGLA.js → chunk-N5G5QMS3.js} +24 -1
  246. package/dist/{chunk-CTTFXXKJ.js → chunk-ND6PNADU.js} +23 -9
  247. package/dist/{chunk-747VBPA4.js → chunk-NERR4TAO.js} +783 -444
  248. package/dist/{chunk-YXOG2277.js → chunk-NOPU4RZ4.js} +2 -2
  249. package/dist/{chunk-AE2OWWDH.js → chunk-PQ5YK4AY.js} +870 -504
  250. package/dist/{chunk-OIGVQF5V.js → chunk-QY4T6YAZ.js} +3 -3
  251. package/dist/{chunk-B5SBNH4S.js → chunk-SSKDAOX5.js} +93 -30
  252. package/dist/{chunk-2SWJ4VO7.js → chunk-TKJZKICB.js} +6 -6
  253. package/dist/{chunk-GNGELAXY.js → chunk-TS3XWPW5.js} +1 -1
  254. package/dist/chunk-UAX4I5ZE.js +217 -0
  255. package/dist/{chunk-VRFZWGMS.js → chunk-XYLGHKG6.js} +5 -1
  256. package/dist/{chunk-6N4R6FVX.js → chunk-YBJ262QL.js} +1 -1
  257. package/dist/{chunk-ZU2UBYBY.js → chunk-Z77YQRQT.js} +11 -207
  258. package/dist/{ci-workflow-NBL4OT4A.js → ci-workflow-EHV65NQB.js} +1 -1
  259. package/dist/{create-skill-WPXHSLX2.js → create-skill-XSWHMSM5.js} +2 -2
  260. package/dist/{dist-IJ4J4C5G.js → dist-2B363XUH.js} +25 -1
  261. package/dist/{dist-M6BQODWC.js → dist-HXHWB7SV.js} +2 -2
  262. package/dist/{docs-CPTMH3VY.js → docs-FZOPM4GK.js} +4 -2
  263. package/dist/{engine-BUWPAAGD.js → engine-OL4T6NZS.js} +1 -1
  264. package/dist/{entropy-Z4FYVQ7L.js → entropy-LVHJMFGH.js} +2 -2
  265. package/dist/{feedback-TT6WF5YX.js → feedback-IHLVLMRD.js} +1 -1
  266. package/dist/{generate-agent-definitions-J5HANRNR.js → generate-agent-definitions-64S3CLEZ.js} +3 -3
  267. package/dist/{glob-helper-5OHBUQAI.js → glob-helper-R5FXNUPS.js} +1 -1
  268. package/dist/{graph-loader-KO4GJ5N2.js → graph-loader-GJZ4FN4Y.js} +1 -1
  269. package/dist/index.d.ts +60 -33
  270. package/dist/index.js +23 -21
  271. package/dist/{loader-PCU5YWRH.js → loader-DPYFB6R6.js} +1 -1
  272. package/dist/{mcp-YM6QLHLZ.js → mcp-JQUI7BVZ.js} +14 -13
  273. package/dist/{performance-YJVXOKIB.js → performance-ZTVSUANN.js} +2 -2
  274. package/dist/{review-pipeline-KGMIMLIE.js → review-pipeline-76JHKGSV.js} +1 -1
  275. package/dist/{runtime-F6R27LD6.js → runtime-X7U6SC7K.js} +1 -1
  276. package/dist/{security-MX5VVXBC.js → security-FWQZF2IZ.js} +1 -1
  277. package/dist/skill-executor-XZLYZYAK.js +8 -0
  278. package/dist/{validate-EFNMSFKD.js → validate-GCHZJIL7.js} +2 -2
  279. package/dist/{validate-cross-check-LJX65SBS.js → validate-cross-check-STFHYMAZ.js} +1 -1
  280. package/package.json +4 -4
  281. package/dist/skill-executor-RG45LUO5.js +0 -8
@@ -3,27 +3,23 @@ import {
3
3
  Ok
4
4
  } from "./chunk-MHBMTPW7.js";
5
5
 
6
- // ../core/dist/chunk-ZHGBWFYD.mjs
6
+ // ../core/dist/chunk-D6VFA6AS.mjs
7
7
  import { z } from "zod";
8
- import { relative as relative2 } from "path";
9
8
  import { createHash } from "crypto";
10
9
  import { minimatch } from "minimatch";
11
10
  import { access, constants, readFile } from "fs";
12
11
  import { promisify } from "util";
12
+ import { relative } from "path";
13
13
  import { glob } from "glob";
14
- import { dirname, resolve, relative } from "path";
15
- import { relative as relative3 } from "path";
14
+ import { dirname, resolve } from "path";
16
15
  import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync } from "fs";
17
16
  import { randomBytes } from "crypto";
18
17
  import { join, dirname as dirname2 } from "path";
19
- import { relative as relative4 } from "path";
20
18
  import { readFile as readFile2 } from "fs/promises";
21
- import { relative as relative5 } from "path";
22
- import { relative as relative6 } from "path";
23
19
  import { readFile as readFile3, readdir } from "fs/promises";
24
- import { join as join2, relative as relative7 } from "path";
20
+ import { join as join2 } from "path";
25
21
  import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
26
- import { join as join3, relative as relative8, dirname as dirname3, resolve as resolve2 } from "path";
22
+ import { join as join3, dirname as dirname3, resolve as resolve2 } from "path";
27
23
  var ArchMetricCategorySchema = z.enum([
28
24
  "circular-deps",
29
25
  "layer-violations",
@@ -106,8 +102,7 @@ var ConstraintRuleSchema = z.object({
106
102
  // forward-compat for governs edges
107
103
  });
108
104
  function violationId(relativePath, category, normalizedDetail) {
109
- const path13 = relativePath.replace(/\\/g, "/");
110
- const input = `${path13}:${category}:${normalizedDetail}`;
105
+ const input = `${relativePath}:${category}:${normalizedDetail}`;
111
106
  return createHash("sha256").update(input).digest("hex");
112
107
  }
113
108
  function constraintRuleId(category, scope, description) {
@@ -139,17 +134,17 @@ function resolveFileToLayer(file, layers) {
139
134
  }
140
135
  var accessAsync = promisify(access);
141
136
  var readFileAsync = promisify(readFile);
142
- async function fileExists(path13) {
137
+ async function fileExists(path20) {
143
138
  try {
144
- await accessAsync(path13, constants.F_OK);
139
+ await accessAsync(path20, constants.F_OK);
145
140
  return true;
146
141
  } catch {
147
142
  return false;
148
143
  }
149
144
  }
150
- async function readFileContent(path13) {
145
+ async function readFileContent(path20) {
151
146
  try {
152
- const content = await readFileAsync(path13, "utf-8");
147
+ const content = await readFileAsync(path20, "utf-8");
153
148
  return Ok(content);
154
149
  } catch (error) {
155
150
  return Err(error);
@@ -158,6 +153,9 @@ async function readFileContent(path13) {
158
153
  async function findFiles(pattern, cwd = process.cwd()) {
159
154
  return glob(pattern, { cwd, absolute: true });
160
155
  }
156
+ function relativePosix(from, to) {
157
+ return relative(from, to).replaceAll("\\", "/");
158
+ }
161
159
  function resolveImportPath(importSource, fromFile, _rootDir) {
162
160
  if (!importSource.startsWith(".") && !importSource.startsWith("/")) {
163
161
  return null;
@@ -209,8 +207,8 @@ async function buildDependencyGraph(files, parser, graphDependencyData) {
209
207
  function checkLayerViolations(graph, layers, rootDir) {
210
208
  const violations = [];
211
209
  for (const edge of graph.edges) {
212
- const fromRelative = relative(rootDir, edge.from);
213
- const toRelative = relative(rootDir, edge.to);
210
+ const fromRelative = relativePosix(rootDir, edge.from);
211
+ const toRelative = relativePosix(rootDir, edge.to);
214
212
  const fromLayer = resolveFileToLayer(fromRelative, layers);
215
213
  const toLayer = resolveFileToLayer(toRelative, layers);
216
214
  if (!fromLayer || !toLayer) continue;
@@ -436,8 +434,8 @@ var CircularDepsCollector = class {
436
434
  }
437
435
  const { cycles, largestCycle } = result.value;
438
436
  const violations = cycles.map((cycle) => {
439
- const cyclePath = cycle.cycle.map((f) => relative2(rootDir, f)).join(" -> ");
440
- const firstFile = relative2(rootDir, cycle.cycle[0]);
437
+ const cyclePath = cycle.cycle.map((f) => relativePosix(rootDir, f)).join(" -> ");
438
+ const firstFile = relativePosix(rootDir, cycle.cycle[0]);
441
439
  return {
442
440
  id: violationId(firstFile, this.category, cyclePath),
443
441
  file: firstFile,
@@ -499,8 +497,8 @@ var LayerViolationCollector = class {
499
497
  (v) => v.reason === "WRONG_LAYER"
500
498
  );
501
499
  const violations = layerViolations.map((v) => {
502
- const relFile = relative3(rootDir, v.file);
503
- const relImport = relative3(rootDir, v.imports);
500
+ const relFile = relativePosix(rootDir, v.file);
501
+ const relImport = relativePosix(rootDir, v.imports);
504
502
  const detail = `${v.fromLayer} -> ${v.toLayer}: ${relFile} imports ${relImport}`;
505
503
  return {
506
504
  id: violationId(relFile, this.category, detail),
@@ -980,7 +978,7 @@ var ComplexityCollector = class {
980
978
  (v) => v.severity === "error" || v.severity === "warning"
981
979
  );
982
980
  const violations = filtered.map((v) => {
983
- const relFile = relative4(rootDir, v.file);
981
+ const relFile = relativePosix(rootDir, v.file);
984
982
  const idDetail = `${v.metric}:${v.function}`;
985
983
  return {
986
984
  id: violationId(relFile, this.category, idDetail),
@@ -1202,7 +1200,7 @@ var CouplingCollector = class {
1202
1200
  (v) => v.severity === "error" || v.severity === "warning"
1203
1201
  );
1204
1202
  const violations = filtered.map((v) => {
1205
- const relFile = relative5(rootDir, v.file);
1203
+ const relFile = relativePosix(rootDir, v.file);
1206
1204
  const idDetail = `${v.metric}`;
1207
1205
  return {
1208
1206
  id: violationId(relFile, this.category, idDetail),
@@ -1266,8 +1264,8 @@ var ForbiddenImportCollector = class {
1266
1264
  (v) => v.reason === "FORBIDDEN_IMPORT"
1267
1265
  );
1268
1266
  const violations = forbidden.map((v) => {
1269
- const relFile = relative6(rootDir, v.file);
1270
- const relImport = relative6(rootDir, v.imports);
1267
+ const relFile = relativePosix(rootDir, v.file);
1268
+ const relImport = relativePosix(rootDir, v.imports);
1271
1269
  const detail = `forbidden import: ${relFile} -> ${relImport}`;
1272
1270
  return {
1273
1271
  id: violationId(relFile, this.category, detail),
@@ -1319,10 +1317,10 @@ async function discoverModules(rootDir) {
1319
1317
  }
1320
1318
  }
1321
1319
  modules.push({
1322
- modulePath: relative7(rootDir, dir),
1320
+ modulePath: relativePosix(rootDir, dir),
1323
1321
  fileCount: tsFiles.length,
1324
1322
  totalLoc,
1325
- files: tsFiles.map((f) => relative7(rootDir, f))
1323
+ files: tsFiles.map((f) => relativePosix(rootDir, f))
1326
1324
  });
1327
1325
  }
1328
1326
  for (const sub of subdirs) {
@@ -1494,7 +1492,7 @@ var DepDepthCollector = class {
1494
1492
  }
1495
1493
  const moduleMap = /* @__PURE__ */ new Map();
1496
1494
  for (const file of allFiles) {
1497
- const relDir = relative8(rootDir, dirname3(file));
1495
+ const relDir = relativePosix(rootDir, dirname3(file));
1498
1496
  if (!moduleMap.has(relDir)) moduleMap.set(relDir, []);
1499
1497
  moduleMap.get(relDir).push(file);
1500
1498
  }
@@ -1743,19 +1741,18 @@ var archMatchers = {
1743
1741
  // ../core/dist/index.mjs
1744
1742
  import { join as join4, dirname as dirname4 } from "path";
1745
1743
  import { minimatch as minimatch2 } from "minimatch";
1746
- import { basename, relative as relative9 } from "path";
1747
- import { join as join22, basename as basename2, relative as relative22 } from "path";
1748
- import { relative as relative32, basename as basename3, dirname as dirname22 } from "path";
1744
+ import { basename } from "path";
1745
+ import { join as join22, basename as basename2 } from "path";
1746
+ import { basename as basename3, dirname as dirname22 } from "path";
1749
1747
  import { z as z2 } from "zod";
1750
1748
  import * as fs from "fs/promises";
1751
1749
  import * as fs2 from "fs/promises";
1752
1750
  import { parse } from "@typescript-eslint/typescript-estree";
1753
- import { join as join32, resolve as resolve3, relative as relative42 } from "path";
1751
+ import { join as join32, resolve as resolve3 } from "path";
1754
1752
  import { minimatch as minimatch22 } from "minimatch";
1755
1753
  import { dirname as dirname32, resolve as resolve22 } from "path";
1756
1754
  import { dirname as dirname42, resolve as resolve32 } from "path";
1757
1755
  import { minimatch as minimatch3 } from "minimatch";
1758
- import { relative as relative52 } from "path";
1759
1756
  import { readdirSync, statSync } from "fs";
1760
1757
  import { join as join42 } from "path";
1761
1758
  import * as fs3 from "fs";
@@ -1770,31 +1767,45 @@ import * as path from "path";
1770
1767
  import { appendFileSync, writeFileSync as writeFileSync22, existsSync as existsSync22, mkdirSync as mkdirSync22 } from "fs";
1771
1768
  import { dirname as dirname7 } from "path";
1772
1769
  import { z as z3 } from "zod";
1773
- import * as fs6 from "fs";
1774
- import * as path3 from "path";
1775
- import { execSync as execSync2 } from "child_process";
1770
+ import * as fs8 from "fs";
1771
+ import * as path5 from "path";
1772
+ import * as fs7 from "fs";
1773
+ import * as path4 from "path";
1776
1774
  import * as fs5 from "fs";
1777
1775
  import * as path2 from "path";
1778
1776
  import { execSync } from "child_process";
1779
1777
  import { z as z4 } from "zod";
1780
- import * as fs8 from "fs/promises";
1781
- import { z as z5 } from "zod";
1782
- import * as fs7 from "fs";
1783
- import * as path4 from "path";
1784
- import * as path5 from "path";
1778
+ import * as fs6 from "fs";
1779
+ import * as path3 from "path";
1780
+ import * as fs9 from "fs";
1785
1781
  import * as path6 from "path";
1782
+ import * as fs10 from "fs";
1786
1783
  import * as path7 from "path";
1784
+ import * as fs11 from "fs";
1787
1785
  import * as path8 from "path";
1788
- import * as fs9 from "fs";
1786
+ import * as fs12 from "fs";
1789
1787
  import * as path9 from "path";
1790
- import { z as z6 } from "zod";
1791
- import * as fs10 from "fs/promises";
1788
+ import { execSync as execSync2 } from "child_process";
1789
+ import * as fs13 from "fs";
1792
1790
  import * as path10 from "path";
1793
- import * as fs11 from "fs/promises";
1791
+ import * as fs15 from "fs/promises";
1792
+ import { z as z5 } from "zod";
1793
+ import * as fs14 from "fs";
1794
1794
  import * as path11 from "path";
1795
- import * as ejs from "ejs";
1796
- import * as fs12 from "fs";
1797
1795
  import * as path12 from "path";
1796
+ import * as path13 from "path";
1797
+ import * as path14 from "path";
1798
+ import * as path15 from "path";
1799
+ import * as fs16 from "fs";
1800
+ import * as path16 from "path";
1801
+ import { z as z6 } from "zod";
1802
+ import * as fs17 from "fs/promises";
1803
+ import * as path17 from "path";
1804
+ import * as fs18 from "fs/promises";
1805
+ import * as path18 from "path";
1806
+ import * as ejs from "ejs";
1807
+ import * as fs19 from "fs";
1808
+ import * as path19 from "path";
1798
1809
  import * as os from "os";
1799
1810
  import { spawn } from "child_process";
1800
1811
  async function validateFileStructure(projectPath, conventions) {
@@ -1832,15 +1843,15 @@ function validateConfig(data, schema) {
1832
1843
  let message = "Configuration validation failed";
1833
1844
  const suggestions = [];
1834
1845
  if (firstError) {
1835
- const path13 = firstError.path.join(".");
1836
- const pathDisplay = path13 ? ` at "${path13}"` : "";
1846
+ const path20 = firstError.path.join(".");
1847
+ const pathDisplay = path20 ? ` at "${path20}"` : "";
1837
1848
  if (firstError.code === "invalid_type") {
1838
1849
  const received = firstError.received;
1839
1850
  const expected = firstError.expected;
1840
1851
  if (received === "undefined") {
1841
1852
  code = "MISSING_FIELD";
1842
1853
  message = `Missing required field${pathDisplay}: ${firstError.message}`;
1843
- suggestions.push(`Field "${path13}" is required and must be of type "${expected}"`);
1854
+ suggestions.push(`Field "${path20}" is required and must be of type "${expected}"`);
1844
1855
  } else {
1845
1856
  code = "INVALID_TYPE";
1846
1857
  message = `Invalid type${pathDisplay}: ${firstError.message}`;
@@ -2046,30 +2057,27 @@ function extractSections(content) {
2046
2057
  return result;
2047
2058
  });
2048
2059
  }
2049
- function isExternalLink(path13) {
2050
- return path13.startsWith("http://") || path13.startsWith("https://") || path13.startsWith("#") || path13.startsWith("mailto:");
2060
+ function isExternalLink(path20) {
2061
+ return path20.startsWith("http://") || path20.startsWith("https://") || path20.startsWith("#") || path20.startsWith("mailto:");
2051
2062
  }
2052
2063
  function resolveLinkPath(linkPath, baseDir) {
2053
2064
  return linkPath.startsWith(".") ? join4(baseDir, linkPath) : linkPath;
2054
2065
  }
2055
- async function validateAgentsMap(path13 = "./AGENTS.md") {
2056
- console.warn(
2057
- "[harness] validateAgentsMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
2058
- );
2059
- const contentResult = await readFileContent(path13);
2066
+ async function validateAgentsMap(path20 = "./AGENTS.md") {
2067
+ const contentResult = await readFileContent(path20);
2060
2068
  if (!contentResult.ok) {
2061
2069
  return Err(
2062
2070
  createError(
2063
2071
  "PARSE_ERROR",
2064
2072
  `Failed to read AGENTS.md: ${contentResult.error.message}`,
2065
- { path: path13 },
2073
+ { path: path20 },
2066
2074
  ["Ensure the file exists", "Check file permissions"]
2067
2075
  )
2068
2076
  );
2069
2077
  }
2070
2078
  const content = contentResult.value;
2071
2079
  const sections = extractSections(content);
2072
- const baseDir = dirname4(path13);
2080
+ const baseDir = dirname4(path20);
2073
2081
  const sectionTitles = sections.map((s) => s.title);
2074
2082
  const missingSections = REQUIRED_SECTIONS.filter(
2075
2083
  (required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
@@ -2146,7 +2154,7 @@ async function checkDocCoverage(domain, options = {}) {
2146
2154
  try {
2147
2155
  const sourceFiles = await findFiles("**/*.{ts,js,tsx,jsx}", sourceDir);
2148
2156
  const filteredSourceFiles = sourceFiles.filter((file) => {
2149
- const relativePath = relative9(sourceDir, file);
2157
+ const relativePath = relativePosix(sourceDir, file);
2150
2158
  return !excludePatterns.some((pattern) => {
2151
2159
  return minimatch2(relativePath, pattern, { dot: true }) || minimatch2(file, pattern, { dot: true });
2152
2160
  });
@@ -2169,7 +2177,7 @@ async function checkDocCoverage(domain, options = {}) {
2169
2177
  const undocumented = [];
2170
2178
  const gaps = [];
2171
2179
  for (const sourceFile of filteredSourceFiles) {
2172
- const relativePath = relative9(sourceDir, sourceFile);
2180
+ const relativePath = relativePosix(sourceDir, sourceFile);
2173
2181
  const fileName = basename(sourceFile);
2174
2182
  const isDocumented = documentedPaths.has(relativePath) || documentedPaths.has(fileName) || documentedPaths.has(`src/${relativePath}`);
2175
2183
  if (isDocumented) {
@@ -2203,8 +2211,8 @@ async function checkDocCoverage(domain, options = {}) {
2203
2211
  );
2204
2212
  }
2205
2213
  }
2206
- function suggestFix(path13, existingFiles) {
2207
- const targetName = basename2(path13).toLowerCase();
2214
+ function suggestFix(path20, existingFiles) {
2215
+ const targetName = basename2(path20).toLowerCase();
2208
2216
  const similar = existingFiles.find((file) => {
2209
2217
  const fileName = basename2(file).toLowerCase();
2210
2218
  return fileName.includes(targetName) || targetName.includes(fileName);
@@ -2212,12 +2220,9 @@ function suggestFix(path13, existingFiles) {
2212
2220
  if (similar) {
2213
2221
  return `Did you mean "${similar}"?`;
2214
2222
  }
2215
- return `Create the file "${path13}" or remove the link`;
2223
+ return `Create the file "${path20}" or remove the link`;
2216
2224
  }
2217
2225
  async function validateKnowledgeMap(rootDir = process.cwd()) {
2218
- console.warn(
2219
- "[harness] validateKnowledgeMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
2220
- );
2221
2226
  const agentsPath = join22(rootDir, "AGENTS.md");
2222
2227
  const agentsResult = await validateAgentsMap(agentsPath);
2223
2228
  if (!agentsResult.ok) {
@@ -2229,7 +2234,7 @@ async function validateKnowledgeMap(rootDir = process.cwd()) {
2229
2234
  totalLinks: agentsTotalLinks
2230
2235
  } = agentsResult.value;
2231
2236
  const existingFiles = await findFiles("**/*", rootDir);
2232
- const relativeExistingFiles = existingFiles.map((f) => relative22(rootDir, f));
2237
+ const relativeExistingFiles = existingFiles.map((f) => relativePosix(rootDir, f));
2233
2238
  const brokenLinks = agentsBrokenLinks.map((link) => {
2234
2239
  const section = sections.find(
2235
2240
  (s) => s.links.some((l) => l.path === link.path && l.line === link.line)
@@ -2267,7 +2272,7 @@ var DEFAULT_SECTIONS = [
2267
2272
  function groupByDirectory(files, rootDir) {
2268
2273
  const groups = /* @__PURE__ */ new Map();
2269
2274
  for (const file of files) {
2270
- const relativePath = relative32(rootDir, file);
2275
+ const relativePath = relativePosix(rootDir, file);
2271
2276
  const dir = dirname22(relativePath);
2272
2277
  if (!groups.has(dir)) {
2273
2278
  groups.set(dir, []);
@@ -2323,7 +2328,7 @@ async function generateAgentsMap(config, graphSections) {
2323
2328
  allFiles.push(...files);
2324
2329
  }
2325
2330
  const filteredFiles = allFiles.filter((file) => {
2326
- const relativePath = relative32(rootDir, file);
2331
+ const relativePath = relativePosix(rootDir, file);
2327
2332
  return !matchesExcludePattern(relativePath, excludePaths);
2328
2333
  });
2329
2334
  lines.push("## Repository Structure");
@@ -2351,11 +2356,11 @@ async function generateAgentsMap(config, graphSections) {
2351
2356
  }
2352
2357
  const sectionFiles = await findFiles(section.pattern, rootDir);
2353
2358
  const filteredSectionFiles = sectionFiles.filter((file) => {
2354
- const relativePath = relative32(rootDir, file);
2359
+ const relativePath = relativePosix(rootDir, file);
2355
2360
  return !matchesExcludePattern(relativePath, excludePaths);
2356
2361
  });
2357
2362
  for (const file of filteredSectionFiles.slice(0, 20)) {
2358
- lines.push(formatFileLink(relative32(rootDir, file)));
2363
+ lines.push(formatFileLink(relativePosix(rootDir, file)));
2359
2364
  }
2360
2365
  if (filteredSectionFiles.length > 20) {
2361
2366
  lines.push(`- _... and ${filteredSectionFiles.length - 20} more files_`);
@@ -2558,8 +2563,8 @@ function createBoundaryValidator(schema, name) {
2558
2563
  return Ok(result.data);
2559
2564
  }
2560
2565
  const suggestions = result.error.issues.map((issue) => {
2561
- const path13 = issue.path.join(".");
2562
- return path13 ? `${path13}: ${issue.message}` : issue.message;
2566
+ const path20 = issue.path.join(".");
2567
+ return path20 ? `${path20}: ${issue.message}` : issue.message;
2563
2568
  });
2564
2569
  return Err(
2565
2570
  createError(
@@ -3089,11 +3094,11 @@ function walk(node, visitor) {
3089
3094
  var TypeScriptParser = class {
3090
3095
  name = "typescript";
3091
3096
  extensions = [".ts", ".tsx", ".mts", ".cts"];
3092
- async parseFile(path13) {
3093
- const contentResult = await readFileContent(path13);
3097
+ async parseFile(path20) {
3098
+ const contentResult = await readFileContent(path20);
3094
3099
  if (!contentResult.ok) {
3095
3100
  return Err(
3096
- createParseError("NOT_FOUND", `File not found: ${path13}`, { path: path13 }, [
3101
+ createParseError("NOT_FOUND", `File not found: ${path20}`, { path: path20 }, [
3097
3102
  "Check that the file exists",
3098
3103
  "Verify the path is correct"
3099
3104
  ])
@@ -3103,7 +3108,7 @@ var TypeScriptParser = class {
3103
3108
  const ast = parse(contentResult.value, {
3104
3109
  loc: true,
3105
3110
  range: true,
3106
- jsx: path13.endsWith(".tsx"),
3111
+ jsx: path20.endsWith(".tsx"),
3107
3112
  errorOnUnknownASTType: false
3108
3113
  });
3109
3114
  return Ok({
@@ -3114,7 +3119,7 @@ var TypeScriptParser = class {
3114
3119
  } catch (e) {
3115
3120
  const error = e;
3116
3121
  return Err(
3117
- createParseError("SYNTAX_ERROR", `Failed to parse ${path13}: ${error.message}`, { path: path13 }, [
3122
+ createParseError("SYNTAX_ERROR", `Failed to parse ${path20}: ${error.message}`, { path: path20 }, [
3118
3123
  "Check for syntax errors in the file",
3119
3124
  "Ensure valid TypeScript syntax"
3120
3125
  ])
@@ -3394,22 +3399,22 @@ function extractInlineRefs(content) {
3394
3399
  }
3395
3400
  return refs;
3396
3401
  }
3397
- async function parseDocumentationFile(path13) {
3398
- const contentResult = await readFileContent(path13);
3402
+ async function parseDocumentationFile(path20) {
3403
+ const contentResult = await readFileContent(path20);
3399
3404
  if (!contentResult.ok) {
3400
3405
  return Err(
3401
3406
  createEntropyError(
3402
3407
  "PARSE_ERROR",
3403
- `Failed to read documentation file: ${path13}`,
3404
- { file: path13 },
3408
+ `Failed to read documentation file: ${path20}`,
3409
+ { file: path20 },
3405
3410
  ["Check that the file exists"]
3406
3411
  )
3407
3412
  );
3408
3413
  }
3409
3414
  const content = contentResult.value;
3410
- const type = path13.endsWith(".md") ? "markdown" : "text";
3415
+ const type = path20.endsWith(".md") ? "markdown" : "text";
3411
3416
  return Ok({
3412
- path: path13,
3417
+ path: path20,
3413
3418
  type,
3414
3419
  content,
3415
3420
  codeBlocks: extractCodeBlocks(content),
@@ -3540,7 +3545,7 @@ async function buildSnapshot(config) {
3540
3545
  sourceFilePaths.push(...files2);
3541
3546
  }
3542
3547
  sourceFilePaths = sourceFilePaths.filter((f) => {
3543
- const rel = relative42(rootDir, f);
3548
+ const rel = relativePosix(rootDir, f);
3544
3549
  return !excludePatterns.some((p) => minimatch22(rel, p));
3545
3550
  });
3546
3551
  const files = [];
@@ -4064,7 +4069,7 @@ async function detectDeadCode(snapshot, graphDeadCodeData) {
4064
4069
  return Ok(report);
4065
4070
  }
4066
4071
  function fileMatchesPattern(filePath, pattern, rootDir) {
4067
- const relativePath = relative52(rootDir, filePath);
4072
+ const relativePath = relativePosix(rootDir, filePath);
4068
4073
  return minimatch3(relativePath, pattern);
4069
4074
  }
4070
4075
  function checkConfigPattern(pattern, file, rootDir) {
@@ -4210,15 +4215,34 @@ async function detectPatternViolations(snapshot, config) {
4210
4215
  }
4211
4216
  }
4212
4217
  }
4218
+ if (config?.customPatterns) {
4219
+ for (const file of snapshot.files) {
4220
+ for (const custom of config.customPatterns) {
4221
+ const matches = custom.check(file, snapshot);
4222
+ for (const match of matches) {
4223
+ violations.push({
4224
+ pattern: custom.name,
4225
+ file: file.path,
4226
+ line: match.line,
4227
+ message: match.message,
4228
+ suggestion: match.suggestion || "Review and fix this pattern violation",
4229
+ severity: custom.severity
4230
+ });
4231
+ }
4232
+ }
4233
+ }
4234
+ }
4213
4235
  const errorCount = violations.filter((v) => v.severity === "error").length;
4214
4236
  const warningCount = violations.filter((v) => v.severity === "warning").length;
4215
- const totalChecks = snapshot.files.length * patterns.length;
4216
- const passRate = totalChecks > 0 ? (totalChecks - violations.length) / totalChecks : 1;
4237
+ const customCount = config?.customPatterns?.length ?? 0;
4238
+ const allPatternsCount = patterns.length + customCount;
4239
+ const totalChecks = snapshot.files.length * allPatternsCount;
4240
+ const passRate = totalChecks > 0 ? Math.max(0, (totalChecks - violations.length) / totalChecks) : 1;
4217
4241
  return Ok({
4218
4242
  violations,
4219
4243
  stats: {
4220
4244
  filesChecked: snapshot.files.length,
4221
- patternsApplied: patterns.length,
4245
+ patternsApplied: allPatternsCount,
4222
4246
  violationCount: violations.length,
4223
4247
  errorCount,
4224
4248
  warningCount
@@ -6349,8 +6373,16 @@ var DEFAULT_STREAM_INDEX = {
6349
6373
  streams: {}
6350
6374
  };
6351
6375
  var HARNESS_DIR = ".harness";
6352
- var STREAMS_DIR = "streams";
6376
+ var STATE_FILE = "state.json";
6377
+ var LEARNINGS_FILE = "learnings.md";
6378
+ var FAILURES_FILE = "failures.md";
6379
+ var HANDOFF_FILE = "handoff.json";
6380
+ var GATE_CONFIG_FILE = "gate.json";
6353
6381
  var INDEX_FILE = "index.json";
6382
+ var SESSIONS_DIR = "sessions";
6383
+ var SESSION_INDEX_FILE = "index.md";
6384
+ var SUMMARY_FILE = "summary.md";
6385
+ var STREAMS_DIR = "streams";
6354
6386
  var STREAM_NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/;
6355
6387
  function streamsDir(projectPath) {
6356
6388
  return path2.join(projectPath, HARNESS_DIR, STREAMS_DIR);
@@ -6576,25 +6608,60 @@ async function migrateToStreams(projectPath) {
6576
6608
  };
6577
6609
  return saveStreamIndex(projectPath, index);
6578
6610
  }
6579
- var HARNESS_DIR2 = ".harness";
6580
- var STATE_FILE = "state.json";
6581
- var LEARNINGS_FILE = "learnings.md";
6582
- var FAILURES_FILE = "failures.md";
6583
- var HANDOFF_FILE = "handoff.json";
6584
- var GATE_CONFIG_FILE = "gate.json";
6585
- var INDEX_FILE2 = "index.json";
6611
+ function resolveSessionDir(projectPath, sessionSlug, options) {
6612
+ if (!sessionSlug || sessionSlug.trim() === "") {
6613
+ return Err(new Error("Session slug must not be empty"));
6614
+ }
6615
+ if (sessionSlug.includes("..") || sessionSlug.includes("/") || sessionSlug.includes("\\")) {
6616
+ return Err(
6617
+ new Error(`Invalid session slug '${sessionSlug}': must not contain path traversal characters`)
6618
+ );
6619
+ }
6620
+ const sessionDir = path3.join(projectPath, HARNESS_DIR, SESSIONS_DIR, sessionSlug);
6621
+ if (options?.create) {
6622
+ fs6.mkdirSync(sessionDir, { recursive: true });
6623
+ }
6624
+ return Ok(sessionDir);
6625
+ }
6626
+ function updateSessionIndex(projectPath, sessionSlug, description) {
6627
+ const sessionsDir = path3.join(projectPath, HARNESS_DIR, SESSIONS_DIR);
6628
+ fs6.mkdirSync(sessionsDir, { recursive: true });
6629
+ const indexPath2 = path3.join(sessionsDir, SESSION_INDEX_FILE);
6630
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6631
+ const newLine = `- [${sessionSlug}](${sessionSlug}/summary.md) \u2014 ${description} (${date})`;
6632
+ if (!fs6.existsSync(indexPath2)) {
6633
+ fs6.writeFileSync(indexPath2, `## Active Sessions
6634
+
6635
+ ${newLine}
6636
+ `);
6637
+ return;
6638
+ }
6639
+ const content = fs6.readFileSync(indexPath2, "utf-8");
6640
+ const lines = content.split("\n");
6641
+ const slugPattern = `- [${sessionSlug}]`;
6642
+ const existingIdx = lines.findIndex((l) => l.startsWith(slugPattern));
6643
+ if (existingIdx >= 0) {
6644
+ lines[existingIdx] = newLine;
6645
+ } else {
6646
+ const lastNonEmpty = lines.reduce((last, line, i) => line.trim() !== "" ? i : last, 0);
6647
+ lines.splice(lastNonEmpty + 1, 0, newLine);
6648
+ }
6649
+ fs6.writeFileSync(indexPath2, lines.join("\n"));
6650
+ }
6586
6651
  var MAX_CACHE_ENTRIES = 8;
6587
- var learningsCacheMap = /* @__PURE__ */ new Map();
6588
- var failuresCacheMap = /* @__PURE__ */ new Map();
6589
6652
  function evictIfNeeded(map) {
6590
6653
  if (map.size > MAX_CACHE_ENTRIES) {
6591
6654
  const oldest = map.keys().next().value;
6592
6655
  if (oldest !== void 0) map.delete(oldest);
6593
6656
  }
6594
6657
  }
6595
- async function getStateDir(projectPath, stream) {
6596
- const streamsIndexPath = path3.join(projectPath, HARNESS_DIR2, "streams", INDEX_FILE2);
6597
- const hasStreams = fs6.existsSync(streamsIndexPath);
6658
+ async function getStateDir(projectPath, stream, session) {
6659
+ if (session) {
6660
+ const sessionResult = resolveSessionDir(projectPath, session, { create: true });
6661
+ return sessionResult;
6662
+ }
6663
+ const streamsIndexPath = path4.join(projectPath, HARNESS_DIR, "streams", INDEX_FILE);
6664
+ const hasStreams = fs7.existsSync(streamsIndexPath);
6598
6665
  if (stream || hasStreams) {
6599
6666
  const result = await resolveStreamPath(projectPath, stream ? { stream } : void 0);
6600
6667
  if (result.ok) {
@@ -6604,18 +6671,18 @@ async function getStateDir(projectPath, stream) {
6604
6671
  return result;
6605
6672
  }
6606
6673
  }
6607
- return Ok(path3.join(projectPath, HARNESS_DIR2));
6674
+ return Ok(path4.join(projectPath, HARNESS_DIR));
6608
6675
  }
6609
- async function loadState(projectPath, stream) {
6676
+ async function loadState(projectPath, stream, session) {
6610
6677
  try {
6611
- const dirResult = await getStateDir(projectPath, stream);
6678
+ const dirResult = await getStateDir(projectPath, stream, session);
6612
6679
  if (!dirResult.ok) return dirResult;
6613
6680
  const stateDir = dirResult.value;
6614
- const statePath = path3.join(stateDir, STATE_FILE);
6615
- if (!fs6.existsSync(statePath)) {
6681
+ const statePath = path5.join(stateDir, STATE_FILE);
6682
+ if (!fs8.existsSync(statePath)) {
6616
6683
  return Ok({ ...DEFAULT_STATE });
6617
6684
  }
6618
- const raw = fs6.readFileSync(statePath, "utf-8");
6685
+ const raw = fs8.readFileSync(statePath, "utf-8");
6619
6686
  const parsed = JSON.parse(raw);
6620
6687
  const result = HarnessStateSchema.safeParse(parsed);
6621
6688
  if (!result.success) {
@@ -6628,14 +6695,14 @@ async function loadState(projectPath, stream) {
6628
6695
  );
6629
6696
  }
6630
6697
  }
6631
- async function saveState(projectPath, state, stream) {
6698
+ async function saveState(projectPath, state, stream, session) {
6632
6699
  try {
6633
- const dirResult = await getStateDir(projectPath, stream);
6700
+ const dirResult = await getStateDir(projectPath, stream, session);
6634
6701
  if (!dirResult.ok) return dirResult;
6635
6702
  const stateDir = dirResult.value;
6636
- const statePath = path3.join(stateDir, STATE_FILE);
6637
- fs6.mkdirSync(stateDir, { recursive: true });
6638
- fs6.writeFileSync(statePath, JSON.stringify(state, null, 2));
6703
+ const statePath = path5.join(stateDir, STATE_FILE);
6704
+ fs8.mkdirSync(stateDir, { recursive: true });
6705
+ fs8.writeFileSync(statePath, JSON.stringify(state, null, 2));
6639
6706
  return Ok(void 0);
6640
6707
  } catch (error) {
6641
6708
  return Err(
@@ -6643,13 +6710,17 @@ async function saveState(projectPath, state, stream) {
6643
6710
  );
6644
6711
  }
6645
6712
  }
6646
- async function appendLearning(projectPath, learning, skillName, outcome, stream) {
6713
+ var learningsCacheMap = /* @__PURE__ */ new Map();
6714
+ function clearLearningsCache() {
6715
+ learningsCacheMap.clear();
6716
+ }
6717
+ async function appendLearning(projectPath, learning, skillName, outcome, stream, session) {
6647
6718
  try {
6648
- const dirResult = await getStateDir(projectPath, stream);
6719
+ const dirResult = await getStateDir(projectPath, stream, session);
6649
6720
  if (!dirResult.ok) return dirResult;
6650
6721
  const stateDir = dirResult.value;
6651
- const learningsPath = path3.join(stateDir, LEARNINGS_FILE);
6652
- fs6.mkdirSync(stateDir, { recursive: true });
6722
+ const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
6723
+ fs9.mkdirSync(stateDir, { recursive: true });
6653
6724
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6654
6725
  let entry;
6655
6726
  if (skillName && outcome) {
@@ -6665,11 +6736,11 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
6665
6736
  - **${timestamp}:** ${learning}
6666
6737
  `;
6667
6738
  }
6668
- if (!fs6.existsSync(learningsPath)) {
6669
- fs6.writeFileSync(learningsPath, `# Learnings
6739
+ if (!fs9.existsSync(learningsPath)) {
6740
+ fs9.writeFileSync(learningsPath, `# Learnings
6670
6741
  ${entry}`);
6671
6742
  } else {
6672
- fs6.appendFileSync(learningsPath, entry);
6743
+ fs9.appendFileSync(learningsPath, entry);
6673
6744
  }
6674
6745
  learningsCacheMap.delete(learningsPath);
6675
6746
  return Ok(void 0);
@@ -6681,23 +6752,92 @@ ${entry}`);
6681
6752
  );
6682
6753
  }
6683
6754
  }
6684
- async function loadRelevantLearnings(projectPath, skillName, stream) {
6755
+ function estimateTokens(text) {
6756
+ return Math.ceil(text.length / 4);
6757
+ }
6758
+ function scoreRelevance(entry, intent) {
6759
+ if (!intent || intent.trim() === "") return 0;
6760
+ const intentWords = intent.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
6761
+ if (intentWords.length === 0) return 0;
6762
+ const entryLower = entry.toLowerCase();
6763
+ const matches = intentWords.filter((word) => entryLower.includes(word));
6764
+ return matches.length / intentWords.length;
6765
+ }
6766
+ function parseDateFromEntry(entry) {
6767
+ const match = entry.match(/(\d{4}-\d{2}-\d{2})/);
6768
+ return match ? match[1] ?? null : null;
6769
+ }
6770
+ function analyzeLearningPatterns(entries) {
6771
+ const tagGroups = /* @__PURE__ */ new Map();
6772
+ for (const entry of entries) {
6773
+ const tagMatches = entry.matchAll(/\[(skill:[^\]]+)\]|\[(outcome:[^\]]+)\]/g);
6774
+ for (const match of tagMatches) {
6775
+ const tag = match[1] ?? match[2];
6776
+ if (tag) {
6777
+ const group = tagGroups.get(tag) ?? [];
6778
+ group.push(entry);
6779
+ tagGroups.set(tag, group);
6780
+ }
6781
+ }
6782
+ }
6783
+ const patterns = [];
6784
+ for (const [tag, groupEntries] of tagGroups) {
6785
+ if (groupEntries.length >= 3) {
6786
+ patterns.push({ tag, count: groupEntries.length, entries: groupEntries });
6787
+ }
6788
+ }
6789
+ return patterns.sort((a, b) => b.count - a.count);
6790
+ }
6791
+ async function loadBudgetedLearnings(projectPath, options) {
6792
+ const { intent, tokenBudget = 1e3, skill, session, stream } = options;
6793
+ const sortByRecencyAndRelevance = (entries) => {
6794
+ return [...entries].sort((a, b) => {
6795
+ const dateA = parseDateFromEntry(a) ?? "0000-00-00";
6796
+ const dateB = parseDateFromEntry(b) ?? "0000-00-00";
6797
+ const dateCompare = dateB.localeCompare(dateA);
6798
+ if (dateCompare !== 0) return dateCompare;
6799
+ return scoreRelevance(b, intent) - scoreRelevance(a, intent);
6800
+ });
6801
+ };
6802
+ const allEntries = [];
6803
+ if (session) {
6804
+ const sessionResult = await loadRelevantLearnings(projectPath, skill, stream, session);
6805
+ if (sessionResult.ok) {
6806
+ allEntries.push(...sortByRecencyAndRelevance(sessionResult.value));
6807
+ }
6808
+ }
6809
+ const globalResult = await loadRelevantLearnings(projectPath, skill, stream);
6810
+ if (globalResult.ok) {
6811
+ allEntries.push(...sortByRecencyAndRelevance(globalResult.value));
6812
+ }
6813
+ const budgeted = [];
6814
+ let totalTokens = 0;
6815
+ for (const entry of allEntries) {
6816
+ const separator = budgeted.length > 0 ? "\n" : "";
6817
+ const entryCost = estimateTokens(entry + separator);
6818
+ if (totalTokens + entryCost > tokenBudget) break;
6819
+ budgeted.push(entry);
6820
+ totalTokens += entryCost;
6821
+ }
6822
+ return Ok(budgeted);
6823
+ }
6824
+ async function loadRelevantLearnings(projectPath, skillName, stream, session) {
6685
6825
  try {
6686
- const dirResult = await getStateDir(projectPath, stream);
6826
+ const dirResult = await getStateDir(projectPath, stream, session);
6687
6827
  if (!dirResult.ok) return dirResult;
6688
6828
  const stateDir = dirResult.value;
6689
- const learningsPath = path3.join(stateDir, LEARNINGS_FILE);
6690
- if (!fs6.existsSync(learningsPath)) {
6829
+ const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
6830
+ if (!fs9.existsSync(learningsPath)) {
6691
6831
  return Ok([]);
6692
6832
  }
6693
- const stats = fs6.statSync(learningsPath);
6833
+ const stats = fs9.statSync(learningsPath);
6694
6834
  const cacheKey = learningsPath;
6695
6835
  const cached = learningsCacheMap.get(cacheKey);
6696
6836
  let entries;
6697
6837
  if (cached && cached.mtimeMs === stats.mtimeMs) {
6698
6838
  entries = cached.entries;
6699
6839
  } else {
6700
- const content = fs6.readFileSync(learningsPath, "utf-8");
6840
+ const content = fs9.readFileSync(learningsPath, "utf-8");
6701
6841
  const lines = content.split("\n");
6702
6842
  entries = [];
6703
6843
  let currentBlock = [];
@@ -6733,23 +6873,106 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
6733
6873
  );
6734
6874
  }
6735
6875
  }
6736
- var FAILURE_LINE_REGEX = /^- \*\*(\d{4}-\d{2}-\d{2}) \[skill:([^\]]+)\] \[type:([^\]]+)\]:\*\* (.+)$/;
6737
- async function appendFailure(projectPath, description, skillName, type, stream) {
6876
+ async function archiveLearnings(projectPath, entries, stream) {
6877
+ try {
6878
+ const dirResult = await getStateDir(projectPath, stream);
6879
+ if (!dirResult.ok) return dirResult;
6880
+ const stateDir = dirResult.value;
6881
+ const archiveDir = path6.join(stateDir, "learnings-archive");
6882
+ fs9.mkdirSync(archiveDir, { recursive: true });
6883
+ const now = /* @__PURE__ */ new Date();
6884
+ const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
6885
+ const archivePath = path6.join(archiveDir, `${yearMonth}.md`);
6886
+ const archiveContent = entries.join("\n\n") + "\n";
6887
+ if (fs9.existsSync(archivePath)) {
6888
+ fs9.appendFileSync(archivePath, "\n" + archiveContent);
6889
+ } else {
6890
+ fs9.writeFileSync(archivePath, `# Learnings Archive
6891
+
6892
+ ${archiveContent}`);
6893
+ }
6894
+ return Ok(void 0);
6895
+ } catch (error) {
6896
+ return Err(
6897
+ new Error(
6898
+ `Failed to archive learnings: ${error instanceof Error ? error.message : String(error)}`
6899
+ )
6900
+ );
6901
+ }
6902
+ }
6903
+ async function pruneLearnings(projectPath, stream) {
6738
6904
  try {
6739
6905
  const dirResult = await getStateDir(projectPath, stream);
6740
6906
  if (!dirResult.ok) return dirResult;
6741
6907
  const stateDir = dirResult.value;
6742
- const failuresPath = path3.join(stateDir, FAILURES_FILE);
6743
- fs6.mkdirSync(stateDir, { recursive: true });
6908
+ const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
6909
+ if (!fs9.existsSync(learningsPath)) {
6910
+ return Ok({ kept: 0, archived: 0, patterns: [] });
6911
+ }
6912
+ const loadResult = await loadRelevantLearnings(projectPath, void 0, stream);
6913
+ if (!loadResult.ok) return loadResult;
6914
+ const allEntries = loadResult.value;
6915
+ if (allEntries.length <= 20) {
6916
+ const cutoffDate = /* @__PURE__ */ new Date();
6917
+ cutoffDate.setDate(cutoffDate.getDate() - 14);
6918
+ const cutoffStr = cutoffDate.toISOString().split("T")[0];
6919
+ const hasOld = allEntries.some((entry) => {
6920
+ const date = parseDateFromEntry(entry);
6921
+ return date !== null && date < cutoffStr;
6922
+ });
6923
+ if (!hasOld) {
6924
+ return Ok({ kept: allEntries.length, archived: 0, patterns: [] });
6925
+ }
6926
+ }
6927
+ const sorted = [...allEntries].sort((a, b) => {
6928
+ const dateA = parseDateFromEntry(a) ?? "0000-00-00";
6929
+ const dateB = parseDateFromEntry(b) ?? "0000-00-00";
6930
+ return dateB.localeCompare(dateA);
6931
+ });
6932
+ const toKeep = sorted.slice(0, 20);
6933
+ const toArchive = sorted.slice(20);
6934
+ const patterns = analyzeLearningPatterns(allEntries);
6935
+ if (toArchive.length > 0) {
6936
+ const archiveResult = await archiveLearnings(projectPath, toArchive, stream);
6937
+ if (!archiveResult.ok) return archiveResult;
6938
+ }
6939
+ const newContent = "# Learnings\n\n" + toKeep.join("\n\n") + "\n";
6940
+ fs9.writeFileSync(learningsPath, newContent);
6941
+ learningsCacheMap.delete(learningsPath);
6942
+ return Ok({
6943
+ kept: toKeep.length,
6944
+ archived: toArchive.length,
6945
+ patterns
6946
+ });
6947
+ } catch (error) {
6948
+ return Err(
6949
+ new Error(
6950
+ `Failed to prune learnings: ${error instanceof Error ? error.message : String(error)}`
6951
+ )
6952
+ );
6953
+ }
6954
+ }
6955
+ var failuresCacheMap = /* @__PURE__ */ new Map();
6956
+ function clearFailuresCache() {
6957
+ failuresCacheMap.clear();
6958
+ }
6959
+ var FAILURE_LINE_REGEX = /^- \*\*(\d{4}-\d{2}-\d{2}) \[skill:([^\]]+)\] \[type:([^\]]+)\]:\*\* (.+)$/;
6960
+ async function appendFailure(projectPath, description, skillName, type, stream, session) {
6961
+ try {
6962
+ const dirResult = await getStateDir(projectPath, stream, session);
6963
+ if (!dirResult.ok) return dirResult;
6964
+ const stateDir = dirResult.value;
6965
+ const failuresPath = path7.join(stateDir, FAILURES_FILE);
6966
+ fs10.mkdirSync(stateDir, { recursive: true });
6744
6967
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6745
6968
  const entry = `
6746
6969
  - **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
6747
6970
  `;
6748
- if (!fs6.existsSync(failuresPath)) {
6749
- fs6.writeFileSync(failuresPath, `# Failures
6971
+ if (!fs10.existsSync(failuresPath)) {
6972
+ fs10.writeFileSync(failuresPath, `# Failures
6750
6973
  ${entry}`);
6751
6974
  } else {
6752
- fs6.appendFileSync(failuresPath, entry);
6975
+ fs10.appendFileSync(failuresPath, entry);
6753
6976
  }
6754
6977
  failuresCacheMap.delete(failuresPath);
6755
6978
  return Ok(void 0);
@@ -6761,22 +6984,22 @@ ${entry}`);
6761
6984
  );
6762
6985
  }
6763
6986
  }
6764
- async function loadFailures(projectPath, stream) {
6987
+ async function loadFailures(projectPath, stream, session) {
6765
6988
  try {
6766
- const dirResult = await getStateDir(projectPath, stream);
6989
+ const dirResult = await getStateDir(projectPath, stream, session);
6767
6990
  if (!dirResult.ok) return dirResult;
6768
6991
  const stateDir = dirResult.value;
6769
- const failuresPath = path3.join(stateDir, FAILURES_FILE);
6770
- if (!fs6.existsSync(failuresPath)) {
6992
+ const failuresPath = path7.join(stateDir, FAILURES_FILE);
6993
+ if (!fs10.existsSync(failuresPath)) {
6771
6994
  return Ok([]);
6772
6995
  }
6773
- const stats = fs6.statSync(failuresPath);
6996
+ const stats = fs10.statSync(failuresPath);
6774
6997
  const cacheKey = failuresPath;
6775
6998
  const cached = failuresCacheMap.get(cacheKey);
6776
6999
  if (cached && cached.mtimeMs === stats.mtimeMs) {
6777
7000
  return Ok(cached.entries);
6778
7001
  }
6779
- const content = fs6.readFileSync(failuresPath, "utf-8");
7002
+ const content = fs10.readFileSync(failuresPath, "utf-8");
6780
7003
  const entries = [];
6781
7004
  for (const line of content.split("\n")) {
6782
7005
  const match = line.match(FAILURE_LINE_REGEX);
@@ -6800,25 +7023,25 @@ async function loadFailures(projectPath, stream) {
6800
7023
  );
6801
7024
  }
6802
7025
  }
6803
- async function archiveFailures(projectPath, stream) {
7026
+ async function archiveFailures(projectPath, stream, session) {
6804
7027
  try {
6805
- const dirResult = await getStateDir(projectPath, stream);
7028
+ const dirResult = await getStateDir(projectPath, stream, session);
6806
7029
  if (!dirResult.ok) return dirResult;
6807
7030
  const stateDir = dirResult.value;
6808
- const failuresPath = path3.join(stateDir, FAILURES_FILE);
6809
- if (!fs6.existsSync(failuresPath)) {
7031
+ const failuresPath = path7.join(stateDir, FAILURES_FILE);
7032
+ if (!fs10.existsSync(failuresPath)) {
6810
7033
  return Ok(void 0);
6811
7034
  }
6812
- const archiveDir = path3.join(stateDir, "archive");
6813
- fs6.mkdirSync(archiveDir, { recursive: true });
7035
+ const archiveDir = path7.join(stateDir, "archive");
7036
+ fs10.mkdirSync(archiveDir, { recursive: true });
6814
7037
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6815
7038
  let archiveName = `failures-${date}.md`;
6816
7039
  let counter = 2;
6817
- while (fs6.existsSync(path3.join(archiveDir, archiveName))) {
7040
+ while (fs10.existsSync(path7.join(archiveDir, archiveName))) {
6818
7041
  archiveName = `failures-${date}-${counter}.md`;
6819
7042
  counter++;
6820
7043
  }
6821
- fs6.renameSync(failuresPath, path3.join(archiveDir, archiveName));
7044
+ fs10.renameSync(failuresPath, path7.join(archiveDir, archiveName));
6822
7045
  failuresCacheMap.delete(failuresPath);
6823
7046
  return Ok(void 0);
6824
7047
  } catch (error) {
@@ -6829,14 +7052,14 @@ async function archiveFailures(projectPath, stream) {
6829
7052
  );
6830
7053
  }
6831
7054
  }
6832
- async function saveHandoff(projectPath, handoff, stream) {
7055
+ async function saveHandoff(projectPath, handoff, stream, session) {
6833
7056
  try {
6834
- const dirResult = await getStateDir(projectPath, stream);
7057
+ const dirResult = await getStateDir(projectPath, stream, session);
6835
7058
  if (!dirResult.ok) return dirResult;
6836
7059
  const stateDir = dirResult.value;
6837
- const handoffPath = path3.join(stateDir, HANDOFF_FILE);
6838
- fs6.mkdirSync(stateDir, { recursive: true });
6839
- fs6.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
7060
+ const handoffPath = path8.join(stateDir, HANDOFF_FILE);
7061
+ fs11.mkdirSync(stateDir, { recursive: true });
7062
+ fs11.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
6840
7063
  return Ok(void 0);
6841
7064
  } catch (error) {
6842
7065
  return Err(
@@ -6844,16 +7067,16 @@ async function saveHandoff(projectPath, handoff, stream) {
6844
7067
  );
6845
7068
  }
6846
7069
  }
6847
- async function loadHandoff(projectPath, stream) {
7070
+ async function loadHandoff(projectPath, stream, session) {
6848
7071
  try {
6849
- const dirResult = await getStateDir(projectPath, stream);
7072
+ const dirResult = await getStateDir(projectPath, stream, session);
6850
7073
  if (!dirResult.ok) return dirResult;
6851
7074
  const stateDir = dirResult.value;
6852
- const handoffPath = path3.join(stateDir, HANDOFF_FILE);
6853
- if (!fs6.existsSync(handoffPath)) {
7075
+ const handoffPath = path8.join(stateDir, HANDOFF_FILE);
7076
+ if (!fs11.existsSync(handoffPath)) {
6854
7077
  return Ok(null);
6855
7078
  }
6856
- const raw = fs6.readFileSync(handoffPath, "utf-8");
7079
+ const raw = fs11.readFileSync(handoffPath, "utf-8");
6857
7080
  const parsed = JSON.parse(raw);
6858
7081
  const result = HandoffSchema.safeParse(parsed);
6859
7082
  if (!result.success) {
@@ -6866,73 +7089,77 @@ async function loadHandoff(projectPath, stream) {
6866
7089
  );
6867
7090
  }
6868
7091
  }
7092
+ var SAFE_GATE_COMMAND = /^(?:npm|pnpm|yarn)\s+(?:test|run\s+[\w.-]+|run-script\s+[\w.-]+)$|^go\s+(?:test|build|vet|fmt)\s+[\w./ -]+$|^(?:python|python3)\s+-m\s+[\w.-]+$|^make\s+[\w.-]+$|^cargo\s+(?:test|build|check|clippy)(?:\s+[\w./ -]+)?$|^(?:gradle|mvn)\s+[\w:.-]+$/;
7093
+ function loadChecksFromConfig(gateConfigPath) {
7094
+ if (!fs12.existsSync(gateConfigPath)) return [];
7095
+ const raw = JSON.parse(fs12.readFileSync(gateConfigPath, "utf-8"));
7096
+ const config = GateConfigSchema.safeParse(raw);
7097
+ if (config.success && config.data.checks) return config.data.checks;
7098
+ return [];
7099
+ }
7100
+ function discoverChecksFromProject(projectPath) {
7101
+ const checks = [];
7102
+ const packageJsonPath = path9.join(projectPath, "package.json");
7103
+ if (fs12.existsSync(packageJsonPath)) {
7104
+ const pkg = JSON.parse(fs12.readFileSync(packageJsonPath, "utf-8"));
7105
+ const scripts = pkg.scripts || {};
7106
+ if (scripts.test) checks.push({ name: "test", command: "npm test" });
7107
+ if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
7108
+ if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
7109
+ if (scripts.build) checks.push({ name: "build", command: "npm run build" });
7110
+ }
7111
+ if (fs12.existsSync(path9.join(projectPath, "go.mod"))) {
7112
+ checks.push({ name: "test", command: "go test ./..." });
7113
+ checks.push({ name: "build", command: "go build ./..." });
7114
+ }
7115
+ if (fs12.existsSync(path9.join(projectPath, "pyproject.toml")) || fs12.existsSync(path9.join(projectPath, "setup.py"))) {
7116
+ checks.push({ name: "test", command: "python -m pytest" });
7117
+ }
7118
+ return checks;
7119
+ }
7120
+ function executeCheck(check, projectPath) {
7121
+ if (!SAFE_GATE_COMMAND.test(check.command)) {
7122
+ return {
7123
+ name: check.name,
7124
+ passed: false,
7125
+ command: check.command,
7126
+ output: `Blocked: command does not match safe gate pattern. Allowed prefixes: npm, pnpm, yarn, go, python, python3, make, cargo, gradle, mvn`,
7127
+ duration: 0
7128
+ };
7129
+ }
7130
+ const start = Date.now();
7131
+ try {
7132
+ execSync2(check.command, {
7133
+ cwd: projectPath,
7134
+ stdio: "pipe",
7135
+ timeout: 12e4
7136
+ });
7137
+ return {
7138
+ name: check.name,
7139
+ passed: true,
7140
+ command: check.command,
7141
+ duration: Date.now() - start
7142
+ };
7143
+ } catch (error) {
7144
+ const output = error instanceof Error ? error.stderr?.toString() || error.message : String(error);
7145
+ return {
7146
+ name: check.name,
7147
+ passed: false,
7148
+ command: check.command,
7149
+ output: output.slice(0, 2e3),
7150
+ duration: Date.now() - start
7151
+ };
7152
+ }
7153
+ }
6869
7154
  async function runMechanicalGate(projectPath) {
6870
- const harnessDir = path3.join(projectPath, HARNESS_DIR2);
6871
- const gateConfigPath = path3.join(harnessDir, GATE_CONFIG_FILE);
7155
+ const harnessDir = path9.join(projectPath, HARNESS_DIR);
7156
+ const gateConfigPath = path9.join(harnessDir, GATE_CONFIG_FILE);
6872
7157
  try {
6873
- let checks = [];
6874
- if (fs6.existsSync(gateConfigPath)) {
6875
- const raw = JSON.parse(fs6.readFileSync(gateConfigPath, "utf-8"));
6876
- const config = GateConfigSchema.safeParse(raw);
6877
- if (config.success && config.data.checks) {
6878
- checks = config.data.checks;
6879
- }
6880
- }
7158
+ let checks = loadChecksFromConfig(gateConfigPath);
6881
7159
  if (checks.length === 0) {
6882
- const packageJsonPath = path3.join(projectPath, "package.json");
6883
- if (fs6.existsSync(packageJsonPath)) {
6884
- const pkg = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
6885
- const scripts = pkg.scripts || {};
6886
- if (scripts.test) checks.push({ name: "test", command: "npm test" });
6887
- if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
6888
- if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
6889
- if (scripts.build) checks.push({ name: "build", command: "npm run build" });
6890
- }
6891
- if (fs6.existsSync(path3.join(projectPath, "go.mod"))) {
6892
- checks.push({ name: "test", command: "go test ./..." });
6893
- checks.push({ name: "build", command: "go build ./..." });
6894
- }
6895
- if (fs6.existsSync(path3.join(projectPath, "pyproject.toml")) || fs6.existsSync(path3.join(projectPath, "setup.py"))) {
6896
- checks.push({ name: "test", command: "python -m pytest" });
6897
- }
6898
- }
6899
- const results = [];
6900
- const SAFE_GATE_COMMAND = /^(?:npm|pnpm|yarn)\s+(?:test|run\s+[\w.-]+|run-script\s+[\w.-]+)$|^go\s+(?:test|build|vet|fmt)\s+[\w./ -]+$|^(?:python|python3)\s+-m\s+[\w.-]+$|^make\s+[\w.-]+$|^cargo\s+(?:test|build|check|clippy)(?:\s+[\w./ -]+)?$|^(?:gradle|mvn)\s+[\w:.-]+$/;
6901
- for (const check of checks) {
6902
- if (!SAFE_GATE_COMMAND.test(check.command)) {
6903
- results.push({
6904
- name: check.name,
6905
- passed: false,
6906
- command: check.command,
6907
- output: `Blocked: command does not match safe gate pattern. Allowed prefixes: npm, npx, pnpm, yarn, go, python, python3, make, cargo, gradle, mvn`,
6908
- duration: 0
6909
- });
6910
- continue;
6911
- }
6912
- const start = Date.now();
6913
- try {
6914
- execSync2(check.command, {
6915
- cwd: projectPath,
6916
- stdio: "pipe",
6917
- timeout: 12e4
6918
- });
6919
- results.push({
6920
- name: check.name,
6921
- passed: true,
6922
- command: check.command,
6923
- duration: Date.now() - start
6924
- });
6925
- } catch (error) {
6926
- const output = error instanceof Error ? error.stderr?.toString() || error.message : String(error);
6927
- results.push({
6928
- name: check.name,
6929
- passed: false,
6930
- command: check.command,
6931
- output: output.slice(0, 2e3),
6932
- duration: Date.now() - start
6933
- });
6934
- }
7160
+ checks = discoverChecksFromProject(projectPath);
6935
7161
  }
7162
+ const results = checks.map((check) => executeCheck(check, projectPath));
6936
7163
  return Ok({
6937
7164
  passed: results.length === 0 || results.every((r) => r.passed),
6938
7165
  checks: results
@@ -6945,6 +7172,92 @@ async function runMechanicalGate(projectPath) {
6945
7172
  );
6946
7173
  }
6947
7174
  }
7175
+ function formatSummary(data) {
7176
+ const lines = [
7177
+ "## Session Summary",
7178
+ "",
7179
+ `**Session:** ${data.session}`,
7180
+ `**Last active:** ${data.lastActive}`,
7181
+ `**Skill:** ${data.skill}`
7182
+ ];
7183
+ if (data.phase) {
7184
+ lines.push(`**Phase:** ${data.phase}`);
7185
+ }
7186
+ lines.push(`**Status:** ${data.status}`);
7187
+ if (data.spec) {
7188
+ lines.push(`**Spec:** ${data.spec}`);
7189
+ }
7190
+ if (data.plan) {
7191
+ lines.push(`**Plan:** ${data.plan}`);
7192
+ }
7193
+ lines.push(`**Key context:** ${data.keyContext}`);
7194
+ lines.push(`**Next step:** ${data.nextStep}`);
7195
+ lines.push("");
7196
+ return lines.join("\n");
7197
+ }
7198
+ function deriveIndexDescription(data) {
7199
+ const skillShort = data.skill.replace("harness-", "");
7200
+ const parts = [skillShort];
7201
+ if (data.phase) {
7202
+ parts.push(`phase ${data.phase}`);
7203
+ }
7204
+ parts.push(data.status.toLowerCase());
7205
+ return parts.join(", ");
7206
+ }
7207
+ function writeSessionSummary(projectPath, sessionSlug, data) {
7208
+ try {
7209
+ const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
7210
+ if (!dirResult.ok) return dirResult;
7211
+ const sessionDir = dirResult.value;
7212
+ const summaryPath = path10.join(sessionDir, SUMMARY_FILE);
7213
+ const content = formatSummary(data);
7214
+ fs13.writeFileSync(summaryPath, content);
7215
+ const description = deriveIndexDescription(data);
7216
+ updateSessionIndex(projectPath, sessionSlug, description);
7217
+ return Ok(void 0);
7218
+ } catch (error) {
7219
+ return Err(
7220
+ new Error(
7221
+ `Failed to write session summary: ${error instanceof Error ? error.message : String(error)}`
7222
+ )
7223
+ );
7224
+ }
7225
+ }
7226
+ function loadSessionSummary(projectPath, sessionSlug) {
7227
+ try {
7228
+ const dirResult = resolveSessionDir(projectPath, sessionSlug);
7229
+ if (!dirResult.ok) return dirResult;
7230
+ const sessionDir = dirResult.value;
7231
+ const summaryPath = path10.join(sessionDir, SUMMARY_FILE);
7232
+ if (!fs13.existsSync(summaryPath)) {
7233
+ return Ok(null);
7234
+ }
7235
+ const content = fs13.readFileSync(summaryPath, "utf-8");
7236
+ return Ok(content);
7237
+ } catch (error) {
7238
+ return Err(
7239
+ new Error(
7240
+ `Failed to load session summary: ${error instanceof Error ? error.message : String(error)}`
7241
+ )
7242
+ );
7243
+ }
7244
+ }
7245
+ function listActiveSessions(projectPath) {
7246
+ try {
7247
+ const indexPath2 = path10.join(projectPath, HARNESS_DIR, SESSIONS_DIR, SESSION_INDEX_FILE);
7248
+ if (!fs13.existsSync(indexPath2)) {
7249
+ return Ok(null);
7250
+ }
7251
+ const content = fs13.readFileSync(indexPath2, "utf-8");
7252
+ return Ok(content);
7253
+ } catch (error) {
7254
+ return Err(
7255
+ new Error(
7256
+ `Failed to list active sessions: ${error instanceof Error ? error.message : String(error)}`
7257
+ )
7258
+ );
7259
+ }
7260
+ }
6948
7261
  async function executeWorkflow(workflow, executor) {
6949
7262
  const stepResults = [];
6950
7263
  const startTime = Date.now();
@@ -7166,11 +7479,11 @@ function resolveRuleSeverity(ruleId, defaultSeverity, overrides, strict) {
7166
7479
  }
7167
7480
  function detectStack(projectRoot) {
7168
7481
  const stacks = [];
7169
- const pkgJsonPath = path4.join(projectRoot, "package.json");
7170
- if (fs7.existsSync(pkgJsonPath)) {
7482
+ const pkgJsonPath = path11.join(projectRoot, "package.json");
7483
+ if (fs14.existsSync(pkgJsonPath)) {
7171
7484
  stacks.push("node");
7172
7485
  try {
7173
- const pkgJson = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
7486
+ const pkgJson = JSON.parse(fs14.readFileSync(pkgJsonPath, "utf-8"));
7174
7487
  const allDeps = {
7175
7488
  ...pkgJson.dependencies,
7176
7489
  ...pkgJson.devDependencies
@@ -7185,13 +7498,13 @@ function detectStack(projectRoot) {
7185
7498
  } catch {
7186
7499
  }
7187
7500
  }
7188
- const goModPath = path4.join(projectRoot, "go.mod");
7189
- if (fs7.existsSync(goModPath)) {
7501
+ const goModPath = path11.join(projectRoot, "go.mod");
7502
+ if (fs14.existsSync(goModPath)) {
7190
7503
  stacks.push("go");
7191
7504
  }
7192
- const requirementsPath = path4.join(projectRoot, "requirements.txt");
7193
- const pyprojectPath = path4.join(projectRoot, "pyproject.toml");
7194
- if (fs7.existsSync(requirementsPath) || fs7.existsSync(pyprojectPath)) {
7505
+ const requirementsPath = path11.join(projectRoot, "requirements.txt");
7506
+ const pyprojectPath = path11.join(projectRoot, "pyproject.toml");
7507
+ if (fs14.existsSync(requirementsPath) || fs14.existsSync(pyprojectPath)) {
7195
7508
  stacks.push("python");
7196
7509
  }
7197
7510
  return stacks;
@@ -7594,7 +7907,7 @@ var SecurityScanner = class {
7594
7907
  }
7595
7908
  async scanFile(filePath) {
7596
7909
  if (!this.config.enabled) return [];
7597
- const content = await fs8.readFile(filePath, "utf-8");
7910
+ const content = await fs15.readFile(filePath, "utf-8");
7598
7911
  return this.scanContent(content, filePath, 1);
7599
7912
  }
7600
7913
  async scanFiles(filePaths) {
@@ -7627,238 +7940,270 @@ var ALL_CHECKS = [
7627
7940
  "phase-gate",
7628
7941
  "arch"
7629
7942
  ];
7630
- async function runSingleCheck(name, projectRoot, config) {
7631
- const start = Date.now();
7943
+ async function runValidateCheck(projectRoot, config) {
7632
7944
  const issues = [];
7633
- try {
7634
- switch (name) {
7635
- case "validate": {
7636
- const agentsPath = path5.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
7637
- const result = await validateAgentsMap(agentsPath);
7638
- if (!result.ok) {
7639
- issues.push({ severity: "error", message: result.error.message });
7640
- } else if (!result.value.valid) {
7641
- if (result.value.errors) {
7642
- for (const err of result.value.errors) {
7643
- issues.push({ severity: "error", message: err.message });
7644
- }
7645
- }
7646
- for (const section of result.value.missingSections) {
7647
- issues.push({ severity: "warning", message: `Missing section: ${section}` });
7648
- }
7649
- for (const link of result.value.brokenLinks) {
7650
- issues.push({
7651
- severity: "warning",
7652
- message: `Broken link: ${link.text} \u2192 ${link.path}`,
7653
- file: link.path
7654
- });
7655
- }
7656
- }
7657
- break;
7945
+ const agentsPath = path12.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
7946
+ const result = await validateAgentsMap(agentsPath);
7947
+ if (!result.ok) {
7948
+ issues.push({ severity: "error", message: result.error.message });
7949
+ } else if (!result.value.valid) {
7950
+ if (result.value.errors) {
7951
+ for (const err of result.value.errors) {
7952
+ issues.push({ severity: "error", message: err.message });
7658
7953
  }
7659
- case "deps": {
7660
- const rawLayers = config.layers;
7661
- if (rawLayers && rawLayers.length > 0) {
7662
- const parser = new TypeScriptParser();
7663
- const layers = rawLayers.map(
7664
- (l) => defineLayer(
7665
- l.name,
7666
- Array.isArray(l.patterns) ? l.patterns : [l.pattern],
7667
- l.allowedDependencies
7668
- )
7669
- );
7670
- const result = await validateDependencies({
7671
- layers,
7672
- rootDir: projectRoot,
7673
- parser
7674
- });
7675
- if (!result.ok) {
7676
- issues.push({ severity: "error", message: result.error.message });
7677
- } else if (result.value.violations.length > 0) {
7678
- for (const v of result.value.violations) {
7679
- issues.push({
7680
- severity: "error",
7681
- message: `${v.reason}: ${v.file} imports ${v.imports} (${v.fromLayer} \u2192 ${v.toLayer})`,
7682
- file: v.file,
7683
- line: v.line
7684
- });
7685
- }
7686
- }
7687
- }
7688
- break;
7954
+ }
7955
+ for (const section of result.value.missingSections) {
7956
+ issues.push({ severity: "warning", message: `Missing section: ${section}` });
7957
+ }
7958
+ for (const link of result.value.brokenLinks) {
7959
+ issues.push({
7960
+ severity: "warning",
7961
+ message: `Broken link: ${link.text} \u2192 ${link.path}`,
7962
+ file: link.path
7963
+ });
7964
+ }
7965
+ }
7966
+ return issues;
7967
+ }
7968
+ async function runDepsCheck(projectRoot, config) {
7969
+ const issues = [];
7970
+ const rawLayers = config.layers;
7971
+ if (rawLayers && rawLayers.length > 0) {
7972
+ const parser = new TypeScriptParser();
7973
+ const layers = rawLayers.map(
7974
+ (l) => defineLayer(
7975
+ l.name,
7976
+ Array.isArray(l.patterns) ? l.patterns : [l.pattern],
7977
+ l.allowedDependencies
7978
+ )
7979
+ );
7980
+ const result = await validateDependencies({
7981
+ layers,
7982
+ rootDir: projectRoot,
7983
+ parser
7984
+ });
7985
+ if (!result.ok) {
7986
+ issues.push({ severity: "error", message: result.error.message });
7987
+ } else if (result.value.violations.length > 0) {
7988
+ for (const v of result.value.violations) {
7989
+ issues.push({
7990
+ severity: "error",
7991
+ message: `${v.reason}: ${v.file} imports ${v.imports} (${v.fromLayer} \u2192 ${v.toLayer})`,
7992
+ file: v.file,
7993
+ line: v.line
7994
+ });
7689
7995
  }
7690
- case "docs": {
7691
- const docsDir = path5.join(projectRoot, config.docsDir ?? "docs");
7692
- const entropyConfig = config.entropy || {};
7693
- const result = await checkDocCoverage("project", {
7694
- docsDir,
7695
- sourceDir: projectRoot,
7696
- excludePatterns: entropyConfig.excludePatterns || [
7697
- "**/node_modules/**",
7698
- "**/dist/**",
7699
- "**/*.test.ts",
7700
- "**/fixtures/**"
7701
- ]
7996
+ }
7997
+ }
7998
+ return issues;
7999
+ }
8000
+ async function runDocsCheck(projectRoot, config) {
8001
+ const issues = [];
8002
+ const docsDir = path12.join(projectRoot, config.docsDir ?? "docs");
8003
+ const entropyConfig = config.entropy || {};
8004
+ const result = await checkDocCoverage("project", {
8005
+ docsDir,
8006
+ sourceDir: projectRoot,
8007
+ excludePatterns: entropyConfig.excludePatterns || [
8008
+ "**/node_modules/**",
8009
+ "**/dist/**",
8010
+ "**/*.test.ts",
8011
+ "**/fixtures/**"
8012
+ ]
8013
+ });
8014
+ if (!result.ok) {
8015
+ issues.push({ severity: "warning", message: result.error.message });
8016
+ } else if (result.value.gaps.length > 0) {
8017
+ for (const gap of result.value.gaps) {
8018
+ issues.push({
8019
+ severity: "warning",
8020
+ message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
8021
+ file: gap.file
8022
+ });
8023
+ }
8024
+ }
8025
+ return issues;
8026
+ }
8027
+ async function runEntropyCheck(projectRoot, _config) {
8028
+ const issues = [];
8029
+ const analyzer = new EntropyAnalyzer({
8030
+ rootDir: projectRoot,
8031
+ analyze: { drift: true, deadCode: true, patterns: false }
8032
+ });
8033
+ const result = await analyzer.analyze();
8034
+ if (!result.ok) {
8035
+ issues.push({ severity: "warning", message: result.error.message });
8036
+ } else {
8037
+ const report = result.value;
8038
+ if (report.drift) {
8039
+ for (const drift of report.drift.drifts) {
8040
+ issues.push({
8041
+ severity: "warning",
8042
+ message: `Doc drift (${drift.type}): ${drift.details}`,
8043
+ file: drift.docFile,
8044
+ line: drift.line
7702
8045
  });
7703
- if (!result.ok) {
7704
- issues.push({ severity: "warning", message: result.error.message });
7705
- } else if (result.value.gaps.length > 0) {
7706
- for (const gap of result.value.gaps) {
7707
- issues.push({
7708
- severity: "warning",
7709
- message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
7710
- file: gap.file
7711
- });
7712
- }
7713
- }
7714
- break;
7715
8046
  }
7716
- case "entropy": {
7717
- const analyzer = new EntropyAnalyzer({
7718
- rootDir: projectRoot,
7719
- analyze: { drift: true, deadCode: true, patterns: false }
8047
+ }
8048
+ if (report.deadCode) {
8049
+ for (const dead of report.deadCode.deadExports) {
8050
+ issues.push({
8051
+ severity: "warning",
8052
+ message: `Dead export: ${dead.name}`,
8053
+ file: dead.file,
8054
+ line: dead.line
7720
8055
  });
7721
- const result = await analyzer.analyze();
7722
- if (!result.ok) {
7723
- issues.push({ severity: "warning", message: result.error.message });
7724
- } else {
7725
- const report = result.value;
7726
- if (report.drift) {
7727
- for (const drift of report.drift.drifts) {
7728
- issues.push({
7729
- severity: "warning",
7730
- message: `Doc drift (${drift.type}): ${drift.details}`,
7731
- file: drift.docFile,
7732
- line: drift.line
7733
- });
7734
- }
7735
- }
7736
- if (report.deadCode) {
7737
- for (const dead of report.deadCode.deadExports) {
7738
- issues.push({
7739
- severity: "warning",
7740
- message: `Dead export: ${dead.name}`,
7741
- file: dead.file,
7742
- line: dead.line
7743
- });
7744
- }
7745
- }
7746
- }
7747
- break;
7748
8056
  }
7749
- case "security": {
7750
- const securityConfig = parseSecurityConfig(config.security);
7751
- if (!securityConfig.enabled) break;
7752
- const scanner = new SecurityScanner(securityConfig);
7753
- scanner.configureForProject(projectRoot);
7754
- const { glob: globFn } = await import("glob");
7755
- const sourceFiles = await globFn("**/*.{ts,tsx,js,jsx,go,py}", {
7756
- cwd: projectRoot,
7757
- ignore: securityConfig.exclude ?? [
7758
- "**/node_modules/**",
7759
- "**/dist/**",
7760
- "**/*.test.ts",
7761
- "**/fixtures/**"
7762
- ],
7763
- absolute: true
8057
+ }
8058
+ }
8059
+ return issues;
8060
+ }
8061
+ async function runSecurityCheck(projectRoot, config) {
8062
+ const issues = [];
8063
+ const securityConfig = parseSecurityConfig(config.security);
8064
+ if (!securityConfig.enabled) return issues;
8065
+ const scanner = new SecurityScanner(securityConfig);
8066
+ scanner.configureForProject(projectRoot);
8067
+ const { glob: globFn } = await import("glob");
8068
+ const sourceFiles = await globFn("**/*.{ts,tsx,js,jsx,go,py}", {
8069
+ cwd: projectRoot,
8070
+ ignore: securityConfig.exclude ?? [
8071
+ "**/node_modules/**",
8072
+ "**/dist/**",
8073
+ "**/*.test.ts",
8074
+ "**/fixtures/**"
8075
+ ],
8076
+ absolute: true
8077
+ });
8078
+ const scanResult = await scanner.scanFiles(sourceFiles);
8079
+ for (const finding of scanResult.findings) {
8080
+ issues.push({
8081
+ severity: finding.severity === "info" ? "warning" : finding.severity,
8082
+ message: `[${finding.ruleId}] ${finding.message}: ${finding.match}`,
8083
+ file: finding.file,
8084
+ line: finding.line
8085
+ });
8086
+ }
8087
+ return issues;
8088
+ }
8089
+ async function runPerfCheck(projectRoot, config) {
8090
+ const issues = [];
8091
+ const perfConfig = config.performance || {};
8092
+ const perfAnalyzer = new EntropyAnalyzer({
8093
+ rootDir: projectRoot,
8094
+ analyze: {
8095
+ complexity: perfConfig.complexity || true,
8096
+ coupling: perfConfig.coupling || true,
8097
+ sizeBudget: perfConfig.sizeBudget || false
8098
+ }
8099
+ });
8100
+ const perfResult = await perfAnalyzer.analyze();
8101
+ if (!perfResult.ok) {
8102
+ issues.push({ severity: "warning", message: perfResult.error.message });
8103
+ } else {
8104
+ const perfReport = perfResult.value;
8105
+ if (perfReport.complexity) {
8106
+ for (const v of perfReport.complexity.violations) {
8107
+ issues.push({
8108
+ severity: v.severity === "info" ? "warning" : v.severity,
8109
+ message: `[Tier ${v.tier}] ${v.metric}: ${v.function} in ${v.file} (${v.value} > ${v.threshold})`,
8110
+ file: v.file,
8111
+ line: v.line
7764
8112
  });
7765
- const scanResult = await scanner.scanFiles(sourceFiles);
7766
- for (const finding of scanResult.findings) {
7767
- issues.push({
7768
- severity: finding.severity === "info" ? "warning" : finding.severity,
7769
- message: `[${finding.ruleId}] ${finding.message}: ${finding.match}`,
7770
- file: finding.file,
7771
- line: finding.line
7772
- });
7773
- }
7774
- break;
7775
8113
  }
7776
- case "perf": {
7777
- const perfConfig = config.performance || {};
7778
- const perfAnalyzer = new EntropyAnalyzer({
7779
- rootDir: projectRoot,
7780
- analyze: {
7781
- complexity: perfConfig.complexity || true,
7782
- coupling: perfConfig.coupling || true,
7783
- sizeBudget: perfConfig.sizeBudget || false
7784
- }
8114
+ }
8115
+ if (perfReport.coupling) {
8116
+ for (const v of perfReport.coupling.violations) {
8117
+ issues.push({
8118
+ severity: v.severity === "info" ? "warning" : v.severity,
8119
+ message: `[Tier ${v.tier}] ${v.metric}: ${v.file} (${v.value} > ${v.threshold})`,
8120
+ file: v.file
7785
8121
  });
7786
- const perfResult = await perfAnalyzer.analyze();
7787
- if (!perfResult.ok) {
7788
- issues.push({ severity: "warning", message: perfResult.error.message });
7789
- } else {
7790
- const perfReport = perfResult.value;
7791
- if (perfReport.complexity) {
7792
- for (const v of perfReport.complexity.violations) {
7793
- issues.push({
7794
- severity: v.severity === "info" ? "warning" : v.severity,
7795
- message: `[Tier ${v.tier}] ${v.metric}: ${v.function} in ${v.file} (${v.value} > ${v.threshold})`,
7796
- file: v.file,
7797
- line: v.line
7798
- });
7799
- }
7800
- }
7801
- if (perfReport.coupling) {
7802
- for (const v of perfReport.coupling.violations) {
7803
- issues.push({
7804
- severity: v.severity === "info" ? "warning" : v.severity,
7805
- message: `[Tier ${v.tier}] ${v.metric}: ${v.file} (${v.value} > ${v.threshold})`,
7806
- file: v.file
7807
- });
7808
- }
7809
- }
7810
- }
7811
- break;
7812
8122
  }
7813
- case "phase-gate": {
7814
- const phaseGates = config.phaseGates;
7815
- if (!phaseGates?.enabled) {
7816
- break;
7817
- }
8123
+ }
8124
+ }
8125
+ return issues;
8126
+ }
8127
+ async function runPhaseGateCheck(_projectRoot, config) {
8128
+ const issues = [];
8129
+ const phaseGates = config.phaseGates;
8130
+ if (!phaseGates?.enabled) {
8131
+ return issues;
8132
+ }
8133
+ issues.push({
8134
+ severity: "warning",
8135
+ message: "Phase gate is enabled but requires CLI context. Run `harness check-phase-gate` separately for full validation."
8136
+ });
8137
+ return issues;
8138
+ }
8139
+ async function runArchCheck(projectRoot, config) {
8140
+ const issues = [];
8141
+ const rawArchConfig = config.architecture;
8142
+ const archConfig = ArchConfigSchema.parse(rawArchConfig ?? {});
8143
+ if (!archConfig.enabled) return issues;
8144
+ const results = await runAll(archConfig, projectRoot);
8145
+ const baselineManager = new ArchBaselineManager(projectRoot, archConfig.baselinePath);
8146
+ const baseline = baselineManager.load();
8147
+ if (baseline) {
8148
+ const diffResult = diff(results, baseline);
8149
+ if (!diffResult.passed) {
8150
+ for (const v of diffResult.newViolations) {
7818
8151
  issues.push({
7819
- severity: "warning",
7820
- message: "Phase gate is enabled but requires CLI context. Run `harness check-phase-gate` separately for full validation."
8152
+ severity: v.severity,
8153
+ message: `[${v.category || "arch"}] NEW: ${v.detail}`,
8154
+ file: v.file
7821
8155
  });
7822
- break;
7823
8156
  }
7824
- case "arch": {
7825
- const rawArchConfig = config.architecture;
7826
- const archConfig = ArchConfigSchema.parse(rawArchConfig ?? {});
7827
- if (!archConfig.enabled) break;
7828
- const results = await runAll(archConfig, projectRoot);
7829
- const baselineManager = new ArchBaselineManager(projectRoot, archConfig.baselinePath);
7830
- const baseline = baselineManager.load();
7831
- if (baseline) {
7832
- const diffResult = diff(results, baseline);
7833
- if (!diffResult.passed) {
7834
- for (const v of diffResult.newViolations) {
7835
- issues.push({
7836
- severity: v.severity,
7837
- message: `[${v.category || "arch"}] NEW: ${v.detail}`,
7838
- file: v.file
7839
- });
7840
- }
7841
- for (const r of diffResult.regressions) {
7842
- issues.push({
7843
- severity: "error",
7844
- message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
7845
- });
7846
- }
7847
- }
7848
- } else {
7849
- for (const result of results) {
7850
- for (const v of result.violations) {
7851
- issues.push({
7852
- severity: v.severity,
7853
- message: `[${result.category}] ${v.detail}`,
7854
- file: v.file
7855
- });
7856
- }
7857
- }
7858
- }
7859
- break;
8157
+ for (const r of diffResult.regressions) {
8158
+ issues.push({
8159
+ severity: "error",
8160
+ message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
8161
+ });
8162
+ }
8163
+ }
8164
+ } else {
8165
+ for (const result of results) {
8166
+ for (const v of result.violations) {
8167
+ issues.push({
8168
+ severity: v.severity,
8169
+ message: `[${result.category}] ${v.detail}`,
8170
+ file: v.file
8171
+ });
7860
8172
  }
7861
8173
  }
8174
+ }
8175
+ return issues;
8176
+ }
8177
+ async function runSingleCheck(name, projectRoot, config) {
8178
+ const start = Date.now();
8179
+ const issues = [];
8180
+ try {
8181
+ switch (name) {
8182
+ case "validate":
8183
+ issues.push(...await runValidateCheck(projectRoot, config));
8184
+ break;
8185
+ case "deps":
8186
+ issues.push(...await runDepsCheck(projectRoot, config));
8187
+ break;
8188
+ case "docs":
8189
+ issues.push(...await runDocsCheck(projectRoot, config));
8190
+ break;
8191
+ case "entropy":
8192
+ issues.push(...await runEntropyCheck(projectRoot, config));
8193
+ break;
8194
+ case "security":
8195
+ issues.push(...await runSecurityCheck(projectRoot, config));
8196
+ break;
8197
+ case "perf":
8198
+ issues.push(...await runPerfCheck(projectRoot, config));
8199
+ break;
8200
+ case "phase-gate":
8201
+ issues.push(...await runPhaseGateCheck(projectRoot, config));
8202
+ break;
8203
+ case "arch":
8204
+ issues.push(...await runArchCheck(projectRoot, config));
8205
+ break;
8206
+ }
7862
8207
  } catch (error) {
7863
8208
  issues.push({
7864
8209
  severity: "error",
@@ -7935,7 +8280,7 @@ async function runMechanicalChecks(options) {
7935
8280
  };
7936
8281
  if (!skip.includes("validate")) {
7937
8282
  try {
7938
- const agentsPath = path6.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
8283
+ const agentsPath = path13.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
7939
8284
  const result = await validateAgentsMap(agentsPath);
7940
8285
  if (!result.ok) {
7941
8286
  statuses.validate = "fail";
@@ -7972,7 +8317,7 @@ async function runMechanicalChecks(options) {
7972
8317
  statuses.validate = "fail";
7973
8318
  findings.push({
7974
8319
  tool: "validate",
7975
- file: path6.join(projectRoot, "AGENTS.md"),
8320
+ file: path13.join(projectRoot, "AGENTS.md"),
7976
8321
  message: err instanceof Error ? err.message : String(err),
7977
8322
  severity: "error"
7978
8323
  });
@@ -8036,7 +8381,7 @@ async function runMechanicalChecks(options) {
8036
8381
  (async () => {
8037
8382
  const localFindings = [];
8038
8383
  try {
8039
- const docsDir = path6.join(projectRoot, config.docsDir ?? "docs");
8384
+ const docsDir = path13.join(projectRoot, config.docsDir ?? "docs");
8040
8385
  const result = await checkDocCoverage("project", { docsDir });
8041
8386
  if (!result.ok) {
8042
8387
  statuses["check-docs"] = "warn";
@@ -8063,7 +8408,7 @@ async function runMechanicalChecks(options) {
8063
8408
  statuses["check-docs"] = "warn";
8064
8409
  localFindings.push({
8065
8410
  tool: "check-docs",
8066
- file: path6.join(projectRoot, "docs"),
8411
+ file: path13.join(projectRoot, "docs"),
8067
8412
  message: err instanceof Error ? err.message : String(err),
8068
8413
  severity: "warning"
8069
8414
  });
@@ -8212,18 +8557,18 @@ function computeContextBudget(diffLines) {
8212
8557
  return diffLines;
8213
8558
  }
8214
8559
  function isWithinProject(absPath, projectRoot) {
8215
- const resolvedRoot = path7.resolve(projectRoot) + path7.sep;
8216
- const resolvedPath = path7.resolve(absPath);
8217
- return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path7.resolve(projectRoot);
8560
+ const resolvedRoot = path14.resolve(projectRoot) + path14.sep;
8561
+ const resolvedPath = path14.resolve(absPath);
8562
+ return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path14.resolve(projectRoot);
8218
8563
  }
8219
8564
  async function readContextFile(projectRoot, filePath, reason) {
8220
- const absPath = path7.isAbsolute(filePath) ? filePath : path7.join(projectRoot, filePath);
8565
+ const absPath = path14.isAbsolute(filePath) ? filePath : path14.join(projectRoot, filePath);
8221
8566
  if (!isWithinProject(absPath, projectRoot)) return null;
8222
8567
  const result = await readFileContent(absPath);
8223
8568
  if (!result.ok) return null;
8224
8569
  const content = result.value;
8225
8570
  const lines = content.split("\n").length;
8226
- const relPath = path7.isAbsolute(filePath) ? path7.relative(projectRoot, filePath) : filePath;
8571
+ const relPath = path14.isAbsolute(filePath) ? relativePosix(projectRoot, filePath) : filePath;
8227
8572
  return { path: relPath, content, reason, lines };
8228
8573
  }
8229
8574
  function extractImportSources2(content) {
@@ -8238,18 +8583,18 @@ function extractImportSources2(content) {
8238
8583
  }
8239
8584
  async function resolveImportPath2(projectRoot, fromFile, importSource) {
8240
8585
  if (!importSource.startsWith(".")) return null;
8241
- const fromDir = path7.dirname(path7.join(projectRoot, fromFile));
8242
- const basePath = path7.resolve(fromDir, importSource);
8586
+ const fromDir = path14.dirname(path14.join(projectRoot, fromFile));
8587
+ const basePath = path14.resolve(fromDir, importSource);
8243
8588
  if (!isWithinProject(basePath, projectRoot)) return null;
8244
- const relBase = path7.relative(projectRoot, basePath);
8589
+ const relBase = relativePosix(projectRoot, basePath);
8245
8590
  const candidates = [
8246
8591
  relBase + ".ts",
8247
8592
  relBase + ".tsx",
8248
8593
  relBase + ".mts",
8249
- path7.join(relBase, "index.ts")
8594
+ path14.join(relBase, "index.ts")
8250
8595
  ];
8251
8596
  for (const candidate of candidates) {
8252
- const absCandidate = path7.join(projectRoot, candidate);
8597
+ const absCandidate = path14.join(projectRoot, candidate);
8253
8598
  if (await fileExists(absCandidate)) {
8254
8599
  return candidate;
8255
8600
  }
@@ -8257,10 +8602,10 @@ async function resolveImportPath2(projectRoot, fromFile, importSource) {
8257
8602
  return null;
8258
8603
  }
8259
8604
  async function findTestFiles(projectRoot, sourceFile) {
8260
- const baseName = path7.basename(sourceFile, path7.extname(sourceFile));
8605
+ const baseName = path14.basename(sourceFile, path14.extname(sourceFile));
8261
8606
  const pattern = `**/${baseName}.{test,spec}.{ts,tsx,mts}`;
8262
8607
  const results = await findFiles(pattern, projectRoot);
8263
- return results.map((f) => path7.relative(projectRoot, f));
8608
+ return results.map((f) => relativePosix(projectRoot, f));
8264
8609
  }
8265
8610
  async function gatherImportContext(projectRoot, changedFiles, budget) {
8266
8611
  const contextFiles = [];
@@ -9052,7 +9397,7 @@ function normalizePath(filePath, projectRoot) {
9052
9397
  let normalized = filePath;
9053
9398
  normalized = normalized.replace(/\\/g, "/");
9054
9399
  const normalizedRoot = projectRoot.replace(/\\/g, "/");
9055
- if (path8.isAbsolute(normalized)) {
9400
+ if (path15.isAbsolute(normalized)) {
9056
9401
  const root = normalizedRoot.endsWith("/") ? normalizedRoot : normalizedRoot + "/";
9057
9402
  if (normalized.startsWith(root)) {
9058
9403
  normalized = normalized.slice(root.length);
@@ -9077,12 +9422,12 @@ function followImportChain(fromFile, fileContents, maxDepth = 2) {
9077
9422
  while ((match = importRegex.exec(content)) !== null) {
9078
9423
  const importPath = match[1];
9079
9424
  if (!importPath.startsWith(".")) continue;
9080
- const dir = path8.dirname(current.file);
9081
- let resolved = path8.join(dir, importPath).replace(/\\/g, "/");
9425
+ const dir = path15.dirname(current.file);
9426
+ let resolved = path15.join(dir, importPath).replace(/\\/g, "/");
9082
9427
  if (!resolved.match(/\.(ts|tsx|js|jsx)$/)) {
9083
9428
  resolved += ".ts";
9084
9429
  }
9085
- resolved = path8.normalize(resolved).replace(/\\/g, "/");
9430
+ resolved = path15.normalize(resolved).replace(/\\/g, "/");
9086
9431
  if (!visited.has(resolved) && current.depth + 1 <= maxDepth) {
9087
9432
  queue.push({ file: resolved, depth: current.depth + 1 });
9088
9433
  }
@@ -9099,7 +9444,7 @@ async function validateFindings(options) {
9099
9444
  if (exclusionSet.isExcluded(normalizedFile, finding.lineRange) || exclusionSet.isExcluded(finding.file, finding.lineRange)) {
9100
9445
  continue;
9101
9446
  }
9102
- const absoluteFile = path8.isAbsolute(finding.file) ? finding.file : path8.join(projectRoot, finding.file).replace(/\\/g, "/");
9447
+ const absoluteFile = path15.isAbsolute(finding.file) ? finding.file : path15.join(projectRoot, finding.file).replace(/\\/g, "/");
9103
9448
  if (exclusionSet.isExcluded(absoluteFile, finding.lineRange)) {
9104
9449
  continue;
9105
9450
  }
@@ -9603,6 +9948,8 @@ function parseFrontmatter(raw) {
9603
9948
  const versionStr = map.get("version");
9604
9949
  const lastSynced = map.get("last_synced");
9605
9950
  const lastManualEdit = map.get("last_manual_edit");
9951
+ const created = map.get("created");
9952
+ const updated = map.get("updated");
9606
9953
  if (!project || !versionStr || !lastSynced || !lastManualEdit) {
9607
9954
  return Err(
9608
9955
  new Error(
@@ -9614,7 +9961,10 @@ function parseFrontmatter(raw) {
9614
9961
  if (isNaN(version)) {
9615
9962
  return Err(new Error("Frontmatter version must be a number"));
9616
9963
  }
9617
- return Ok({ project, version, lastSynced, lastManualEdit });
9964
+ const fm = { project, version, lastSynced, lastManualEdit };
9965
+ if (created) fm.created = created;
9966
+ if (updated) fm.updated = updated;
9967
+ return Ok(fm);
9618
9968
  }
9619
9969
  function parseMilestones(body) {
9620
9970
  const milestones = [];
@@ -9622,12 +9972,12 @@ function parseMilestones(body) {
9622
9972
  const h2Matches = [];
9623
9973
  let match;
9624
9974
  while ((match = h2Pattern.exec(body)) !== null) {
9625
- h2Matches.push({ heading: match[1], startIndex: match.index });
9975
+ h2Matches.push({ heading: match[1], startIndex: match.index, fullMatch: match[0] });
9626
9976
  }
9627
9977
  for (let i = 0; i < h2Matches.length; i++) {
9628
9978
  const h2 = h2Matches[i];
9629
9979
  const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex : body.length;
9630
- const sectionBody = body.slice(h2.startIndex + h2.heading.length + 4, nextStart);
9980
+ const sectionBody = body.slice(h2.startIndex + h2.fullMatch.length, nextStart);
9631
9981
  const isBacklog = h2.heading === "Backlog";
9632
9982
  const milestoneName = isBacklog ? "Backlog" : h2.heading.replace(/^Milestone:\s*/, "");
9633
9983
  const featuresResult = parseFeatures(sectionBody);
@@ -9642,19 +9992,16 @@ function parseMilestones(body) {
9642
9992
  }
9643
9993
  function parseFeatures(sectionBody) {
9644
9994
  const features = [];
9645
- const h3Pattern = /^### Feature: (.+)$/gm;
9995
+ const h3Pattern = /^### (?:Feature: )?(.+)$/gm;
9646
9996
  const h3Matches = [];
9647
9997
  let match;
9648
9998
  while ((match = h3Pattern.exec(sectionBody)) !== null) {
9649
- h3Matches.push({ name: match[1], startIndex: match.index });
9999
+ h3Matches.push({ name: match[1], startIndex: match.index, fullMatch: match[0] });
9650
10000
  }
9651
10001
  for (let i = 0; i < h3Matches.length; i++) {
9652
10002
  const h3 = h3Matches[i];
9653
10003
  const nextStart = i + 1 < h3Matches.length ? h3Matches[i + 1].startIndex : sectionBody.length;
9654
- const featureBody = sectionBody.slice(
9655
- h3.startIndex + `### Feature: ${h3.name}`.length,
9656
- nextStart
9657
- );
10004
+ const featureBody = sectionBody.slice(h3.startIndex + h3.fullMatch.length, nextStart);
9658
10005
  const featureResult = parseFeatureFields(h3.name, featureBody);
9659
10006
  if (!featureResult.ok) return featureResult;
9660
10007
  features.push(featureResult.value);
@@ -9679,10 +10026,10 @@ function parseFeatureFields(name, body) {
9679
10026
  const status = statusRaw;
9680
10027
  const specRaw = fieldMap.get("Spec") ?? EM_DASH;
9681
10028
  const spec = specRaw === EM_DASH ? null : specRaw;
9682
- const plansRaw = fieldMap.get("Plans") ?? EM_DASH;
9683
- const plans = plansRaw === EM_DASH ? [] : plansRaw.split(",").map((p) => p.trim());
9684
- const blockedByRaw = fieldMap.get("Blocked by") ?? EM_DASH;
9685
- const blockedBy = blockedByRaw === EM_DASH ? [] : blockedByRaw.split(",").map((b) => b.trim());
10029
+ const plansRaw = fieldMap.get("Plans") ?? fieldMap.get("Plan") ?? EM_DASH;
10030
+ const plans = plansRaw === EM_DASH || plansRaw === "none" ? [] : plansRaw.split(",").map((p) => p.trim());
10031
+ const blockedByRaw = fieldMap.get("Blocked by") ?? fieldMap.get("Blockers") ?? EM_DASH;
10032
+ const blockedBy = blockedByRaw === EM_DASH || blockedByRaw === "none" ? [] : blockedByRaw.split(",").map((b) => b.trim());
9686
10033
  const summary = fieldMap.get("Summary") ?? "";
9687
10034
  return Ok({ name, status, spec, plans, blockedBy, summary });
9688
10035
  }
@@ -9692,11 +10039,17 @@ function serializeRoadmap(roadmap) {
9692
10039
  lines.push("---");
9693
10040
  lines.push(`project: ${roadmap.frontmatter.project}`);
9694
10041
  lines.push(`version: ${roadmap.frontmatter.version}`);
10042
+ if (roadmap.frontmatter.created) {
10043
+ lines.push(`created: ${roadmap.frontmatter.created}`);
10044
+ }
10045
+ if (roadmap.frontmatter.updated) {
10046
+ lines.push(`updated: ${roadmap.frontmatter.updated}`);
10047
+ }
9695
10048
  lines.push(`last_synced: ${roadmap.frontmatter.lastSynced}`);
9696
10049
  lines.push(`last_manual_edit: ${roadmap.frontmatter.lastManualEdit}`);
9697
10050
  lines.push("---");
9698
10051
  lines.push("");
9699
- lines.push("# Project Roadmap");
10052
+ lines.push("# Roadmap");
9700
10053
  for (const milestone of roadmap.milestones) {
9701
10054
  lines.push("");
9702
10055
  lines.push(serializeMilestoneHeading(milestone));
@@ -9709,19 +10062,20 @@ function serializeRoadmap(roadmap) {
9709
10062
  return lines.join("\n");
9710
10063
  }
9711
10064
  function serializeMilestoneHeading(milestone) {
9712
- return milestone.isBacklog ? "## Backlog" : `## Milestone: ${milestone.name}`;
10065
+ return milestone.isBacklog ? "## Backlog" : `## ${milestone.name}`;
9713
10066
  }
9714
10067
  function serializeFeature(feature) {
9715
10068
  const spec = feature.spec ?? EM_DASH2;
9716
10069
  const plans = feature.plans.length > 0 ? feature.plans.join(", ") : EM_DASH2;
9717
10070
  const blockedBy = feature.blockedBy.length > 0 ? feature.blockedBy.join(", ") : EM_DASH2;
9718
10071
  return [
9719
- `### Feature: ${feature.name}`,
10072
+ `### ${feature.name}`,
10073
+ "",
9720
10074
  `- **Status:** ${feature.status}`,
9721
10075
  `- **Spec:** ${spec}`,
9722
- `- **Plans:** ${plans}`,
9723
- `- **Blocked by:** ${blockedBy}`,
9724
- `- **Summary:** ${feature.summary}`
10076
+ `- **Summary:** ${feature.summary}`,
10077
+ `- **Blockers:** ${blockedBy}`,
10078
+ `- **Plan:** ${plans}`
9725
10079
  ];
9726
10080
  }
9727
10081
  function inferStatus(feature, projectPath, allFeatures) {
@@ -9737,10 +10091,10 @@ function inferStatus(feature, projectPath, allFeatures) {
9737
10091
  const featuresWithPlans = allFeatures.filter((f) => f.plans.length > 0);
9738
10092
  const useRootState = featuresWithPlans.length <= 1;
9739
10093
  if (useRootState) {
9740
- const rootStatePath = path9.join(projectPath, ".harness", "state.json");
9741
- if (fs9.existsSync(rootStatePath)) {
10094
+ const rootStatePath = path16.join(projectPath, ".harness", "state.json");
10095
+ if (fs16.existsSync(rootStatePath)) {
9742
10096
  try {
9743
- const raw = fs9.readFileSync(rootStatePath, "utf-8");
10097
+ const raw = fs16.readFileSync(rootStatePath, "utf-8");
9744
10098
  const state = JSON.parse(raw);
9745
10099
  if (state.progress) {
9746
10100
  for (const status of Object.values(state.progress)) {
@@ -9751,16 +10105,16 @@ function inferStatus(feature, projectPath, allFeatures) {
9751
10105
  }
9752
10106
  }
9753
10107
  }
9754
- const sessionsDir = path9.join(projectPath, ".harness", "sessions");
9755
- if (fs9.existsSync(sessionsDir)) {
10108
+ const sessionsDir = path16.join(projectPath, ".harness", "sessions");
10109
+ if (fs16.existsSync(sessionsDir)) {
9756
10110
  try {
9757
- const sessionDirs = fs9.readdirSync(sessionsDir, { withFileTypes: true });
10111
+ const sessionDirs = fs16.readdirSync(sessionsDir, { withFileTypes: true });
9758
10112
  for (const entry of sessionDirs) {
9759
10113
  if (!entry.isDirectory()) continue;
9760
- const autopilotPath = path9.join(sessionsDir, entry.name, "autopilot-state.json");
9761
- if (!fs9.existsSync(autopilotPath)) continue;
10114
+ const autopilotPath = path16.join(sessionsDir, entry.name, "autopilot-state.json");
10115
+ if (!fs16.existsSync(autopilotPath)) continue;
9762
10116
  try {
9763
- const raw = fs9.readFileSync(autopilotPath, "utf-8");
10117
+ const raw = fs16.readFileSync(autopilotPath, "utf-8");
9764
10118
  const autopilot = JSON.parse(raw);
9765
10119
  if (!autopilot.phases) continue;
9766
10120
  const linkedPhases = autopilot.phases.filter(
@@ -9840,10 +10194,10 @@ var ProjectScanner = class {
9840
10194
  this.rootDir = rootDir;
9841
10195
  }
9842
10196
  async scan() {
9843
- let projectName = path10.basename(this.rootDir);
10197
+ let projectName = path17.basename(this.rootDir);
9844
10198
  try {
9845
- const pkgPath = path10.join(this.rootDir, "package.json");
9846
- const pkgRaw = await fs10.readFile(pkgPath, "utf-8");
10199
+ const pkgPath = path17.join(this.rootDir, "package.json");
10200
+ const pkgRaw = await fs17.readFile(pkgPath, "utf-8");
9847
10201
  const pkg = JSON.parse(pkgRaw);
9848
10202
  if (pkg.name) projectName = pkg.name;
9849
10203
  } catch {
@@ -9956,13 +10310,13 @@ var BlueprintGenerator = class {
9956
10310
  styles: STYLES,
9957
10311
  scripts: SCRIPTS
9958
10312
  });
9959
- await fs11.mkdir(options.outputDir, { recursive: true });
9960
- await fs11.writeFile(path11.join(options.outputDir, "index.html"), html);
10313
+ await fs18.mkdir(options.outputDir, { recursive: true });
10314
+ await fs18.writeFile(path18.join(options.outputDir, "index.html"), html);
9961
10315
  }
9962
10316
  };
9963
10317
  function getStatePath() {
9964
10318
  const home = process.env["HOME"] || os.homedir();
9965
- return path12.join(home, ".harness", "update-check.json");
10319
+ return path19.join(home, ".harness", "update-check.json");
9966
10320
  }
9967
10321
  function isUpdateCheckEnabled(configInterval) {
9968
10322
  if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
@@ -9975,7 +10329,7 @@ function shouldRunCheck(state, intervalMs) {
9975
10329
  }
9976
10330
  function readCheckState() {
9977
10331
  try {
9978
- const raw = fs12.readFileSync(getStatePath(), "utf-8");
10332
+ const raw = fs19.readFileSync(getStatePath(), "utf-8");
9979
10333
  const parsed = JSON.parse(raw);
9980
10334
  if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
9981
10335
  const state = parsed;
@@ -9992,7 +10346,7 @@ function readCheckState() {
9992
10346
  }
9993
10347
  function spawnBackgroundCheck(currentVersion) {
9994
10348
  const statePath = getStatePath();
9995
- const stateDir = path12.dirname(statePath);
10349
+ const stateDir = path19.dirname(statePath);
9996
10350
  const script = `
9997
10351
  const { execSync } = require('child_process');
9998
10352
  const fs = require('fs');
@@ -10044,7 +10398,7 @@ function getUpdateNotification(currentVersion) {
10044
10398
  return `Update available: v${currentVersion} -> v${state.latestVersion}
10045
10399
  Run "harness update" to upgrade.`;
10046
10400
  }
10047
- var VERSION = "0.11.0";
10401
+ var VERSION = "0.13.0";
10048
10402
 
10049
10403
  export {
10050
10404
  ArchMetricCategorySchema,
@@ -10182,16 +10536,28 @@ export {
10182
10536
  archiveStream,
10183
10537
  getStreamForBranch,
10184
10538
  migrateToStreams,
10539
+ resolveSessionDir,
10540
+ updateSessionIndex,
10185
10541
  loadState,
10186
10542
  saveState,
10543
+ clearLearningsCache,
10187
10544
  appendLearning,
10545
+ parseDateFromEntry,
10546
+ analyzeLearningPatterns,
10547
+ loadBudgetedLearnings,
10188
10548
  loadRelevantLearnings,
10549
+ archiveLearnings,
10550
+ pruneLearnings,
10551
+ clearFailuresCache,
10189
10552
  appendFailure,
10190
10553
  loadFailures,
10191
10554
  archiveFailures,
10192
10555
  saveHandoff,
10193
10556
  loadHandoff,
10194
10557
  runMechanicalGate,
10558
+ writeSessionSummary,
10559
+ loadSessionSummary,
10560
+ listActiveSessions,
10195
10561
  executeWorkflow,
10196
10562
  runPipeline,
10197
10563
  runMultiTurnPipeline,