harnery 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (445) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -2
  3. package/bin/agent-coord +42 -0
  4. package/bin/agent-hook +44 -0
  5. package/bin/harn +40 -0
  6. package/dist/cli.d.ts +9 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +18 -0
  9. package/dist/commander.d.ts +128 -0
  10. package/dist/commander.d.ts.map +1 -0
  11. package/dist/commander.js +126 -0
  12. package/dist/commands/agents.d.ts +18 -0
  13. package/dist/commands/agents.d.ts.map +1 -0
  14. package/dist/commands/agents.js +3946 -0
  15. package/dist/commands/backup.d.ts +22 -0
  16. package/dist/commands/backup.d.ts.map +1 -0
  17. package/dist/commands/backup.js +262 -0
  18. package/dist/commands/browse-ai.d.ts +4 -0
  19. package/dist/commands/browse-ai.d.ts.map +1 -0
  20. package/dist/commands/browse-ai.js +156 -0
  21. package/dist/commands/browse.d.ts +4 -0
  22. package/dist/commands/browse.d.ts.map +1 -0
  23. package/dist/commands/browse.js +590 -0
  24. package/dist/commands/callers.d.ts +4 -0
  25. package/dist/commands/callers.d.ts.map +1 -0
  26. package/dist/commands/callers.js +276 -0
  27. package/dist/commands/completion.d.ts +17 -0
  28. package/dist/commands/completion.d.ts.map +1 -0
  29. package/dist/commands/completion.js +158 -0
  30. package/dist/commands/config-get.d.ts +4 -0
  31. package/dist/commands/config-get.d.ts.map +1 -0
  32. package/dist/commands/config-get.js +131 -0
  33. package/dist/commands/context.d.ts +11 -0
  34. package/dist/commands/context.d.ts.map +1 -0
  35. package/dist/commands/context.js +185 -0
  36. package/dist/commands/cookies.d.ts +4 -0
  37. package/dist/commands/cookies.d.ts.map +1 -0
  38. package/dist/commands/cookies.js +140 -0
  39. package/dist/commands/docs.d.ts +4 -0
  40. package/dist/commands/docs.d.ts.map +1 -0
  41. package/dist/commands/docs.js +137 -0
  42. package/dist/commands/doctor.d.ts +25 -0
  43. package/dist/commands/doctor.d.ts.map +1 -0
  44. package/dist/commands/doctor.js +200 -0
  45. package/dist/commands/edit-batch.d.ts +18 -0
  46. package/dist/commands/edit-batch.d.ts.map +1 -0
  47. package/dist/commands/edit-batch.js +172 -0
  48. package/dist/commands/eml.d.ts +4 -0
  49. package/dist/commands/eml.d.ts.map +1 -0
  50. package/dist/commands/eml.js +428 -0
  51. package/dist/commands/env.d.ts +4 -0
  52. package/dist/commands/env.d.ts.map +1 -0
  53. package/dist/commands/env.js +201 -0
  54. package/dist/commands/fetch.d.ts +4 -0
  55. package/dist/commands/fetch.d.ts.map +1 -0
  56. package/dist/commands/fetch.js +99 -0
  57. package/dist/commands/file-history.d.ts +4 -0
  58. package/dist/commands/file-history.d.ts.map +1 -0
  59. package/dist/commands/file-history.js +152 -0
  60. package/dist/commands/grep.d.ts +4 -0
  61. package/dist/commands/grep.d.ts.map +1 -0
  62. package/dist/commands/grep.js +317 -0
  63. package/dist/commands/init.d.ts +82 -0
  64. package/dist/commands/init.d.ts.map +1 -0
  65. package/dist/commands/init.js +288 -0
  66. package/dist/commands/outline.d.ts +4 -0
  67. package/dist/commands/outline.d.ts.map +1 -0
  68. package/dist/commands/outline.js +494 -0
  69. package/dist/commands/presence.d.ts +12 -0
  70. package/dist/commands/presence.d.ts.map +1 -0
  71. package/dist/commands/presence.js +123 -0
  72. package/dist/commands/read.d.ts +7 -0
  73. package/dist/commands/read.d.ts.map +1 -0
  74. package/dist/commands/read.js +46 -0
  75. package/dist/commands/scratch.d.ts +4 -0
  76. package/dist/commands/scratch.d.ts.map +1 -0
  77. package/dist/commands/scratch.js +426 -0
  78. package/dist/commands/session.d.ts +4 -0
  79. package/dist/commands/session.d.ts.map +1 -0
  80. package/dist/commands/session.js +162 -0
  81. package/dist/commands/sync.d.ts +24 -0
  82. package/dist/commands/sync.d.ts.map +1 -0
  83. package/dist/commands/sync.js +275 -0
  84. package/dist/commands/toc.d.ts +5 -0
  85. package/dist/commands/toc.d.ts.map +1 -0
  86. package/dist/commands/toc.js +153 -0
  87. package/dist/commands/tokens.d.ts +4 -0
  88. package/dist/commands/tokens.d.ts.map +1 -0
  89. package/dist/commands/tokens.js +48 -0
  90. package/dist/commands/tunnel.d.ts +4 -0
  91. package/dist/commands/tunnel.d.ts.map +1 -0
  92. package/dist/commands/tunnel.js +513 -0
  93. package/dist/commands/uninstall.d.ts +22 -0
  94. package/dist/commands/uninstall.d.ts.map +1 -0
  95. package/dist/commands/uninstall.js +126 -0
  96. package/dist/commands/web.d.ts +4 -0
  97. package/dist/commands/web.d.ts.map +1 -0
  98. package/dist/commands/web.js +165 -0
  99. package/dist/core/agents/canonical-emit.d.ts +27 -0
  100. package/dist/core/agents/canonical-emit.d.ts.map +1 -0
  101. package/dist/core/agents/canonical-emit.js +72 -0
  102. package/dist/core/agents/cli-emit.d.ts +27 -0
  103. package/dist/core/agents/cli-emit.d.ts.map +1 -0
  104. package/dist/core/agents/cli-emit.js +57 -0
  105. package/dist/core/agents/cli.d.ts +10 -0
  106. package/dist/core/agents/cli.d.ts.map +1 -0
  107. package/dist/core/agents/cli.js +757 -0
  108. package/dist/core/agents/codex-replay.d.ts +29 -0
  109. package/dist/core/agents/codex-replay.d.ts.map +1 -0
  110. package/dist/core/agents/codex-replay.js +138 -0
  111. package/dist/core/agents/coord-client.d.ts +98 -0
  112. package/dist/core/agents/coord-client.d.ts.map +1 -0
  113. package/dist/core/agents/coord-client.js +212 -0
  114. package/dist/core/agents/events/consume.d.ts +59 -0
  115. package/dist/core/agents/events/consume.d.ts.map +1 -0
  116. package/dist/core/agents/events/consume.js +147 -0
  117. package/dist/core/agents/events/emit.d.ts +42 -0
  118. package/dist/core/agents/events/emit.d.ts.map +1 -0
  119. package/dist/core/agents/events/emit.js +70 -0
  120. package/dist/core/agents/events/ulid.d.ts +11 -0
  121. package/dist/core/agents/events/ulid.d.ts.map +1 -0
  122. package/dist/core/agents/events/ulid.js +47 -0
  123. package/dist/core/agents/index.d.ts +14 -0
  124. package/dist/core/agents/index.d.ts.map +1 -0
  125. package/dist/core/agents/index.js +13 -0
  126. package/dist/core/agents/paths.d.ts +6 -0
  127. package/dist/core/agents/paths.d.ts.map +1 -0
  128. package/dist/core/agents/paths.js +17 -0
  129. package/dist/core/agents/render/prompt-context.d.ts +43 -0
  130. package/dist/core/agents/render/prompt-context.d.ts.map +1 -0
  131. package/dist/core/agents/render/prompt-context.js +335 -0
  132. package/dist/core/agents/render/session-context.d.ts +39 -0
  133. package/dist/core/agents/render/session-context.d.ts.map +1 -0
  134. package/dist/core/agents/render/session-context.js +283 -0
  135. package/dist/core/agents/rules/claim-conflict.d.ts +35 -0
  136. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -0
  137. package/dist/core/agents/rules/claim-conflict.js +244 -0
  138. package/dist/core/agents/rules/commit-conflict.d.ts +59 -0
  139. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -0
  140. package/dist/core/agents/rules/commit-conflict.js +244 -0
  141. package/dist/core/agents/rules/stop-hook.d.ts +44 -0
  142. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -0
  143. package/dist/core/agents/rules/stop-hook.js +161 -0
  144. package/dist/core/agents/session-events.d.ts +41 -0
  145. package/dist/core/agents/session-events.d.ts.map +1 -0
  146. package/dist/core/agents/session-events.js +205 -0
  147. package/dist/core/agents/state/activity-log.d.ts +18 -0
  148. package/dist/core/agents/state/activity-log.d.ts.map +1 -0
  149. package/dist/core/agents/state/activity-log.js +34 -0
  150. package/dist/core/agents/state/council.d.ts +39 -0
  151. package/dist/core/agents/state/council.d.ts.map +1 -0
  152. package/dist/core/agents/state/council.js +216 -0
  153. package/dist/core/agents/state/heartbeat-projector.d.ts +59 -0
  154. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -0
  155. package/dist/core/agents/state/heartbeat-projector.js +436 -0
  156. package/dist/core/agents/state/heartbeat-writer.d.ts +64 -0
  157. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -0
  158. package/dist/core/agents/state/heartbeat-writer.js +271 -0
  159. package/dist/core/agents/state/names.d.ts +35 -0
  160. package/dist/core/agents/state/names.d.ts.map +1 -0
  161. package/dist/core/agents/state/names.js +376 -0
  162. package/dist/core/agents/state/pidmap.d.ts +11 -0
  163. package/dist/core/agents/state/pidmap.d.ts.map +1 -0
  164. package/dist/core/agents/state/pidmap.js +32 -0
  165. package/dist/core/agents/state/scratch.d.ts +27 -0
  166. package/dist/core/agents/state/scratch.d.ts.map +1 -0
  167. package/dist/core/agents/state/scratch.js +90 -0
  168. package/dist/core/agents/state/shell-mutation.d.ts +17 -0
  169. package/dist/core/agents/state/shell-mutation.d.ts.map +1 -0
  170. package/dist/core/agents/state/shell-mutation.js +41 -0
  171. package/dist/core/agents/state/stale-sweep.d.ts +16 -0
  172. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -0
  173. package/dist/core/agents/state/stale-sweep.js +166 -0
  174. package/dist/core/config.d.ts +29 -0
  175. package/dist/core/config.d.ts.map +1 -0
  176. package/dist/core/config.js +108 -0
  177. package/dist/core/hooks/cli.d.ts +21 -0
  178. package/dist/core/hooks/cli.d.ts.map +1 -0
  179. package/dist/core/hooks/cli.js +1123 -0
  180. package/dist/core/hooks/effects/image-capture.d.ts +43 -0
  181. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -0
  182. package/dist/core/hooks/effects/image-capture.js +288 -0
  183. package/dist/core/hooks/effects/index.d.ts +64 -0
  184. package/dist/core/hooks/effects/index.d.ts.map +1 -0
  185. package/dist/core/hooks/effects/index.js +197 -0
  186. package/dist/core/hooks/events/emit.d.ts +31 -0
  187. package/dist/core/hooks/events/emit.d.ts.map +1 -0
  188. package/dist/core/hooks/events/emit.js +89 -0
  189. package/dist/core/hooks/events/schema.d.ts +235 -0
  190. package/dist/core/hooks/events/schema.d.ts.map +1 -0
  191. package/dist/core/hooks/events/schema.js +12 -0
  192. package/dist/core/hooks/events/ulid.d.ts +10 -0
  193. package/dist/core/hooks/events/ulid.d.ts.map +1 -0
  194. package/dist/core/hooks/events/ulid.js +47 -0
  195. package/dist/core/hooks/harness/detect.d.ts +9 -0
  196. package/dist/core/hooks/harness/detect.d.ts.map +1 -0
  197. package/dist/core/hooks/harness/detect.js +29 -0
  198. package/dist/core/hooks/harness/events.d.ts +45 -0
  199. package/dist/core/hooks/harness/events.d.ts.map +1 -0
  200. package/dist/core/hooks/harness/events.js +71 -0
  201. package/dist/core/hooks/harness/output.d.ts +46 -0
  202. package/dist/core/hooks/harness/output.d.ts.map +1 -0
  203. package/dist/core/hooks/harness/output.js +87 -0
  204. package/dist/core/hooks/harness/parse.d.ts +67 -0
  205. package/dist/core/hooks/harness/parse.d.ts.map +1 -0
  206. package/dist/core/hooks/harness/parse.js +132 -0
  207. package/dist/core/hooks/index.d.ts +8 -0
  208. package/dist/core/hooks/index.d.ts.map +1 -0
  209. package/dist/core/hooks/index.js +7 -0
  210. package/dist/core/hooks/resolve/anchor.d.ts +37 -0
  211. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -0
  212. package/dist/core/hooks/resolve/anchor.js +48 -0
  213. package/dist/core/hooks/resolve/coord-root.d.ts +6 -0
  214. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -0
  215. package/dist/core/hooks/resolve/coord-root.js +27 -0
  216. package/dist/core/hooks/resolve/intent.d.ts +33 -0
  217. package/dist/core/hooks/resolve/intent.d.ts.map +1 -0
  218. package/dist/core/hooks/resolve/intent.js +79 -0
  219. package/dist/core/hooks/resolve/owner.d.ts +42 -0
  220. package/dist/core/hooks/resolve/owner.d.ts.map +1 -0
  221. package/dist/core/hooks/resolve/owner.js +140 -0
  222. package/dist/core/hooks/resolve/transcript.d.ts +26 -0
  223. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -0
  224. package/dist/core/hooks/resolve/transcript.js +73 -0
  225. package/dist/index.d.ts +15 -0
  226. package/dist/index.d.ts.map +1 -0
  227. package/dist/index.js +13 -0
  228. package/dist/lib/agent-browser/client.d.ts +99 -0
  229. package/dist/lib/agent-browser/client.d.ts.map +1 -0
  230. package/dist/lib/agent-browser/client.js +177 -0
  231. package/dist/lib/agent-browser/index.d.ts +2 -0
  232. package/dist/lib/agent-browser/index.d.ts.map +1 -0
  233. package/dist/lib/agent-browser/index.js +1 -0
  234. package/dist/lib/browser/client.d.ts +193 -0
  235. package/dist/lib/browser/client.d.ts.map +1 -0
  236. package/dist/lib/browser/client.js +325 -0
  237. package/dist/lib/browser/dev-overlay.d.ts +23 -0
  238. package/dist/lib/browser/dev-overlay.d.ts.map +1 -0
  239. package/dist/lib/browser/dev-overlay.js +153 -0
  240. package/dist/lib/browser/index.d.ts +5 -0
  241. package/dist/lib/browser/index.d.ts.map +1 -0
  242. package/dist/lib/browser/index.js +2 -0
  243. package/dist/lib/browser/layout.d.ts +79 -0
  244. package/dist/lib/browser/layout.d.ts.map +1 -0
  245. package/dist/lib/browser/layout.js +220 -0
  246. package/dist/lib/browser/visibility.d.ts +86 -0
  247. package/dist/lib/browser/visibility.d.ts.map +1 -0
  248. package/dist/lib/browser/visibility.js +333 -0
  249. package/dist/lib/browser/visual-diff.d.ts +38 -0
  250. package/dist/lib/browser/visual-diff.d.ts.map +1 -0
  251. package/dist/lib/browser/visual-diff.js +107 -0
  252. package/dist/lib/completion/bash.d.ts +25 -0
  253. package/dist/lib/completion/bash.d.ts.map +1 -0
  254. package/dist/lib/completion/bash.js +284 -0
  255. package/dist/lib/completion/fish.d.ts +16 -0
  256. package/dist/lib/completion/fish.d.ts.map +1 -0
  257. package/dist/lib/completion/fish.js +118 -0
  258. package/dist/lib/completion/index.d.ts +5 -0
  259. package/dist/lib/completion/index.d.ts.map +1 -0
  260. package/dist/lib/completion/index.js +4 -0
  261. package/dist/lib/completion/walk.d.ts +68 -0
  262. package/dist/lib/completion/walk.d.ts.map +1 -0
  263. package/dist/lib/completion/walk.js +102 -0
  264. package/dist/lib/completion/zsh.d.ts +13 -0
  265. package/dist/lib/completion/zsh.d.ts.map +1 -0
  266. package/dist/lib/completion/zsh.js +249 -0
  267. package/dist/lib/context/index.d.ts +107 -0
  268. package/dist/lib/context/index.d.ts.map +1 -0
  269. package/dist/lib/context/index.js +275 -0
  270. package/dist/lib/cookies/client.d.ts +131 -0
  271. package/dist/lib/cookies/client.d.ts.map +1 -0
  272. package/dist/lib/cookies/client.js +239 -0
  273. package/dist/lib/cookies/index.d.ts +2 -0
  274. package/dist/lib/cookies/index.d.ts.map +1 -0
  275. package/dist/lib/cookies/index.js +1 -0
  276. package/dist/lib/council/index.d.ts +266 -0
  277. package/dist/lib/council/index.d.ts.map +1 -0
  278. package/dist/lib/council/index.js +674 -0
  279. package/dist/lib/docs-index.d.ts +28 -0
  280. package/dist/lib/docs-index.d.ts.map +1 -0
  281. package/dist/lib/docs-index.js +169 -0
  282. package/dist/lib/docs-lint.d.ts +26 -0
  283. package/dist/lib/docs-lint.d.ts.map +1 -0
  284. package/dist/lib/docs-lint.js +378 -0
  285. package/dist/lib/docs-sweep.d.ts +34 -0
  286. package/dist/lib/docs-sweep.d.ts.map +1 -0
  287. package/dist/lib/docs-sweep.js +304 -0
  288. package/dist/lib/docs.d.ts +27 -0
  289. package/dist/lib/docs.d.ts.map +1 -0
  290. package/dist/lib/docs.js +142 -0
  291. package/dist/lib/env.d.ts +11 -0
  292. package/dist/lib/env.d.ts.map +1 -0
  293. package/dist/lib/env.js +12 -0
  294. package/dist/lib/exec.d.ts +32 -0
  295. package/dist/lib/exec.d.ts.map +1 -0
  296. package/dist/lib/exec.js +54 -0
  297. package/dist/lib/format.d.ts +29 -0
  298. package/dist/lib/format.d.ts.map +1 -0
  299. package/dist/lib/format.js +139 -0
  300. package/dist/lib/http/client.d.ts +56 -0
  301. package/dist/lib/http/client.d.ts.map +1 -0
  302. package/dist/lib/http/client.js +160 -0
  303. package/dist/lib/http/index.d.ts +2 -0
  304. package/dist/lib/http/index.d.ts.map +1 -0
  305. package/dist/lib/http/index.js +1 -0
  306. package/dist/lib/identities/index.d.ts +77 -0
  307. package/dist/lib/identities/index.d.ts.map +1 -0
  308. package/dist/lib/identities/index.js +190 -0
  309. package/dist/lib/machine.d.ts +19 -0
  310. package/dist/lib/machine.d.ts.map +1 -0
  311. package/dist/lib/machine.js +61 -0
  312. package/dist/lib/presence.d.ts +48 -0
  313. package/dist/lib/presence.d.ts.map +1 -0
  314. package/dist/lib/presence.js +123 -0
  315. package/dist/lib/readability/client.d.ts +32 -0
  316. package/dist/lib/readability/client.d.ts.map +1 -0
  317. package/dist/lib/readability/client.js +119 -0
  318. package/dist/lib/readability/index.d.ts +2 -0
  319. package/dist/lib/readability/index.d.ts.map +1 -0
  320. package/dist/lib/readability/index.js +1 -0
  321. package/dist/lib/scratch/index.d.ts +74 -0
  322. package/dist/lib/scratch/index.d.ts.map +1 -0
  323. package/dist/lib/scratch/index.js +393 -0
  324. package/dist/lib/tunnel/gate.d.ts +12 -0
  325. package/dist/lib/tunnel/gate.d.ts.map +1 -0
  326. package/dist/lib/tunnel/gate.js +101 -0
  327. package/dist/lib/tunnel/state.d.ts +34 -0
  328. package/dist/lib/tunnel/state.d.ts.map +1 -0
  329. package/dist/lib/tunnel/state.js +132 -0
  330. package/package.json +160 -8
  331. package/schemas/.gitkeep +0 -0
  332. package/schemas/config.schema.json +109 -0
  333. package/src/cli.ts +22 -0
  334. package/src/commander.ts +242 -0
  335. package/src/commands/.gitkeep +0 -0
  336. package/src/commands/agents.ts +4567 -0
  337. package/src/commands/backup.ts +305 -0
  338. package/src/commands/browse-ai.ts +198 -0
  339. package/src/commands/browse.ts +849 -0
  340. package/src/commands/callers.ts +363 -0
  341. package/src/commands/completion.ts +193 -0
  342. package/src/commands/config-get.ts +161 -0
  343. package/src/commands/context.ts +209 -0
  344. package/src/commands/cookies.ts +198 -0
  345. package/src/commands/docs.ts +174 -0
  346. package/src/commands/doctor.ts +231 -0
  347. package/src/commands/edit-batch.ts +233 -0
  348. package/src/commands/eml.ts +519 -0
  349. package/src/commands/env.ts +254 -0
  350. package/src/commands/fetch.ts +136 -0
  351. package/src/commands/file-history.ts +202 -0
  352. package/src/commands/grep.ts +371 -0
  353. package/src/commands/init.ts +335 -0
  354. package/src/commands/outline.ts +564 -0
  355. package/src/commands/presence.ts +152 -0
  356. package/src/commands/read.ts +64 -0
  357. package/src/commands/scratch.ts +445 -0
  358. package/src/commands/session.ts +187 -0
  359. package/src/commands/sync.ts +306 -0
  360. package/src/commands/toc.ts +218 -0
  361. package/src/commands/tokens.ts +79 -0
  362. package/src/commands/tunnel.ts +633 -0
  363. package/src/commands/uninstall.ts +144 -0
  364. package/src/commands/web.ts +193 -0
  365. package/src/core/agents/canonical-emit.ts +77 -0
  366. package/src/core/agents/cli-emit.ts +64 -0
  367. package/src/core/agents/cli.ts +838 -0
  368. package/src/core/agents/codex-replay.ts +163 -0
  369. package/src/core/agents/coord-client.ts +249 -0
  370. package/src/core/agents/events/consume.ts +196 -0
  371. package/src/core/agents/events/emit.ts +108 -0
  372. package/src/core/agents/events/ulid.ts +51 -0
  373. package/src/core/agents/index.ts +14 -0
  374. package/src/core/agents/paths.ts +16 -0
  375. package/src/core/agents/render/prompt-context.ts +401 -0
  376. package/src/core/agents/render/session-context.ts +341 -0
  377. package/src/core/agents/rules/claim-conflict.ts +282 -0
  378. package/src/core/agents/rules/commit-conflict.ts +303 -0
  379. package/src/core/agents/rules/stop-hook.ts +229 -0
  380. package/src/core/agents/session-events.ts +228 -0
  381. package/src/core/agents/state/activity-log.ts +33 -0
  382. package/src/core/agents/state/council.ts +265 -0
  383. package/src/core/agents/state/heartbeat-projector.ts +488 -0
  384. package/src/core/agents/state/heartbeat-writer.ts +333 -0
  385. package/src/core/agents/state/names.ts +399 -0
  386. package/src/core/agents/state/pidmap.ts +38 -0
  387. package/src/core/agents/state/scratch.ts +121 -0
  388. package/src/core/agents/state/shell-mutation.ts +44 -0
  389. package/src/core/agents/state/stale-sweep.ts +190 -0
  390. package/src/core/config.ts +111 -0
  391. package/src/core/hooks/cli.ts +1247 -0
  392. package/src/core/hooks/effects/image-capture.ts +330 -0
  393. package/src/core/hooks/effects/index.ts +210 -0
  394. package/src/core/hooks/events/emit.ts +120 -0
  395. package/src/core/hooks/events/schema.ts +430 -0
  396. package/src/core/hooks/events/ulid.ts +51 -0
  397. package/src/core/hooks/harness/detect.ts +30 -0
  398. package/src/core/hooks/harness/events.ts +102 -0
  399. package/src/core/hooks/harness/output.ts +100 -0
  400. package/src/core/hooks/harness/parse.ts +180 -0
  401. package/src/core/hooks/index.ts +16 -0
  402. package/src/core/hooks/resolve/anchor.ts +51 -0
  403. package/src/core/hooks/resolve/coord-root.ts +25 -0
  404. package/src/core/hooks/resolve/intent.ts +89 -0
  405. package/src/core/hooks/resolve/owner.ts +140 -0
  406. package/src/core/hooks/resolve/transcript.ts +72 -0
  407. package/src/hooks/.gitkeep +0 -0
  408. package/src/index.ts +15 -0
  409. package/src/lib/agent-browser/client.ts +239 -0
  410. package/src/lib/agent-browser/index.ts +1 -0
  411. package/src/lib/browser/client.ts +449 -0
  412. package/src/lib/browser/dev-overlay.ts +207 -0
  413. package/src/lib/browser/index.ts +24 -0
  414. package/src/lib/browser/layout.ts +288 -0
  415. package/src/lib/browser/visibility.ts +419 -0
  416. package/src/lib/browser/visual-diff.ts +150 -0
  417. package/src/lib/completion/bash.ts +291 -0
  418. package/src/lib/completion/fish.ts +134 -0
  419. package/src/lib/completion/index.ts +10 -0
  420. package/src/lib/completion/walk.ts +184 -0
  421. package/src/lib/completion/zsh.ts +262 -0
  422. package/src/lib/context/index.ts +386 -0
  423. package/src/lib/cookies/client.ts +301 -0
  424. package/src/lib/cookies/index.ts +13 -0
  425. package/src/lib/council/index.ts +803 -0
  426. package/src/lib/docs-index.ts +216 -0
  427. package/src/lib/docs-lint.ts +413 -0
  428. package/src/lib/docs-sweep.ts +348 -0
  429. package/src/lib/docs.ts +199 -0
  430. package/src/lib/env.ts +12 -0
  431. package/src/lib/exec.ts +74 -0
  432. package/src/lib/format.ts +147 -0
  433. package/src/lib/http/client.ts +211 -0
  434. package/src/lib/http/index.ts +1 -0
  435. package/src/lib/identities/index.ts +210 -0
  436. package/src/lib/machine.ts +61 -0
  437. package/src/lib/presence.ts +154 -0
  438. package/src/lib/readability/client.ts +156 -0
  439. package/src/lib/readability/index.ts +5 -0
  440. package/src/lib/readability/turndown-plugin-gfm.d.ts +10 -0
  441. package/src/lib/scratch/index.ts +470 -0
  442. package/src/lib/tunnel/gate.ts +113 -0
  443. package/src/lib/tunnel/state.ts +167 -0
  444. package/src/web/.gitkeep +0 -0
  445. package/index.js +0 -1
