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,291 @@
1
+ /**
2
+ * Bash completion generator. Walks a CommandSpec tree and emits a single
3
+ * self-contained bash function `_<bin>` plus the `complete -F _<bin> <bin>`
4
+ * directive that registers it. The bin name is injected by the caller so the
5
+ * same generator serves any host CLI.
6
+ *
7
+ * Generated script architecture:
8
+ * 1. `_<bin>_path()`: walks COMP_WORDS forward, skipping options and option
9
+ * values, returns the current command-path string (e.g., "anthropic cost").
10
+ * 2. `_<bin>_options()` / `_<bin>_subcommands()` / `_<bin>_option_takes_value()` /
11
+ * `_<bin>_option_choices()`: table-lookup functions, generated as big
12
+ * case statements from the spec tree.
13
+ * 3. `_<bin>()`: main entry. Sets COMPREPLY based on whether the cursor is
14
+ * on a subcommand name, option name (--<TAB>), or value.
15
+ * 4. Dynamic value paths shell out to `<bin> __complete <provider> -- "$cur"`.
16
+ *
17
+ * Important escaping rules:
18
+ * - Single-quoted strings in bash don't honor escapes. We replace `'` in
19
+ * descriptions/choices with `'\''` (the standard bash single-quote escape).
20
+ * - We avoid descriptions in bash output because bash COMPREPLY can't render
21
+ * them; descriptions are a zsh/fish feature.
22
+ */
23
+
24
+ import type { CommandSpec, OptionSpec, PositionalSpec } from "./walk.js";
25
+
26
+ /**
27
+ * Derive a valid shell function-name prefix from the bin name (e.g. `my-cli`
28
+ * → `_my_cli`). Non-identifier characters collapse to `_`.
29
+ */
30
+ function fnPrefix(binName: string): string {
31
+ return `_${binName.replace(/[^a-zA-Z0-9_]/g, "_")}`;
32
+ }
33
+
34
+ /**
35
+ * Encode a positional/option value source as a single string the bash driver
36
+ * can parse: `DYN:<provider>` for dynamic, `ENUM:val1 val2 val3` for static
37
+ * choices, or empty when there's nothing to suggest.
38
+ */
39
+ function valueSpec(opt: { dynamicProvider?: string; valueChoices?: string[] }): string {
40
+ if (opt.dynamicProvider) return `DYN:${opt.dynamicProvider}`;
41
+ if (opt.valueChoices && opt.valueChoices.length > 0) {
42
+ return `ENUM:${opt.valueChoices.join(" ")}`;
43
+ }
44
+ return "";
45
+ }
46
+
47
+ export function generateBash(root: CommandSpec, binName: string): string {
48
+ const fn = fnPrefix(binName);
49
+ // Build lookup tables keyed by command path. Path "" represents the root.
50
+ const subcommandsByPath = new Map<string, string[]>();
51
+ const optionsByPath = new Map<string, OptionSpec[]>();
52
+ const positionalsByPath = new Map<string, PositionalSpec[]>();
53
+
54
+ const walk = (s: CommandSpec): void => {
55
+ subcommandsByPath.set(s.path, s.subcommands.map((c) => c.name).sort());
56
+ optionsByPath.set(s.path, s.options);
57
+ positionalsByPath.set(s.path, s.positionals);
58
+ for (const sub of s.subcommands) walk(sub);
59
+ };
60
+ walk(root);
61
+
62
+ const out: string[] = [];
63
+ out.push(
64
+ `# ${binName} bash completion, generated by \`${binName} completion bash\`. Do not edit.`,
65
+ );
66
+ out.push(`# Source via: eval "$(${binName} completion bash)"`);
67
+ out.push("");
68
+ out.push(`${fn}_subcommands() {`);
69
+ out.push(` case "$1" in`);
70
+ for (const [path, names] of [...subcommandsByPath.entries()].sort()) {
71
+ if (names.length === 0) continue;
72
+ out.push(` ${bashCase(path)}) echo '${names.join(" ")}' ;;`);
73
+ }
74
+ out.push(" esac");
75
+ out.push("}");
76
+ out.push("");
77
+ out.push(`${fn}_options() {`);
78
+ out.push(` case "$1" in`);
79
+ for (const [path, opts] of [...optionsByPath.entries()].sort()) {
80
+ if (opts.length === 0) continue;
81
+ const flags = opts.flatMap((o) => [o.long, o.short]).filter((f) => f.length > 0);
82
+ out.push(` ${bashCase(path)}) echo '${flags.join(" ")}' ;;`);
83
+ }
84
+ out.push(" esac");
85
+ out.push("}");
86
+ out.push("");
87
+ out.push(`${fn}_option_takes_value() {`);
88
+ out.push(
89
+ ` # echo "1" if option takes a value, empty otherwise. $1 = command path, $2 = option flag.`,
90
+ );
91
+ out.push(` case "$1|$2" in`);
92
+ for (const [path, opts] of [...optionsByPath.entries()].sort()) {
93
+ for (const o of opts) {
94
+ if (!o.takesValue) continue;
95
+ for (const flag of [o.long, o.short].filter((f) => f.length > 0)) {
96
+ out.push(` ${bashCase(path)}\\|${bashEscape(flag)}) echo 1 ;;`);
97
+ }
98
+ }
99
+ }
100
+ out.push(" esac");
101
+ out.push("}");
102
+ out.push("");
103
+ out.push(`${fn}_option_completion() {`);
104
+ out.push(` # Echo "DYN:<provider>" or "ENUM:val1 val2 ...". Empty if no spec.`);
105
+ out.push(` case "$1|$2" in`);
106
+ for (const [path, opts] of [...optionsByPath.entries()].sort()) {
107
+ for (const o of opts) {
108
+ if (!o.takesValue) continue;
109
+ const spec = valueSpec(o);
110
+ if (!spec) continue;
111
+ for (const flag of [o.long, o.short].filter((f) => f.length > 0)) {
112
+ out.push(` ${bashCase(path)}\\|${bashEscape(flag)}) echo '${bashEscape(spec)}' ;;`);
113
+ }
114
+ }
115
+ }
116
+ out.push(" esac");
117
+ out.push("}");
118
+ out.push("");
119
+ out.push(`${fn}_positional_completion() {`);
120
+ out.push(
121
+ ` # Echo "DYN:<provider>" or "ENUM:val1 val2 ...". $1 = command path, $2 = positional index.`,
122
+ );
123
+ out.push(` case "$1|$2" in`);
124
+ for (const [path, positionals] of [...positionalsByPath.entries()].sort()) {
125
+ positionals.forEach((p, i) => {
126
+ const spec = valueSpec(p);
127
+ if (!spec) return;
128
+ out.push(` ${bashCase(path)}\\|${i}) echo '${bashEscape(spec)}' ;;`);
129
+ });
130
+ }
131
+ out.push(" esac");
132
+ out.push("}");
133
+ out.push("");
134
+ out.push(bashDriver(fn, binName));
135
+ out.push("");
136
+ out.push(`complete -F ${fn} ${binName}`);
137
+ return `${out.join("\n")}\n`;
138
+ }
139
+
140
+ /**
141
+ * Converts a command path to a bash `case` pattern. Empty path → "ROOT".
142
+ * Replace spaces with `__` since bash case patterns don't tolerate spaces well
143
+ * inside an unquoted pattern. The driver substitutes the same way.
144
+ */
145
+ function bashCase(path: string): string {
146
+ if (path === "") return "ROOT";
147
+ return path.replace(/ /g, "__");
148
+ }
149
+
150
+ /**
151
+ * Escape a string for safe embedding in a single-quoted bash string. Inside
152
+ * single quotes the only character that needs escaping is the single quote
153
+ * itself, which we close, escape, and reopen: `'\''`.
154
+ */
155
+ function bashEscape(s: string): string {
156
+ return s.replace(/'/g, `'\\''`);
157
+ }
158
+
159
+ /**
160
+ * The driver: the parts of the completion that don't depend on the command
161
+ * tree. Walks COMP_WORDS to determine the current command path, then dispatches
162
+ * to subcommand / option / value completion. `fn` is the function-name prefix
163
+ * and `binName` is the CLI name used for the `__complete` callback.
164
+ */
165
+ function bashDriver(fn: string, binName: string): string {
166
+ return `${fn}() {
167
+ local cur prev words cword
168
+ COMPREPLY=()
169
+ cur="\${COMP_WORDS[COMP_CWORD]}"
170
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
171
+
172
+ # Walk forward through words, skipping options and option-values, to
173
+ # determine which command path we're currently inside. Stop when we hit
174
+ # COMP_CWORD (the position the cursor is on).
175
+ local -a path_parts=()
176
+ local i=1
177
+ local path_str="ROOT"
178
+ while [ $i -lt $COMP_CWORD ]; do
179
+ local w="\${COMP_WORDS[$i]}"
180
+ case "$w" in
181
+ -*)
182
+ # Option. If it takes a value, skip the next word too.
183
+ if [ "$(${fn}_option_takes_value "$path_str" "$w")" = "1" ]; then
184
+ i=$((i+1))
185
+ fi
186
+ ;;
187
+ *)
188
+ path_parts+=("$w")
189
+ local joined
190
+ joined=$(printf '%s__' "\${path_parts[@]}")
191
+ joined="\${joined%__}"
192
+ # If this word is a known subcommand at our current path, descend.
193
+ local known
194
+ known=$(${fn}_subcommands "$path_str")
195
+ if [[ " $known " == *" $w "* ]]; then
196
+ path_str="$joined"
197
+ fi
198
+ ;;
199
+ esac
200
+ i=$((i+1))
201
+ done
202
+
203
+ # --- decide what we're completing ----------------------------------------
204
+
205
+ # Case 1: previous word is an option that takes a value → complete that value.
206
+ if [ -n "$prev" ] && [ "$(${fn}_option_takes_value "$path_str" "$prev")" = "1" ]; then
207
+ local spec
208
+ spec=$(${fn}_option_completion "$path_str" "$prev")
209
+ ${fn}_emit_values "$spec" "$cur"
210
+ return
211
+ fi
212
+
213
+ # Case 2: current word starts with - → complete option name.
214
+ if [[ "$cur" == -* ]]; then
215
+ local opts
216
+ opts=$(${fn}_options "$path_str")
217
+ if [ -n "$opts" ]; then
218
+ COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
219
+ fi
220
+ return
221
+ fi
222
+
223
+ # Case 3: complete subcommand name (and any positional values for this command).
224
+ local subs
225
+ subs=$(${fn}_subcommands "$path_str")
226
+
227
+ # How many positional args have we accumulated for the current path? Count
228
+ # path_parts entries beyond the part that matched a subcommand.
229
+ local pos_index=0
230
+ if [ -n "$subs" ]; then
231
+ # If subs are still available here, the cursor is on a subcommand slot.
232
+ COMPREPLY=( $(compgen -W "$subs" -- "$cur") )
233
+ return
234
+ fi
235
+
236
+ # No more subcommands, we're on a positional. Determine which positional
237
+ # index we're on by counting non-option words after the last matched subcommand.
238
+ local after_cmd=0
239
+ local j=1
240
+ local seen_path="ROOT"
241
+ while [ $j -lt $COMP_CWORD ]; do
242
+ local w="\${COMP_WORDS[$j]}"
243
+ case "$w" in
244
+ -*)
245
+ if [ "$(${fn}_option_takes_value "$seen_path" "$w")" = "1" ]; then
246
+ j=$((j+1))
247
+ fi
248
+ ;;
249
+ *)
250
+ local joined
251
+ if [ "$seen_path" = "ROOT" ]; then joined="$w"; else joined="\${seen_path}__\${w}"; fi
252
+ local known
253
+ known=$(${fn}_subcommands "$seen_path")
254
+ if [[ " $known " == *" $w "* ]]; then
255
+ seen_path="$joined"
256
+ else
257
+ after_cmd=$((after_cmd+1))
258
+ fi
259
+ ;;
260
+ esac
261
+ j=$((j+1))
262
+ done
263
+ pos_index=$after_cmd
264
+
265
+ local pos_spec
266
+ pos_spec=$(${fn}_positional_completion "$path_str" "$pos_index")
267
+ if [ -n "$pos_spec" ]; then
268
+ ${fn}_emit_values "$pos_spec" "$cur"
269
+ return
270
+ fi
271
+
272
+ # Fallback: file completion.
273
+ COMPREPLY=( $(compgen -f -- "$cur") )
274
+ }
275
+
276
+ ${fn}_emit_values() {
277
+ local spec="$1"
278
+ local cur="$2"
279
+ if [[ "$spec" == DYN:* ]]; then
280
+ local provider="\${spec#DYN:}"
281
+ local values
282
+ values=$(${binName} __complete "$provider" -- "$cur" 2>/dev/null)
283
+ if [ -n "$values" ]; then
284
+ COMPREPLY=( $(compgen -W "$values" -- "$cur") )
285
+ fi
286
+ elif [[ "$spec" == ENUM:* ]]; then
287
+ local choices="\${spec#ENUM:}"
288
+ COMPREPLY=( $(compgen -W "$choices" -- "$cur") )
289
+ fi
290
+ }`;
291
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Fish completion generator. Fish has the cleanest completion grammar of the
3
+ * three shells: one `complete` line per option per command. No driver function
4
+ * needed; fish's built-in `__fish_seen_subcommand_from` and friends do the
5
+ * subcommand parsing for us. The bin name is injected by the caller.
6
+ *
7
+ * Layout per command path:
8
+ * complete -c <bin> -n '<condition>' -a <subcommand> -d '<description>'
9
+ * complete -c <bin> -n '<condition>' -l <long> [-s <short>] -d '<description>' [-r] [-x] [-a '<choices>']
10
+ *
11
+ * Dynamic values: fish runs the command in `-a '(...)'` at completion time,
12
+ * giving us per-tab callbacks for free.
13
+ */
14
+
15
+ import type { CommandSpec } from "./walk.js";
16
+
17
+ function fishEscape(s: string): string {
18
+ // Inside single-quoted fish strings, escape \\ and '.
19
+ return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
20
+ }
21
+
22
+ function truncate(s: string, n: number): string {
23
+ if (!s) return "";
24
+ return s.length > n ? `${s.slice(0, n - 1)}…` : s;
25
+ }
26
+
27
+ /**
28
+ * Returns the fish condition expression for "we are inside the command path".
29
+ * Path "" (root) means "no subcommand seen yet".
30
+ */
31
+ function pathCondition(path: string): string {
32
+ if (path === "") {
33
+ return "__fish_use_subcommand";
34
+ }
35
+ const parts = path.split(" ");
36
+ // Build: __fish_seen_subcommand_from a; and __fish_seen_subcommand_from b; ...
37
+ // Fish's __fish_seen_subcommand_from accepts a list of allowed words at a
38
+ // given depth. For a 2-deep path "anthropic cost", the condition is:
39
+ // __fish_seen_subcommand_from anthropic; and __fish_seen_subcommand_from cost
40
+ const clauses = parts.map((p) => `__fish_seen_subcommand_from ${p}`);
41
+ return clauses.join("; and ");
42
+ }
43
+
44
+ function valueAction(
45
+ opt: { dynamicProvider?: string; valueChoices?: string[] },
46
+ binName: string,
47
+ ): {
48
+ flag: string;
49
+ arg: string;
50
+ } {
51
+ // Fish flag semantics:
52
+ // -r/--require-parameter : the flag requires an argument
53
+ // -x/--exclusive : flag requires an argument AND no further completion after
54
+ // -a '<arg>' : completions for the argument
55
+ if (opt.dynamicProvider) {
56
+ return {
57
+ flag: "-r",
58
+ arg: `-a '(${binName} __complete ${opt.dynamicProvider} -- (commandline -ct))'`,
59
+ };
60
+ }
61
+ if (opt.valueChoices && opt.valueChoices.length > 0) {
62
+ return {
63
+ flag: "-r",
64
+ arg: `-a '${opt.valueChoices.map(fishEscape).join(" ")}'`,
65
+ };
66
+ }
67
+ return { flag: "-r", arg: "" };
68
+ }
69
+
70
+ export function generateFish(root: CommandSpec, binName: string): string {
71
+ const out: string[] = [];
72
+ out.push(
73
+ `# ${binName} fish completion, generated by \`${binName} completion fish\`. Do not edit.`,
74
+ );
75
+ out.push(`# Source via: ${binName} completion fish | source`);
76
+ out.push(
77
+ `# Or install: ${binName} completion fish > ~/.config/fish/completions/${binName}.fish`,
78
+ );
79
+ out.push("");
80
+
81
+ const walk = (s: CommandSpec): void => {
82
+ const cond = pathCondition(s.path);
83
+ // Subcommands at this depth
84
+ for (const sub of s.subcommands) {
85
+ const desc = truncate(sub.description, 80);
86
+ out.push(
87
+ `complete -c ${binName} -f -n '${cond}' -a '${fishEscape(sub.name)}' -d '${fishEscape(desc)}'`,
88
+ );
89
+ }
90
+ // Options for this command
91
+ for (const o of s.options) {
92
+ const long = o.long ? o.long.replace(/^--/, "") : "";
93
+ const short = o.short ? o.short.replace(/^-/, "") : "";
94
+ const desc = truncate(o.description, 80);
95
+ let line = `complete -c ${binName} -n '${cond}'`;
96
+ if (long) line += ` -l ${long}`;
97
+ if (short) line += ` -s ${short}`;
98
+ if (o.takesValue) {
99
+ const v = valueAction(o, binName);
100
+ line += ` ${v.flag}`;
101
+ if (v.arg) line += ` ${v.arg}`;
102
+ else line += " -f";
103
+ } else {
104
+ line += " -f";
105
+ }
106
+ line += ` -d '${fishEscape(desc)}'`;
107
+ out.push(line);
108
+ }
109
+ // Positional values: fish doesn't natively handle Nth positional, but we
110
+ // can suggest values when no subcommand follows. The condition becomes:
111
+ // "in this path AND not seen another subcommand from this path's subs".
112
+ if (s.positionals.length > 0) {
113
+ const subNames = s.subcommands.map((c) => c.name).join(" ");
114
+ const positionalCond =
115
+ s.subcommands.length > 0
116
+ ? `${cond}; and not __fish_seen_subcommand_from ${subNames}`
117
+ : cond;
118
+ // We only render the first positional; fish has limited support for
119
+ // distinguishing positional indices in this completion grammar.
120
+ const p = s.positionals[0];
121
+ if (p) {
122
+ const v = valueAction(p, binName);
123
+ if (v.arg) {
124
+ out.push(`complete -c ${binName} -f -n '${positionalCond}' ${v.arg}`);
125
+ }
126
+ }
127
+ }
128
+
129
+ for (const sub of s.subcommands) walk(sub);
130
+ };
131
+ walk(root);
132
+
133
+ return `${out.join("\n")}\n`;
134
+ }
@@ -0,0 +1,10 @@
1
+ export { generateBash } from "./bash.js";
2
+ export { generateFish } from "./fish.js";
3
+ export {
4
+ type CommandSpec,
5
+ type CompletionContextLookup,
6
+ type OptionSpec,
7
+ type PositionalSpec,
8
+ walkProgram,
9
+ } from "./walk.js";
10
+ export { generateZsh } from "./zsh.js";
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Walks the Commander program tree and produces a normalized CommandSpec tree
3
+ * that the bash/zsh/fish completion generators consume. Decoupled from
4
+ * Commander internals so the generators don't have to know about Option/Argument
5
+ * classes, just the spec types defined here.
6
+ */
7
+
8
+ import type { Argument, Command, Option } from "commander";
9
+
10
+ /**
11
+ * Caller-injected lookup that maps a (commandPath, option-or-positional)
12
+ * pair to a dynamic-provider key. The shell completion script later
13
+ * invokes `<bin> __complete <key>` at tab-time and the consumer's
14
+ * provider registry produces the actual values. harn standalone uses
15
+ * the default no-op lookup (no dynamic completion).
16
+ */
17
+ export type CompletionContextLookup = (key: {
18
+ commandPath: string;
19
+ option?: string;
20
+ positional?: number;
21
+ }) => string | undefined;
22
+
23
+ const noopLookup: CompletionContextLookup = () => undefined;
24
+
25
+ export interface CommandSpec {
26
+ /** Bare command name (e.g., "anthropic", "cost"). */
27
+ name: string;
28
+ /** Full path from root, space-separated (e.g., "anthropic cost"). Empty for the root program. */
29
+ path: string;
30
+ description: string;
31
+ subcommands: CommandSpec[];
32
+ options: OptionSpec[];
33
+ positionals: PositionalSpec[];
34
+ /** True if this command has the `--help` flag (default for all Commander commands). */
35
+ hasHelp: boolean;
36
+ /** True if this is a hidden command; completion can skip it. */
37
+ hidden: boolean;
38
+ }
39
+
40
+ export interface OptionSpec {
41
+ /** Long form including `--`, e.g., "--workspace". May be empty if only short form exists. */
42
+ long: string;
43
+ /** Short form including `-`, e.g., "-w". May be empty. */
44
+ short: string;
45
+ description: string;
46
+ /** True if the option requires a value (e.g., `--workspace <id>`). */
47
+ takesValue: boolean;
48
+ /** Optional static choice list for the value (e.g. `--format json|csv|table`). */
49
+ valueChoices?: string[];
50
+ /** Optional dynamic-value provider key; completion script calls back via `harn __complete <key>`. */
51
+ dynamicProvider?: string;
52
+ /** True if the option can be repeated. */
53
+ variadic: boolean;
54
+ }
55
+
56
+ export interface PositionalSpec {
57
+ name: string;
58
+ description: string;
59
+ required: boolean;
60
+ variadic: boolean;
61
+ valueChoices?: string[];
62
+ dynamicProvider?: string;
63
+ }
64
+
65
+ /**
66
+ * Walks the Commander program tree and returns the root CommandSpec.
67
+ * The root represents `harn` itself; its `subcommands` are the top-level
68
+ * subcommands.
69
+ */
70
+ export function walkProgram(
71
+ program: Command,
72
+ lookup: CompletionContextLookup = noopLookup,
73
+ ): CommandSpec {
74
+ return walkCommand(program, "", true, lookup);
75
+ }
76
+
77
+ function walkCommand(
78
+ cmd: Command,
79
+ parentPath: string,
80
+ isRoot: boolean,
81
+ lookup: CompletionContextLookup,
82
+ ): CommandSpec {
83
+ const name = cmd.name();
84
+ // The root program represents `harn` itself; its path stays empty so that
85
+ // top-level subcommands have paths like "anthropic", not "harn anthropic".
86
+ // The shell driver walks COMP_WORDS[1..], so it's already operating in
87
+ // post-`harn` space and shouldn't see "bp" in its lookup keys.
88
+ const path = isRoot ? "" : parentPath ? `${parentPath} ${name}` : name;
89
+
90
+ const subcommands: CommandSpec[] = cmd.commands
91
+ .filter((c) => !isHidden(c))
92
+ // Skip the auto-injected `help` subcommand; noisy without being useful.
93
+ .filter((c) => c.name() !== "help")
94
+ .map((c) => walkCommand(c, path, false, lookup));
95
+
96
+ // Sort subcommands alphabetically for predictable output.
97
+ subcommands.sort((a, b) => a.name.localeCompare(b.name));
98
+
99
+ const options: OptionSpec[] = cmd.options
100
+ .filter((o) => !o.hidden)
101
+ .map((o) => optionSpec(o, path, lookup));
102
+
103
+ // Commander auto-injects `-h, --help` for every command but doesn't expose
104
+ // it in `cmd.options`. Add a synthetic entry so tab-completion suggests it.
105
+ options.push({
106
+ long: "--help",
107
+ short: "-h",
108
+ description: "Show help for this command",
109
+ takesValue: false,
110
+ variadic: false,
111
+ });
112
+
113
+ const positionals: PositionalSpec[] = cmd.registeredArguments.map((a, i) =>
114
+ positionalSpec(a, path, i, lookup),
115
+ );
116
+
117
+ return {
118
+ name,
119
+ path,
120
+ description: (cmd.description() || "").replace(/\s+/g, " ").trim(),
121
+ subcommands,
122
+ options,
123
+ positionals,
124
+ hasHelp: true,
125
+ hidden: isHidden(cmd),
126
+ };
127
+ }
128
+
129
+ function optionSpec(opt: Option, commandPath: string, lookup: CompletionContextLookup): OptionSpec {
130
+ const long = opt.long ?? "";
131
+ const short = opt.short ?? "";
132
+ const takesValue = opt.required || opt.optional;
133
+ const provider = lookup({
134
+ commandPath,
135
+ option: long || short,
136
+ });
137
+ return {
138
+ long,
139
+ short,
140
+ description: (opt.description || "").replace(/\s+/g, " ").trim(),
141
+ takesValue,
142
+ valueChoices: opt.argChoices ? [...opt.argChoices] : undefined,
143
+ dynamicProvider: provider,
144
+ variadic: opt.variadic,
145
+ };
146
+ }
147
+
148
+ function positionalSpec(
149
+ arg: Argument,
150
+ commandPath: string,
151
+ index: number,
152
+ lookup: CompletionContextLookup,
153
+ ): PositionalSpec {
154
+ const provider = lookup({
155
+ commandPath,
156
+ positional: index,
157
+ });
158
+ return {
159
+ name: arg.name(),
160
+ description: (arg.description || "").replace(/\s+/g, " ").trim(),
161
+ required: arg.required,
162
+ variadic: arg.variadic,
163
+ valueChoices: arg.argChoices ? [...arg.argChoices] : undefined,
164
+ dynamicProvider: provider,
165
+ };
166
+ }
167
+
168
+ function isHidden(cmd: Command): boolean {
169
+ // Commander marks hidden commands via `command(..., { hidden: true })`.
170
+ // The flag lives on a private field; we read it defensively via `any`.
171
+ const c = cmd as unknown as { _hidden?: boolean };
172
+ return c._hidden === true;
173
+ }
174
+
175
+ /** Flatten the spec tree into a list of (path, spec) entries for code generation. */
176
+ export function flatten(root: CommandSpec): Array<{ path: string; spec: CommandSpec }> {
177
+ const out: Array<{ path: string; spec: CommandSpec }> = [];
178
+ const walk = (s: CommandSpec): void => {
179
+ out.push({ path: s.path, spec: s });
180
+ for (const sub of s.subcommands) walk(sub);
181
+ };
182
+ walk(root);
183
+ return out;
184
+ }