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,363 @@
1
+ import { spawn } from "node:child_process";
2
+ import type { Command } from "commander";
3
+ import type { EmitContext, HarneryProgramContext } from "../commander.ts";
4
+
5
+ /**
6
+ * `harn callers <symbol>`: find references to a symbol across the monorepo with
7
+ * kind classification (call / import / type / decl / ref). Thin wrapper over
8
+ * grep -rnw with post-filtering: declarations + single-line comments are
9
+ * filtered out by default, and lines starting with " * " (multi-line comment
10
+ * continuation) are dropped. Inherent text-based-search limitations: multi-line
11
+ * string literals and block-comment interiors aren't excluded.
12
+ */
13
+
14
+ interface CallersOpts {
15
+ repo?: string;
16
+ allRepos?: boolean;
17
+ lang?: string;
18
+ includeDecl?: boolean;
19
+ includeComments?: boolean;
20
+ limit?: string;
21
+ json?: boolean;
22
+ }
23
+
24
+ interface Caller {
25
+ repo: string;
26
+ file: string;
27
+ line: number;
28
+ kind: "call" | "import" | "decl" | "type" | "ref";
29
+ text: string;
30
+ }
31
+
32
+ interface CallersResult {
33
+ symbol: string;
34
+ repos: { name: string; cwd: string }[];
35
+ callers: Caller[];
36
+ by_kind: Record<string, number>;
37
+ total: number;
38
+ truncated: boolean;
39
+ elapsed_ms: number;
40
+ }
41
+
42
+ const DEFAULT_EXCLUDE_DIRS = [
43
+ ".git",
44
+ ".cache",
45
+ "node_modules",
46
+ "dist",
47
+ "build",
48
+ "out",
49
+ ".next",
50
+ "vendor",
51
+ "__pycache__",
52
+ ".venv",
53
+ "tmp",
54
+ "tmp-files",
55
+ "coverage",
56
+ ".parcel-cache",
57
+ ".turbo",
58
+ ".harnery",
59
+ ];
60
+
61
+ // (no file-name excludes by default).
62
+ const DEFAULT_EXCLUDE_FILES: string[] = [];
63
+
64
+ const LANG_GLOBS: Record<string, string[]> = {
65
+ ts: ["*.ts", "*.tsx"],
66
+ js: ["*.js", "*.jsx", "*.mjs", "*.cjs"],
67
+ py: ["*.py"],
68
+ php: ["*.php"],
69
+ rb: ["*.rb"],
70
+ go: ["*.go"],
71
+ rs: ["*.rs"],
72
+ };
73
+
74
+ export function registerCallersCommand(
75
+ program: Command,
76
+ emit: EmitContext,
77
+ context?: HarneryProgramContext,
78
+ ): void {
79
+ program
80
+ .command("callers <symbol>")
81
+ .description(
82
+ "Find references to a symbol across the monorepo with kind classification " +
83
+ "(call / import / type / decl / ref). Heuristic: filters out declarations + " +
84
+ "single-line comments by default; opt back in with --include-decl / --include-comments.",
85
+ )
86
+ .option("--repo <name>", "Scope to one submodule (`.` = parent repo root)")
87
+ .option("--all-repos", "Search parent + every submodule")
88
+ .option("--lang <lang>", `File type preset (${Object.keys(LANG_GLOBS).join(", ")})`)
89
+ .option("--include-decl", "Include the declaration line(s) too")
90
+ .option("--include-comments", "Include matches in single-line comments")
91
+ .option("--limit <n>", "Truncate output to N matches total", "500")
92
+ .option("--json", "Structured JSON envelope")
93
+ .action(async (symbol: string, opts: CallersOpts) => {
94
+ try {
95
+ const result = await runCallers(symbol, opts, context);
96
+ if (opts.json) {
97
+ emit.config({ format: "json" });
98
+ emit.data(result);
99
+ return;
100
+ }
101
+ emit.text(`${renderCallers(result)}\n`);
102
+ } catch (err) {
103
+ emit.error({ code: "callers_failed", message: (err as Error).message });
104
+ process.exit(1);
105
+ }
106
+ });
107
+ }
108
+
109
+ async function runCallers(
110
+ symbol: string,
111
+ opts: CallersOpts,
112
+ context: HarneryProgramContext | undefined,
113
+ ): Promise<CallersResult> {
114
+ if (!/^[A-Za-z_$][\w$]*$/.test(symbol)) {
115
+ throw new Error(`symbol must be a valid identifier (got: ${symbol})`);
116
+ }
117
+
118
+ const started = Date.now();
119
+ const repos = resolveRepos(opts, context);
120
+ const limit = opts.limit ? Number.parseInt(opts.limit, 10) : 500;
121
+
122
+ const allCallers: Caller[] = [];
123
+ let truncated = false;
124
+
125
+ for (const repo of repos) {
126
+ if (allCallers.length >= limit) {
127
+ truncated = true;
128
+ break;
129
+ }
130
+ const remaining = limit - allCallers.length;
131
+ const found = await grepInRepo(symbol, opts, repo, remaining);
132
+ if (found.truncated) truncated = true;
133
+ allCallers.push(...found.callers);
134
+ }
135
+
136
+ const by_kind: Record<string, number> = {};
137
+ for (const c of allCallers) {
138
+ by_kind[c.kind] = (by_kind[c.kind] || 0) + 1;
139
+ }
140
+
141
+ return {
142
+ symbol,
143
+ repos: repos.map((r) => ({ name: r.name, cwd: r.cwd })),
144
+ callers: allCallers,
145
+ by_kind,
146
+ total: allCallers.length,
147
+ truncated,
148
+ elapsed_ms: Date.now() - started,
149
+ };
150
+ }
151
+
152
+ function resolveRepos(
153
+ opts: CallersOpts,
154
+ context: HarneryProgramContext | undefined,
155
+ ): { name: string; cwd: string }[] {
156
+ const repoRoot = context?.repoRoot;
157
+ const submodules = context?.submodules;
158
+ if (opts.allRepos) {
159
+ if (!repoRoot || !submodules) {
160
+ throw new Error("--all-repos requires harnery to be configured with repoRoot + submodules");
161
+ }
162
+ const out: { name: string; cwd: string }[] = [{ name: "parent", cwd: repoRoot }];
163
+ for (const name of submodules) out.push({ name, cwd: `${repoRoot}/${name}` });
164
+ return out;
165
+ }
166
+ if (opts.repo) {
167
+ if (opts.repo === "." || opts.repo === "parent") {
168
+ if (!repoRoot) {
169
+ throw new Error('--repo "." requires harnery to be configured with repoRoot');
170
+ }
171
+ return [{ name: "parent", cwd: repoRoot }];
172
+ }
173
+ if (!submodules || !repoRoot) {
174
+ throw new Error(
175
+ `--repo "${opts.repo}" requires harnery to be configured with repoRoot + submodules`,
176
+ );
177
+ }
178
+ const found = submodules.find((n) => n === opts.repo);
179
+ if (!found) {
180
+ throw new Error(`unknown repo "${opts.repo}". Valid: parent (.), ${submodules.join(", ")}`);
181
+ }
182
+ return [{ name: found, cwd: `${repoRoot}/${found}` }];
183
+ }
184
+ return [{ name: "cwd", cwd: process.cwd() }];
185
+ }
186
+
187
+ function grepInRepo(
188
+ symbol: string,
189
+ opts: CallersOpts,
190
+ repo: { name: string; cwd: string },
191
+ limit: number,
192
+ ): Promise<{ callers: Caller[]; truncated: boolean }> {
193
+ const args: string[] = ["-rnw", "--color=never", "-I", "-E"];
194
+ for (const d of DEFAULT_EXCLUDE_DIRS) args.push(`--exclude-dir=${d}`);
195
+ for (const f of DEFAULT_EXCLUDE_FILES) args.push(`--exclude=${f}`);
196
+ const langGlobs = opts.lang ? LANG_GLOBS[opts.lang] : undefined;
197
+ if (opts.lang && !langGlobs) {
198
+ throw new Error(`unknown --lang "${opts.lang}". Valid: ${Object.keys(LANG_GLOBS).join(", ")}`);
199
+ }
200
+ if (langGlobs) for (const g of langGlobs) args.push(`--include=${g}`);
201
+ args.push("--", symbol, ".");
202
+
203
+ return new Promise((resolveP, reject) => {
204
+ const proc = spawn("grep", args, { cwd: repo.cwd, stdio: ["ignore", "pipe", "pipe"] });
205
+ const callers: Caller[] = [];
206
+ let buffer = "";
207
+ let stderr = "";
208
+ let truncated = false;
209
+
210
+ proc.stdout.setEncoding("utf8");
211
+ proc.stdout.on("data", (chunk: string) => {
212
+ if (truncated) return;
213
+ buffer += chunk;
214
+ let nl = buffer.indexOf("\n");
215
+ while (nl >= 0) {
216
+ const line = buffer.slice(0, nl);
217
+ buffer = buffer.slice(nl + 1);
218
+ if (line.length !== 0) {
219
+ const parsed = parseCallerLine(line, symbol, opts, repo.name);
220
+ if (parsed) callers.push(parsed);
221
+ if (callers.length >= limit) {
222
+ truncated = true;
223
+ proc.kill("SIGTERM");
224
+ return;
225
+ }
226
+ }
227
+ nl = buffer.indexOf("\n");
228
+ }
229
+ });
230
+ proc.stderr.setEncoding("utf8");
231
+ proc.stderr.on("data", (chunk: string) => {
232
+ stderr += chunk;
233
+ });
234
+ proc.on("error", reject);
235
+ proc.on("close", (code) => {
236
+ if (buffer.length > 0 && !truncated) {
237
+ const parsed = parseCallerLine(buffer, symbol, opts, repo.name);
238
+ if (parsed) callers.push(parsed);
239
+ }
240
+ if (code !== null && code !== 0 && code !== 1 && !truncated) {
241
+ reject(new Error(`grep exited ${code}: ${stderr.trim() || "(no stderr)"}`));
242
+ return;
243
+ }
244
+ resolveP({ callers, truncated });
245
+ });
246
+ });
247
+ }
248
+
249
+ function parseCallerLine(
250
+ line: string,
251
+ symbol: string,
252
+ opts: CallersOpts,
253
+ repoName: string,
254
+ ): Caller | null {
255
+ const firstColon = line.indexOf(":");
256
+ if (firstColon < 0) return null;
257
+ const after = line.slice(firstColon + 1);
258
+ const secondColon = after.indexOf(":");
259
+ if (secondColon < 0) return null;
260
+ const lineNum = Number.parseInt(after.slice(0, secondColon), 10);
261
+ if (!Number.isFinite(lineNum)) return null;
262
+
263
+ const file = line.slice(0, firstColon).replace(/^\.\//, "");
264
+ const text = after.slice(secondColon + 1);
265
+
266
+ if (!opts.includeComments) {
267
+ if (/^\s*(\/\/|#|--)/.test(text)) return null;
268
+ }
269
+ if (/^\s*\*[\s/]/.test(text)) return null; // /* multi-line comment continuation
270
+
271
+ const kind = classifyMatch(text, symbol);
272
+ if (kind === "decl" && !opts.includeDecl) return null;
273
+
274
+ return { repo: repoName, file, line: lineNum, kind, text };
275
+ }
276
+
277
+ function classifyMatch(text: string, symbol: string): Caller["kind"] {
278
+ const sym = escapeRegex(symbol);
279
+
280
+ if (
281
+ new RegExp(
282
+ `^\\s*(export\\s+)?(async\\s+)?(function|class|interface|type|enum|namespace)\\s+${sym}\\b`,
283
+ ).test(text)
284
+ ) {
285
+ return "decl";
286
+ }
287
+ if (new RegExp(`^\\s*(export\\s+)?(const|let|var)\\s+${sym}\\b`).test(text)) return "decl";
288
+ if (
289
+ new RegExp(`^\\s*(public|private|protected)\\s+(static\\s+)?function\\s+${sym}\\b`).test(text)
290
+ ) {
291
+ return "decl";
292
+ }
293
+ if (new RegExp(`^\\s*(async\\s+)?def\\s+${sym}\\b`).test(text)) return "decl";
294
+ if (new RegExp(`^\\s*class\\s+${sym}\\b`).test(text)) return "decl";
295
+
296
+ if (/^\s*(import\b|from\b|use\s+|require\s*\(|include\s|require_once\s)/.test(text)) {
297
+ return "import";
298
+ }
299
+
300
+ if (new RegExp(`\\b${sym}\\s*\\(`).test(text)) return "call";
301
+
302
+ if (
303
+ new RegExp(
304
+ `(:\\s*${sym}\\b|extends\\s+${sym}\\b|implements\\s+${sym}\\b|as\\s+${sym}\\b|<${sym}[>\\s,])`,
305
+ ).test(text)
306
+ ) {
307
+ return "type";
308
+ }
309
+
310
+ return "ref";
311
+ }
312
+
313
+ function escapeRegex(s: string): string {
314
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
315
+ }
316
+
317
+ function renderCallers(r: CallersResult): string {
318
+ const lines: string[] = [];
319
+
320
+ if (r.total === 0) {
321
+ lines.push(`callers · ${r.symbol}: no matches`);
322
+ return lines.join("\n");
323
+ }
324
+
325
+ const summary = Object.entries(r.by_kind)
326
+ .sort((a, b) => b[1] - a[1])
327
+ .map(([k, n]) => `${k}=${n}`)
328
+ .join(", ");
329
+ lines.push(`callers · ${r.symbol} (${r.total} total: ${summary}, ${r.elapsed_ms}ms)`);
330
+
331
+ const byRepo = new Map<string, Caller[]>();
332
+ for (const c of r.callers) {
333
+ if (!byRepo.has(c.repo)) byRepo.set(c.repo, []);
334
+ byRepo.get(c.repo)!.push(c);
335
+ }
336
+
337
+ const showRepoHeader = byRepo.size > 1;
338
+
339
+ for (const [repo, callers] of byRepo) {
340
+ if (showRepoHeader) {
341
+ lines.push("");
342
+ lines.push(`── ${repo} (${callers.length}) ──`);
343
+ }
344
+ let lastFile = "";
345
+ for (const c of callers) {
346
+ if (c.file !== lastFile) {
347
+ lines.push("");
348
+ lines.push(` ${c.file}`);
349
+ lastFile = c.file;
350
+ }
351
+ const kindLabel = `[${c.kind}]`.padEnd(8);
352
+ const snippet = c.text.trim().slice(0, 100);
353
+ lines.push(` L${c.line.toString().padEnd(5)} ${kindLabel} ${snippet}`);
354
+ }
355
+ }
356
+
357
+ if (r.truncated) {
358
+ lines.push("");
359
+ lines.push("(truncated; pass --limit to widen)");
360
+ }
361
+
362
+ return lines.join("\n");
363
+ }
@@ -0,0 +1,193 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import type { Command } from "commander";
4
+ import type { EmitContext, HarneryProgramContext } from "../commander.ts";
5
+ import {
6
+ type CompletionContextLookup,
7
+ generateBash,
8
+ generateFish,
9
+ generateZsh,
10
+ walkProgram,
11
+ } from "../lib/completion/index.ts";
12
+
13
+ const noopProviderRunner = async (): Promise<string[]> => [];
14
+ const noopLookup: CompletionContextLookup = () => undefined;
15
+
16
+ /**
17
+ * `harn completion`: emit / install shell completion scripts.
18
+ *
19
+ * harn completion bash | zsh | fish → write generated script to stdout
20
+ * harn completion install [--shell ...] → install to the conventional location
21
+ *
22
+ * And the hidden internal entry point:
23
+ *
24
+ * harn __complete <provider> -- <partial>
25
+ * Called by the generated shell scripts at tab-time for dynamic-value
26
+ * completion. Returns newline-separated candidate values. Empty exit
27
+ * code 0 even on errors (don't disrupt the user's tab).
28
+ */
29
+ export function registerCompletionCommand(
30
+ program: Command,
31
+ _emit: EmitContext,
32
+ context?: HarneryProgramContext,
33
+ ): void {
34
+ const lookup = context?.completionLookup ?? noopLookup;
35
+ const runProvider = context?.completionRunner ?? noopProviderRunner;
36
+ const root = program
37
+ .command("completion")
38
+ .description(
39
+ "Shell tab-completion. Emit a script per shell or install to the standard location.",
40
+ );
41
+
42
+ root
43
+ .command("bash")
44
+ .description("Emit bash completion script to stdout")
45
+ .action(() => {
46
+ const out = generateBash(walkProgram(program, lookup), program.name());
47
+ process.stdout.write(out); // raw bytes: shell completion scripts must be unframed (consumer evals stdout).
48
+ });
49
+
50
+ root
51
+ .command("zsh")
52
+ .description("Emit zsh completion script to stdout")
53
+ .action(() => {
54
+ const out = generateZsh(walkProgram(program, lookup), program.name());
55
+ process.stdout.write(out); // raw bytes: shell completion scripts must be unframed (consumer evals stdout).
56
+ });
57
+
58
+ root
59
+ .command("fish")
60
+ .description("Emit fish completion script to stdout")
61
+ .action(() => {
62
+ const out = generateFish(walkProgram(program, lookup), program.name());
63
+ process.stdout.write(out); // raw bytes: shell completion scripts must be unframed (consumer evals stdout).
64
+ });
65
+
66
+ root
67
+ .command("install")
68
+ .description("Write completion script to the conventional location for the chosen shell")
69
+ .option("--shell <name>", "bash | zsh | fish (default: auto-detect from $SHELL)")
70
+ .option("--path <file>", "Override destination path")
71
+ .option("--print-path", "Print the destination path and exit (no write)")
72
+ .action(async (opts: InstallOpts) => {
73
+ await installCompletion(program, opts, lookup);
74
+ });
75
+
76
+ // Hidden internal entry: shells call this at tab-time. `hidden: true` so
77
+ // `harn --help` doesn't surface it.
78
+ const hidden = program
79
+ .command("__complete <provider> [partial]", { hidden: true })
80
+ .description("Internal: dynamic-value completion callback for the shell")
81
+ .allowExcessArguments(true)
82
+ .action(async (provider: string, partial: string | undefined) => {
83
+ try {
84
+ const values = await runProvider(provider, partial ?? "");
85
+ for (const v of values) {
86
+ process.stdout.write(`${v}\n`); // lint-ok-emission: shell callback; newline-delimited raw values is the contract with compgen/_describe/fish.
87
+ }
88
+ } catch {
89
+ // Swallow errors silently: failure during tab completion should not
90
+ // break the user's shell.
91
+ }
92
+ });
93
+ // Keep TS happy that the variable is used.
94
+ void hidden;
95
+ }
96
+
97
+ interface InstallOpts {
98
+ shell?: string;
99
+ path?: string;
100
+ printPath?: boolean;
101
+ }
102
+
103
+ async function installCompletion(
104
+ program: Command,
105
+ opts: InstallOpts,
106
+ lookup: CompletionContextLookup,
107
+ ): Promise<void> {
108
+ const shell = opts.shell ?? detectShell();
109
+ if (!shell) {
110
+ process.stderr.write("Could not auto-detect shell. Pass --shell bash|zsh|fish explicitly.\n"); // lint-ok-emission: install error; stderr keeps stdout clean for --print-path piping.
111
+ process.exit(1);
112
+ }
113
+
114
+ const destination = opts.path ?? defaultInstallPath(shell, program.name());
115
+ if (opts.printPath) {
116
+ process.stdout.write(`${destination}\n`); // lint-ok-emission: --print-path is meant to be piped (e.g., dest=$(harn completion install --print-path)).
117
+ return;
118
+ }
119
+
120
+ let content: string;
121
+ switch (shell) {
122
+ case "bash":
123
+ content = generateBash(walkProgram(program, lookup), program.name());
124
+ break;
125
+ case "zsh":
126
+ content = generateZsh(walkProgram(program, lookup), program.name());
127
+ break;
128
+ case "fish":
129
+ content = generateFish(walkProgram(program, lookup), program.name());
130
+ break;
131
+ default:
132
+ process.stderr.write(`Unknown shell: ${shell}\n`); // lint-ok-emission: install-time error, see above.
133
+ process.exit(1);
134
+ }
135
+
136
+ mkdirSync(dirname(destination), { recursive: true });
137
+ writeFileSync(destination, content);
138
+ process.stderr.write(`Installed ${shell} completion to ${destination}\n`); // lint-ok-emission: install progress; stderr keeps stdout clean.
139
+
140
+ // Hint how to activate.
141
+ printActivationHint(shell, destination);
142
+ }
143
+
144
+ function detectShell(): string | null {
145
+ const shellPath = process.env.SHELL ?? "";
146
+ if (shellPath.endsWith("/bash")) return "bash";
147
+ if (shellPath.endsWith("/zsh")) return "zsh";
148
+ if (shellPath.endsWith("/fish")) return "fish";
149
+ return null;
150
+ }
151
+
152
+ function defaultInstallPath(shell: string, binName: string): string {
153
+ const home = process.env.HOME ?? "";
154
+ switch (shell) {
155
+ case "bash": {
156
+ // bash-completion v2 user-level location.
157
+ const xdg = process.env.XDG_DATA_HOME ?? resolve(home, ".local/share");
158
+ return resolve(xdg, `bash-completion/completions/${binName}`);
159
+ }
160
+ case "zsh": {
161
+ // Common $fpath user location. zsh auto-loads from ~/.zsh/completions if
162
+ // it's in $fpath.
163
+ return resolve(home, `.zsh/completions/_${binName}`);
164
+ }
165
+ case "fish": {
166
+ // Fish auto-loads everything in ~/.config/fish/completions/.
167
+ return resolve(home, `.config/fish/completions/${binName}.fish`);
168
+ }
169
+ default:
170
+ return resolve(home, `.${binName}-completion.${shell}`);
171
+ }
172
+ }
173
+
174
+ function printActivationHint(shell: string, path: string): void {
175
+ switch (shell) {
176
+ case "bash":
177
+ if (existsSync(resolve(process.env.HOME ?? "", ".bashrc"))) {
178
+ const bashHint = `\nTo activate now in this shell: source "${path}"\nNew shells will pick it up automatically if bash-completion is enabled.\nIf completions don't fire in new shells, add this to ~/.bashrc:\n [ -f "${path}" ] && source "${path}"\n`;
179
+ process.stderr.write(bashHint); // lint-ok-emission: post-install activation hint; stderr keeps stdout clean.
180
+ }
181
+ break;
182
+ case "zsh": {
183
+ const zshHint = `\nTo activate now: autoload -U compinit && compinit\nMake sure ${resolve(path, "..")} is in your $fpath. Add to ~/.zshrc:\n fpath=(${resolve(path, "..")} $fpath)\n autoload -U compinit && compinit\n`;
184
+ process.stderr.write(zshHint); // lint-ok-emission: post-install activation hint; stderr keeps stdout clean.
185
+ break;
186
+ }
187
+ case "fish": {
188
+ const fishHint = `\nFish auto-loads completions from ${resolve(path, "..")}. New shells pick it up.\nTo activate now: source "${path}"\n`;
189
+ process.stderr.write(fishHint); // lint-ok-emission: post-install activation hint; stderr keeps stdout clean.
190
+ break;
191
+ }
192
+ }
193
+ }