@@ -0,0 +1,99 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { resolve } from "node:path";
4
+ import { CookieJar } from "../lib/cookies/index.js";
5
+ import { fetchWithJar } from "../lib/http/index.js";
6
+ /**
7
+ * `harn fetch`: HTTP request with cookie-jar attach + persist.
8
+ *
9
+ * Default jar is the same one `harn cookies` reads/writes
10
+ * (`~/.cache/harnery/cookies.json`), so cookies set during a `harn browse`
11
+ * session flow naturally to subsequent `harn fetch` calls.
12
+ */
13
+ const DEFAULT_STORE = resolve(homedir(), ".cache", "harnery", "cookies.json");
14
+ export function registerFetchCommand(program, emit, context) {
15
+ program
16
+ .command("fetch <url>")
17
+ .description("HTTP GET (or --method) with cookie-jar attach + persist. " +
18
+ "Default jar is ~/.cache/harnery/cookies.json (shared with harn cookies / harn browse).")
19
+ .option("-X, --method <method>", "HTTP method", "GET")
20
+ .option("-H, --header <header...>", "Extra request header (repeatable, format: 'Name: value')")
21
+ .option("-d, --data <body>", "Request body")
22
+ .option("-o, --output <file>", "Write response body to file (default: stdout)")
23
+ .option("--store <path>", `Cookie store path (default ${DEFAULT_STORE})`)
24
+ .option("--no-cookies", "Skip cookie-jar attach + persist")
25
+ .option("--redirect <mode>", "Redirect handling: follow | error | manual", "follow")
26
+ .option("--timeout <ms>", "Request timeout in milliseconds", "30000")
27
+ .option("--status", "Print status to stderr")
28
+ .option("--headers", "Print response headers to stderr")
29
+ .option("--json", "Output the full FetchResult (status, headers, body) as JSON")
30
+ .action(async (url, opts) => {
31
+ try {
32
+ await runFetch(url, opts, emit, context);
33
+ }
34
+ catch (err) {
35
+ const msg = err instanceof Error ? err.message : String(err);
36
+ emit.error({ code: "fetch_error", message: msg });
37
+ process.exit(1);
38
+ }
39
+ });
40
+ }
41
+ async function runFetch(url, opts, emit, context) {
42
+ const headers = {};
43
+ for (const h of opts.header ?? []) {
44
+ const idx = h.indexOf(":");
45
+ if (idx < 0) {
46
+ throw new Error(`Bad header (expected 'Name: value'): ${h}`);
47
+ }
48
+ headers[h.slice(0, idx).trim()] = h.slice(idx + 1).trim();
49
+ }
50
+ const jar = opts.cookies !== false
51
+ ? new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "bp-fetch" })
52
+ : null;
53
+ const timeoutMs = Number.parseInt(opts.timeout, 10);
54
+ const ac = new AbortController();
55
+ const timer = setTimeout(() => ac.abort(), timeoutMs);
56
+ let result;
57
+ try {
58
+ result = await fetchWithJar(url, {
59
+ method: opts.method,
60
+ headers,
61
+ body: opts.data,
62
+ jar,
63
+ redirect: opts.redirect,
64
+ signal: ac.signal,
65
+ extraHeaders: context?.extraHeaders,
66
+ });
67
+ }
68
+ finally {
69
+ clearTimeout(timer);
70
+ }
71
+ if (opts.json) {
72
+ emit.data(result);
73
+ return;
74
+ }
75
+ if (opts.status) {
76
+ emit.log(`${result.status} ${result.statusText} ${result.url}`, "info");
77
+ }
78
+ if (opts.headers) {
79
+ for (const [k, v] of Object.entries(result.headers)) {
80
+ emit.log(`${k}: ${v}`, "info");
81
+ }
82
+ }
83
+ if (opts.output) {
84
+ writeFileSync(opts.output, result.body);
85
+ emit.file(opts.output, {
86
+ bytes: result.body.length,
87
+ status: result.status,
88
+ status_text: result.statusText,
89
+ });
90
+ }
91
+ else {
92
+ // Body is potentially binary; route as text since most fetch responses
93
+ // are text/HTML/JSON. Binary callers should use --output.
94
+ emit.text(result.body);
95
+ }
96
+ if (jar && result.cookiesSaved > 0) {
97
+ emit.log(`saved ${result.cookiesSaved} cookie${result.cookiesSaved === 1 ? "" : "s"} to ${jar.path}`, "info");
98
+ }
99
+ }
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ import type { EmitContext } from "../commander.js";
3
+ export declare function registerFileHistoryCommand(program: Command, emit: EmitContext): void;
4
+ //# sourceMappingURL=file-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-history.d.ts","sourceRoot":"","sources":["../../src/commands/file-history.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAwCnD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAwBpF"}
@@ -0,0 +1,152 @@
1
+ import { existsSync } from "node:fs";
2
+ import { basename, dirname, relative, resolve } from "node:path";
3
+ import { exec } from "../lib/exec.js";
4
+ export function registerFileHistoryCommand(program, emit) {
5
+ program
6
+ .command("file-history <path>")
7
+ .description("Concise per-file git history: summary stats + N most-recent commits with line impact.")
8
+ .option("--limit <n>", "Show N most-recent commits", "20")
9
+ .option("--since <date>", "Restrict to commits since DATE (e.g. '30 days ago', '2026-01-01')")
10
+ .option("--author <pat>", "Filter to commits by author (substring match)")
11
+ .option("--json", "Structured JSON envelope")
12
+ .action(async (path, opts) => {
13
+ try {
14
+ const result = await runFileHistory(path, opts);
15
+ if (opts.json) {
16
+ emit.config({ format: "json" });
17
+ emit.data(result);
18
+ return;
19
+ }
20
+ emit.text(`${renderFileHistory(result)}\n`);
21
+ }
22
+ catch (err) {
23
+ emit.error({ code: "file_history_failed", message: err.message });
24
+ process.exit(1);
25
+ }
26
+ });
27
+ }
28
+ async function runFileHistory(path, opts) {
29
+ const absPath = resolve(path);
30
+ if (!existsSync(absPath))
31
+ throw new Error(`no such file: ${path}`);
32
+ const repo = await detectRepo(absPath);
33
+ const relPath = relative(repo.cwd, absPath);
34
+ if (relPath.startsWith("..")) {
35
+ throw new Error(`path outside detected repo (${repo.cwd}): ${absPath}`);
36
+ }
37
+ const args = [
38
+ "log",
39
+ "--no-merges",
40
+ "--follow",
41
+ "--pretty=format:%H%x09%an%x09%aI%x09%s",
42
+ "--shortstat",
43
+ ];
44
+ if (opts.since)
45
+ args.push(`--since=${opts.since}`);
46
+ if (opts.author)
47
+ args.push(`--author=${opts.author}`);
48
+ args.push("--", relPath);
49
+ const log = await exec(["git", ...args], { cwd: repo.cwd, timeout: 30_000 });
50
+ if (log.exitCode !== 0)
51
+ throw new Error(`git log failed: ${log.stderr || log.stdout}`);
52
+ const commits = parseGitLog(log.stdout);
53
+ const authorMap = new Map();
54
+ let totalAdded = 0;
55
+ let totalRemoved = 0;
56
+ for (const c of commits) {
57
+ authorMap.set(c.author, (authorMap.get(c.author) || 0) + 1);
58
+ totalAdded += c.added;
59
+ totalRemoved += c.removed;
60
+ }
61
+ const authors = Array.from(authorMap.entries())
62
+ .map(([name, commits]) => ({ name, commits }))
63
+ .sort((a, b) => b.commits - a.commits);
64
+ const limit = opts.limit ? Number.parseInt(opts.limit, 10) : 20;
65
+ const recentCommits = commits.slice(0, Number.isFinite(limit) ? limit : 20);
66
+ return {
67
+ file: relPath,
68
+ repo: repo.name,
69
+ total_commits: commits.length,
70
+ authors,
71
+ first_commit: commits.length > 0
72
+ ? { sha: commits[commits.length - 1].short_sha, date: commits[commits.length - 1].date }
73
+ : null,
74
+ last_commit: commits.length > 0 ? { sha: commits[0].short_sha, date: commits[0].date } : null,
75
+ total_added: totalAdded,
76
+ total_removed: totalRemoved,
77
+ commits: recentCommits,
78
+ };
79
+ }
80
+ function parseGitLog(output) {
81
+ const commits = [];
82
+ const lines = output.split("\n");
83
+ let current = null;
84
+ for (const line of lines) {
85
+ if (line.includes("\t")) {
86
+ if (current)
87
+ commits.push(current);
88
+ const [sha, author, date, ...rest] = line.split("\t");
89
+ current = {
90
+ sha: sha,
91
+ short_sha: sha.slice(0, 8),
92
+ author: author,
93
+ date: date,
94
+ subject: rest.join("\t"),
95
+ added: 0,
96
+ removed: 0,
97
+ };
98
+ }
99
+ else if (current && /\bfile[s]?\b.*changed/.test(line)) {
100
+ const ins = line.match(/(\d+) insertion/);
101
+ const del = line.match(/(\d+) deletion/);
102
+ if (ins)
103
+ current.added = Number.parseInt(ins[1], 10);
104
+ if (del)
105
+ current.removed = Number.parseInt(del[1], 10);
106
+ }
107
+ }
108
+ if (current)
109
+ commits.push(current);
110
+ return commits;
111
+ }
112
+ async function detectRepo(absPath) {
113
+ const dir = dirname(absPath);
114
+ const result = await exec(["git", "rev-parse", "--show-toplevel"], { cwd: dir });
115
+ if (result.exitCode !== 0) {
116
+ throw new Error(`not inside a git repository: ${absPath}`);
117
+ }
118
+ const cwd = result.stdout.trim();
119
+ return { name: basename(cwd), cwd };
120
+ }
121
+ function renderFileHistory(r) {
122
+ const lines = [];
123
+ lines.push(`file-history · ${r.repo}/${r.file}`);
124
+ lines.push("");
125
+ if (r.total_commits === 0) {
126
+ lines.push("(no commits found for this path)");
127
+ return lines.join("\n");
128
+ }
129
+ lines.push(`summary: ${r.total_commits} commit${r.total_commits === 1 ? "" : "s"} by ${r.authors.length} author${r.authors.length === 1 ? "" : "s"}, +${r.total_added}/-${r.total_removed} lines`);
130
+ if (r.last_commit)
131
+ lines.push(` last: ${r.last_commit.sha} ${r.last_commit.date}`);
132
+ if (r.first_commit)
133
+ lines.push(` first: ${r.first_commit.sha} ${r.first_commit.date}`);
134
+ if (r.authors.length > 0) {
135
+ lines.push("");
136
+ lines.push("authors:");
137
+ for (const a of r.authors.slice(0, 5)) {
138
+ lines.push(` ${a.commits.toString().padStart(3)} ${a.name}`);
139
+ }
140
+ if (r.authors.length > 5)
141
+ lines.push(` ... +${r.authors.length - 5} more`);
142
+ }
143
+ lines.push("");
144
+ lines.push(`recent (showing ${r.commits.length} of ${r.total_commits}):`);
145
+ for (const c of r.commits) {
146
+ const dateOnly = c.date.slice(0, 10);
147
+ const sig = `+${c.added}/-${c.removed}`.padStart(10);
148
+ const author = c.author.slice(0, 18).padEnd(18);
149
+ lines.push(` ${c.short_sha} ${dateOnly} ${sig} ${author} ${c.subject}`);
150
+ }
151
+ return lines.join("\n");
152
+ }
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ import type { EmitContext, HarneryProgramContext } from "../commander.js";
3
+ export declare function registerGrepCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
4
+ //# sourceMappingURL=grep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/commands/grep.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AA4E1E,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CAoCN"}
@@ -0,0 +1,317 @@
1
+ import { spawn } from "node:child_process";
2
+ /**
3
+ * `grep`: monorepo-aware code search. Thin wrapper over grep -rn with
4
+ * smart default excludes (skip dist/.next/node_modules/.git/...), repo
5
+ * scoping (`--repo <name>` or `--all-repos`), and language presets.
6
+ *
7
+ * Default behavior matches grep's "regex" semantics (`-E` extended). Use
8
+ * `-F` / `--literal` to pin to literal-string mode. Output is line-oriented
9
+ * `file:line:content` in TTY mode, `{rows, total, truncated}` in --json mode.
10
+ */
11
+ const DEFAULT_EXCLUDE_DIRS = [
12
+ ".git",
13
+ ".cache",
14
+ "node_modules",
15
+ "dist",
16
+ "build",
17
+ "out",
18
+ ".next",
19
+ "vendor",
20
+ "__pycache__",
21
+ ".venv",
22
+ "tmp",
23
+ "tmp-files",
24
+ "coverage",
25
+ ".parcel-cache",
26
+ ".turbo",
27
+ ".harnery",
28
+ ];
29
+ // (no file-name excludes by default).
30
+ const DEFAULT_EXCLUDE_FILES = [];
31
+ const LANG_GLOBS = {
32
+ ts: ["*.ts"],
33
+ tsx: ["*.tsx"],
34
+ js: ["*.js", "*.mjs", "*.cjs"],
35
+ jsx: ["*.jsx"],
36
+ py: ["*.py"],
37
+ php: ["*.php"],
38
+ sql: ["*.sql"],
39
+ md: ["*.md", "*.mdx"],
40
+ sh: ["*.sh", "*.bash"],
41
+ json: ["*.json"],
42
+ yaml: ["*.yaml", "*.yml"],
43
+ rb: ["*.rb"],
44
+ go: ["*.go"],
45
+ rs: ["*.rs"],
46
+ };
47
+ export function registerGrepCommand(program, emit, context) {
48
+ program
49
+ .command("grep <pattern> [paths...]")
50
+ .description("Monorepo-aware code search. Skips dist/.next/node_modules/.git/... by default. " +
51
+ "Use --repo, --all-repos, --lang for scoping. Regex by default; -F for literal.")
52
+ .option("--repo <name>", "Scope to one submodule (`.` = parent repo root)")
53
+ .option("--all-repos", "Search parent + every submodule")
54
+ .option("--lang <lang>", `File type preset (${Object.keys(LANG_GLOBS).join(", ")})`)
55
+ .option("-i, --ignore-case", "Case-insensitive match")
56
+ .option("-w, --whole-word", "Match whole words only")
57
+ .option("-F, --literal", "Treat <pattern> as a literal string (no regex)")
58
+ .option("-l, --files-only", "Only print file names containing a match")
59
+ .option("-c, --count", "Print match count per file (suppresses content)")
60
+ .option("-C, --context <n>", "Print N lines of context around each match", "0")
61
+ .option("--max-count <n>", "Stop after N matches per file")
62
+ .option("--limit <n>", "Truncate output to N matches total")
63
+ .option("--include <glob>", "Extra --include glob (repeatable)", collect, [])
64
+ .option("--exclude <glob>", "Extra --exclude glob (repeatable)", collect, [])
65
+ .option("--no-default-excludes", "Disable the default skip list (node_modules, dist, etc.)")
66
+ .option("--json", "Structured JSON envelope")
67
+ .action(async (pattern, paths, opts) => {
68
+ try {
69
+ const result = await runGrep(pattern, paths, opts, context);
70
+ if (opts.json) {
71
+ emit.config({ format: "json" });
72
+ emit.data(result);
73
+ return;
74
+ }
75
+ emit.text(`${renderResult(result, opts)}\n`);
76
+ }
77
+ catch (err) {
78
+ emit.error({ code: "grep_failed", message: err.message });
79
+ process.exit(1);
80
+ }
81
+ });
82
+ }
83
+ function collect(value, prev) {
84
+ return [...prev, value];
85
+ }
86
+ async function runGrep(pattern, paths, opts, context) {
87
+ if (!pattern)
88
+ throw new Error("pattern required");
89
+ const started = Date.now();
90
+ const repos = resolveRepos(opts, context);
91
+ const limit = opts.limit ? Number.parseInt(opts.limit, 10) : Number.POSITIVE_INFINITY;
92
+ const allRepoResults = [];
93
+ let totalMatches = 0;
94
+ const filesSeen = new Set();
95
+ let truncated = false;
96
+ for (const repo of repos) {
97
+ if (truncated)
98
+ break;
99
+ const repoLimit = Number.isFinite(limit)
100
+ ? Math.max(0, limit - totalMatches)
101
+ : Number.POSITIVE_INFINITY;
102
+ if (repoLimit === 0) {
103
+ truncated = true;
104
+ allRepoResults.push({ name: repo.name, cwd: repo.cwd, matches: [], truncated: true });
105
+ break;
106
+ }
107
+ const matches = await runGrepInRepo(pattern, paths, opts, repo.cwd, repo.name, repoLimit);
108
+ let repoTruncated = false;
109
+ if (Number.isFinite(repoLimit) && matches.length >= repoLimit) {
110
+ repoTruncated = true;
111
+ truncated = true;
112
+ }
113
+ totalMatches += matches.length;
114
+ for (const m of matches)
115
+ filesSeen.add(`${repo.name}/${m.file}`);
116
+ allRepoResults.push({ name: repo.name, cwd: repo.cwd, matches, truncated: repoTruncated });
117
+ }
118
+ return {
119
+ pattern,
120
+ mode: opts.literal ? "literal" : "regex",
121
+ repos: allRepoResults,
122
+ total_matches: totalMatches,
123
+ total_files: filesSeen.size,
124
+ truncated,
125
+ elapsed_ms: Date.now() - started,
126
+ };
127
+ }
128
+ function resolveRepos(opts, context) {
129
+ const repoRoot = context?.repoRoot;
130
+ const submodules = context?.submodules;
131
+ if (opts.allRepos) {
132
+ if (!repoRoot || !submodules) {
133
+ throw new Error("--all-repos requires harnery to be configured with repoRoot + submodules");
134
+ }
135
+ const out = [{ name: "parent", cwd: repoRoot }];
136
+ for (const name of submodules)
137
+ out.push({ name, cwd: `${repoRoot}/${name}` });
138
+ return out;
139
+ }
140
+ if (opts.repo) {
141
+ if (opts.repo === "." || opts.repo === "parent") {
142
+ if (!repoRoot) {
143
+ throw new Error('--repo "." requires harnery to be configured with repoRoot');
144
+ }
145
+ return [{ name: "parent", cwd: repoRoot }];
146
+ }
147
+ if (!submodules || !repoRoot) {
148
+ throw new Error(`--repo "${opts.repo}" requires harnery to be configured with repoRoot + submodules`);
149
+ }
150
+ const found = submodules.find((n) => n === opts.repo);
151
+ if (!found) {
152
+ throw new Error(`unknown repo "${opts.repo}". Valid: parent (.), ${submodules.join(", ")}`);
153
+ }
154
+ return [{ name: found, cwd: `${repoRoot}/${found}` }];
155
+ }
156
+ return [{ name: "cwd", cwd: process.cwd() }];
157
+ }
158
+ async function runGrepInRepo(pattern, paths, opts, cwd, repoName, limit) {
159
+ const args = ["-rn", "--color=never", "-I"]; // -I: skip binary files
160
+ if (opts.ignoreCase)
161
+ args.push("-i");
162
+ if (opts.wholeWord)
163
+ args.push("-w");
164
+ if (opts.literal)
165
+ args.push("-F");
166
+ else
167
+ args.push("-E");
168
+ if (opts.filesOnly)
169
+ args.push("-l");
170
+ if (opts.count)
171
+ args.push("-c");
172
+ const contextN = Number.parseInt(opts.context ?? "0", 10);
173
+ if (Number.isFinite(contextN) && contextN > 0)
174
+ args.push(`-C${contextN}`);
175
+ if (opts.maxCount) {
176
+ const n = Number.parseInt(opts.maxCount, 10);
177
+ if (Number.isFinite(n) && n > 0)
178
+ args.push(`-m${n}`);
179
+ }
180
+ if (!opts.noDefaultExcludes) {
181
+ for (const d of DEFAULT_EXCLUDE_DIRS)
182
+ args.push(`--exclude-dir=${d}`);
183
+ for (const f of DEFAULT_EXCLUDE_FILES)
184
+ args.push(`--exclude=${f}`);
185
+ }
186
+ for (const e of opts.exclude ?? [])
187
+ args.push(`--exclude=${e}`);
188
+ const langGlobs = opts.lang ? LANG_GLOBS[opts.lang] : undefined;
189
+ if (opts.lang && !langGlobs) {
190
+ throw new Error(`unknown --lang "${opts.lang}". Valid: ${Object.keys(LANG_GLOBS).join(", ")}`);
191
+ }
192
+ if (langGlobs)
193
+ for (const g of langGlobs)
194
+ args.push(`--include=${g}`);
195
+ for (const g of opts.include ?? [])
196
+ args.push(`--include=${g}`);
197
+ args.push("--", pattern);
198
+ if (paths.length > 0)
199
+ args.push(...paths);
200
+ else
201
+ args.push(".");
202
+ const matches = await execGrep(args, cwd, limit);
203
+ return matches.map((m) => ({ ...m, repo: repoName }));
204
+ }
205
+ function execGrep(args, cwd, limit) {
206
+ return new Promise((resolveP, reject) => {
207
+ const proc = spawn("grep", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
208
+ const matches = [];
209
+ let buffer = "";
210
+ let stderr = "";
211
+ let truncated = false;
212
+ proc.stdout.setEncoding("utf8");
213
+ proc.stdout.on("data", (chunk) => {
214
+ if (truncated)
215
+ return;
216
+ buffer += chunk;
217
+ let nl = buffer.indexOf("\n");
218
+ while (nl >= 0) {
219
+ const line = buffer.slice(0, nl);
220
+ buffer = buffer.slice(nl + 1);
221
+ if (line.length !== 0) {
222
+ const parsed = parseGrepLine(line);
223
+ if (parsed)
224
+ matches.push(parsed);
225
+ if (Number.isFinite(limit) && matches.length >= limit) {
226
+ truncated = true;
227
+ proc.kill("SIGTERM");
228
+ return;
229
+ }
230
+ }
231
+ nl = buffer.indexOf("\n");
232
+ }
233
+ });
234
+ proc.stderr.setEncoding("utf8");
235
+ proc.stderr.on("data", (chunk) => {
236
+ stderr += chunk;
237
+ });
238
+ proc.on("error", reject);
239
+ proc.on("close", (code) => {
240
+ if (buffer.length > 0 && !truncated) {
241
+ const parsed = parseGrepLine(buffer);
242
+ if (parsed)
243
+ matches.push(parsed);
244
+ }
245
+ // grep exits 1 on "no matches": normal, not an error.
246
+ if (code !== null && code !== 0 && code !== 1 && !truncated) {
247
+ reject(new Error(`grep exited ${code}: ${stderr.trim() || "(no stderr)"}`));
248
+ return;
249
+ }
250
+ resolveP(matches);
251
+ });
252
+ });
253
+ }
254
+ /** Parse `path:line:content` from grep -n output. Returns null for ambiguous (no line number) lines. */
255
+ function parseGrepLine(line) {
256
+ // First colon ends path; second colon ends line number (if numeric).
257
+ const firstColon = line.indexOf(":");
258
+ if (firstColon < 0) {
259
+ // -l mode: just a path. Encode as line=0, text="".
260
+ return { file: line, line: 0, text: "" };
261
+ }
262
+ const after = line.slice(firstColon + 1);
263
+ const secondColon = after.indexOf(":");
264
+ if (secondColon < 0) {
265
+ // -c mode: `path:count`.
266
+ const count = Number.parseInt(after, 10);
267
+ if (Number.isFinite(count))
268
+ return { file: line.slice(0, firstColon), line: count, text: "" };
269
+ return { file: line.slice(0, firstColon), line: 0, text: after };
270
+ }
271
+ const lineNum = Number.parseInt(after.slice(0, secondColon), 10);
272
+ if (!Number.isFinite(lineNum)) {
273
+ // Probably a context separator like `--`. Skip.
274
+ return null;
275
+ }
276
+ return {
277
+ file: line.slice(0, firstColon),
278
+ line: lineNum,
279
+ text: after.slice(secondColon + 1),
280
+ };
281
+ }
282
+ function renderResult(r, opts) {
283
+ const lines = [];
284
+ const showRepoHeader = r.repos.length > 1;
285
+ for (const repo of r.repos) {
286
+ if (repo.matches.length === 0)
287
+ continue;
288
+ if (showRepoHeader) {
289
+ lines.push("");
290
+ lines.push(`── ${repo.name} (${repo.matches.length} match${repo.matches.length === 1 ? "" : "es"}) ──`);
291
+ }
292
+ for (const m of repo.matches) {
293
+ if (opts.filesOnly) {
294
+ lines.push(m.file);
295
+ }
296
+ else if (opts.count) {
297
+ lines.push(`${m.file}:${m.line}`);
298
+ }
299
+ else {
300
+ lines.push(`${m.file}:${m.line}:${m.text}`);
301
+ }
302
+ }
303
+ if (repo.truncated) {
304
+ lines.push(" (truncated; pass --limit to widen)");
305
+ }
306
+ }
307
+ if (r.total_matches === 0) {
308
+ lines.push(`(no matches for /${r.pattern}/${r.mode === "literal" ? " (literal)" : ""})`);
309
+ }
310
+ else if (r.repos.length > 1 || r.truncated) {
311
+ lines.push("");
312
+ const summary = `${r.total_matches} match${r.total_matches === 1 ? "" : "es"} across ${r.total_files} file${r.total_files === 1 ? "" : "s"}`;
313
+ const tail = r.truncated ? " (truncated; use --limit)" : "";
314
+ lines.push(`${summary}${tail} (${r.elapsed_ms}ms)`);
315
+ }
316
+ return lines.join("\n");
317
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * `harn init`: idempotently wire harnery into a project.
3
+ *
4
+ * Two things a fresh consumer otherwise has to know to do by hand (both silent
5
+ * if skipped):
6
+ * 1. Create the `.harnery/` coord root; without it `findCoordRoot` returns
7
+ * null and every hook no-ops forever.
8
+ * 2. Register the agent-hook entries in the harness settings file.
9
+ *
10
+ * Wires whichever harness `--harness` names (Claude Code `.claude/settings.json`,
11
+ * Cursor `.cursor/hooks.json`, or Codex `.codex/hooks.json`): the per-harness
12
+ * file path, event list, and hook-entry shape all come from HARNESS_SPECS.
13
+ *
14
+ * `harn init` does both, non-destructively: it merges hook entries into an
15
+ * existing settings file (preserving any other hooks) and skips entries that are
16
+ * already wired, so it's safe to re-run. `--dry-run` previews without writing.
17
+ */
18
+ import type { Command } from "commander";
19
+ import type { EmitContext } from "../commander.js";
20
+ import { type HarnessId, type HarnessSpec } from "../core/hooks/harness/events.js";
21
+ /** Claude Code + Codex entry: `{ hooks: [{ type, command }] }`. */
22
+ interface ClaudeHookGroup {
23
+ matcher?: string;
24
+ hooks: {
25
+ type: string;
26
+ command: string;
27
+ }[];
28
+ }
29
+ /** Cursor entry: a flat `{ command }`. */
30
+ interface CursorHookGroup {
31
+ command: string;
32
+ type?: string;
33
+ matcher?: string;
34
+ }
35
+ type HookGroup = ClaudeHookGroup | CursorHookGroup;
36
+ export interface SettingsFile {
37
+ version?: number;
38
+ hooks?: Record<string, HookGroup[]>;
39
+ [k: string]: unknown;
40
+ }
41
+ export declare function registerInitCommand(program: Command, emit: EmitContext, binName?: string): void;
42
+ /**
43
+ * Merge agent-hook entries into a harness settings object in place, idempotently.
44
+ * Preserves every existing hook; skips events already wired to
45
+ * `agent-hook <subcommand>`. Honors the harness's entry shape (Claude/Codex nest
46
+ * under an inner `hooks` array; Cursor uses a flat `{ command }`) and ensures the
47
+ * root `version` key when the harness requires one. Pure (no fs/git) so it's
48
+ * unit-testable.
49
+ *
50
+ * The trailing space in the match (`agent-hook ${subcommand} `) is load-bearing:
51
+ * it keeps `stop` from matching `stop-failure`.
52
+ */
53
+ export declare function wireHooks(settings: SettingsFile, spec: HarnessSpec, agentHookPath: string, harness: HarnessId): {
54
+ wired: number;
55
+ already: number;
56
+ };
57
+ /**
58
+ * Inverse of {@link wireHooks}: strip every harnery-owned hook entry from a
59
+ * settings object in place, idempotently. A hook is "harnery's" when its command
60
+ * contains `agent-hook ` (the trailing space matches `agent-hook <subcommand>`),
61
+ * so any non-harnery hook the consumer added is preserved. Emptied
62
+ * `hooks[settingsKey]` arrays are dropped; an emptied `hooks` object is dropped
63
+ * entirely. Harness-agnostic (scans every key) so it removes entries left by any
64
+ * harness. Pure (no fs) so it's unit-testable. Returns the count removed and the
65
+ * count of non-harnery hooks left behind.
66
+ */
67
+ export declare function unwireHooks(settings: SettingsFile): {
68
+ removed: number;
69
+ remaining: number;
70
+ };
71
+ /**
72
+ * Idempotently record `binName` in `.harnery/config.jsonc`, preserving any
73
+ * existing JSONC comments and the `files` section. Returns an action line, or
74
+ * null when nothing changed. Three cases:
75
+ * - file absent → write a minimal commented stub;
76
+ * - `binName` already present + matching → no-op;
77
+ * - present-but-different value → comment-safe in-place value swap;
78
+ * - key absent → splice it in as the first key (comment-safe).
79
+ */
80
+ export declare function stampBinName(configPath: string, binName: string, dryRun: boolean): string | null;
81
+ export {};
82
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,WAAW,EAEjB,MAAM,iCAAiC,CAAC;AAWzC,mEAAmE;AACnE,UAAU,eAAe;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5C;AACD,0CAA0C;AAC1C,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,KAAK,SAAS,GAAG,eAAe,GAAG,eAAe,CAAC;AAEnD,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAsF/F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,WAAW,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,SAAS,GACjB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAsBpC;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAmB1F;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAmDhG"}