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,838 @@
1
+ /**
2
+ * `agent-coord` CLI entry point. Phase 1: every subcommand no-ops with a
3
+ * structured debug log. The one exception is `verdict`, which replies
4
+ * fail-open so adapters can already wire it up without affecting flow.
5
+ *
6
+ * Phase 2 replaces the no-op branches with real state projection + CLI
7
+ * handlers; Phase 4 flips the default so `harn agents …` shims through here.
8
+ */
9
+
10
+ import { appendFileSync, existsSync } from "node:fs";
11
+ import { appendFile, mkdir } from "node:fs/promises";
12
+ import { dirname, join, resolve } from "node:path";
13
+ import { coordEnv } from "../../lib/env.ts";
14
+
15
+ function findCoordRoot(start: string): string | null {
16
+ // HARNERY_COORD_ROOT_OVERRIDE: the bash side's test-mode escape hatch. agent-coord
17
+ // honors the same env so sandboxed test runs don't get derailed when cwd
18
+ // doesn't contain a .harnery/ tree. Phase 8: dropped the `.harnery/` existence
19
+ // precondition so test fixtures that haven't bootstrapped the dir yet still
20
+ // resolve.
21
+ const override = coordEnv("COORD_ROOT_OVERRIDE");
22
+ if (override) return override;
23
+ let dir = resolve(start);
24
+ while (true) {
25
+ if (existsSync(join(dir, ".harnery"))) return dir;
26
+ const parent = dirname(dir);
27
+ if (parent === dir) return null;
28
+ dir = parent;
29
+ }
30
+ }
31
+
32
+ async function readStdin(): Promise<string> {
33
+ if (process.stdin.isTTY) return "";
34
+ const chunks: Uint8Array[] = [];
35
+ for await (const chunk of process.stdin) {
36
+ chunks.push(chunk as Uint8Array);
37
+ }
38
+ return Buffer.concat(chunks).toString("utf8");
39
+ }
40
+
41
+ async function logNoop(root: string, subcommand: string, argv: string[]): Promise<void> {
42
+ const logPath = join(root, ".harnery", "debug", "agent-coord.ndjson");
43
+ await mkdir(dirname(logPath), { recursive: true });
44
+ const entry = {
45
+ ts: new Date().toISOString(),
46
+ note: "called, no-op",
47
+ subcommand,
48
+ extra_argv: argv,
49
+ cwd: process.cwd(),
50
+ pid: process.pid,
51
+ ppid: process.ppid,
52
+ };
53
+ await appendFile(logPath, `${JSON.stringify(entry)}\n`, "utf8");
54
+ }
55
+
56
+ /**
57
+ * Verdict endpoint. Reads a JSON request from stdin, dispatches to the
58
+ * matching rule evaluator, writes a JSON verdict to stdout. Exit code is
59
+ * 0 regardless; the caller branches on the JSON's `exit_code` field so
60
+ * fail-open semantics survive a malformed-request bug here.
61
+ *
62
+ * The Stop and PreToolUse hooks route here.
63
+ */
64
+ async function handleVerdict(root: string): Promise<number> {
65
+ const raw = await readStdin();
66
+ const logPath = join(root, ".harnery", "debug", "agent-coord-verdict.ndjson");
67
+ await mkdir(dirname(logPath), { recursive: true });
68
+
69
+ let parsed: { rule?: string } & Record<string, unknown> = {};
70
+ let parseErr: string | null = null;
71
+ try {
72
+ if (raw.trim().length > 0) {
73
+ parsed = JSON.parse(raw) as { rule?: string } & Record<string, unknown>;
74
+ }
75
+ } catch (err) {
76
+ parseErr = err instanceof Error ? err.message : String(err);
77
+ }
78
+
79
+ let verdict: {
80
+ allow: boolean;
81
+ exit_code: number;
82
+ rule: string;
83
+ reason?: string;
84
+ };
85
+
86
+ if (parseErr) {
87
+ verdict = {
88
+ allow: true,
89
+ exit_code: 0,
90
+ rule: "verdict.bad_request",
91
+ reason: `invalid JSON: ${parseErr} (fail-open)`,
92
+ };
93
+ } else if (parsed.rule === "stop-hook") {
94
+ const { evaluateStopHook } = await import("./rules/stop-hook.ts");
95
+ verdict = evaluateStopHook(root, parsed as unknown as Parameters<typeof evaluateStopHook>[1]);
96
+ } else if (parsed.rule === "claim") {
97
+ const { evaluateClaim } = await import("./rules/claim-conflict.ts");
98
+ verdict = evaluateClaim(root, parsed as unknown as Parameters<typeof evaluateClaim>[1]);
99
+ } else if (parsed.rule === "commit") {
100
+ const { evaluateCommit } = await import("./rules/commit-conflict.ts");
101
+ const result = evaluateCommit(root, parsed as unknown as Parameters<typeof evaluateCommit>[1]);
102
+ // Map CommitVerdictResult → the standard verdict envelope, stash details
103
+ // on extra fields so the bash caller can pull conflicts + log_lines.
104
+ verdict = {
105
+ allow: result.allow,
106
+ exit_code: result.exit_code,
107
+ rule: result.rule,
108
+ reason: result.message,
109
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
110
+ } as typeof verdict;
111
+ (verdict as Record<string, unknown>).conflicts = result.conflicts;
112
+ (verdict as Record<string, unknown>).log_lines = result.log_lines;
113
+ (verdict as Record<string, unknown>).suppressed_self_attribution =
114
+ result.suppressed_self_attribution ?? false;
115
+ } else {
116
+ verdict = {
117
+ allow: true,
118
+ exit_code: 0,
119
+ rule: "verdict.unknown_rule",
120
+ reason: `no evaluator for rule=${parsed.rule ?? "<missing>"} (fail-open)`,
121
+ };
122
+ }
123
+
124
+ await appendFile(
125
+ logPath,
126
+ `${JSON.stringify({
127
+ ts: new Date().toISOString(),
128
+ request_preview: raw.slice(0, 500),
129
+ verdict,
130
+ })}\n`,
131
+ "utf8",
132
+ );
133
+
134
+ process.stdout.write(`${JSON.stringify(verdict)}\n`);
135
+ return 0;
136
+ }
137
+
138
+ async function handleProject(root: string, rest: string[]): Promise<number> {
139
+ const { consumeSince, writeCursor } = await import("./events/consume.ts");
140
+ const { projectHeartbeats } = await import("./state/heartbeat-projector.ts");
141
+ const replayAll = rest.includes("--replay-all");
142
+ const result = consumeSince(root, { replayAll });
143
+ const project = projectHeartbeats(root, result.events);
144
+ const report = {
145
+ events_consumed: result.events.length,
146
+ stream_bytes: result.streamBytes,
147
+ owners_projected: project.written.length,
148
+ owners: project.written,
149
+ cursor: result.lastEventId,
150
+ replayed_all: replayAll,
151
+ };
152
+ if (result.lastEventId) writeCursor(root, result.lastEventId);
153
+ if (rest.includes("--json")) {
154
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
155
+ } else {
156
+ process.stdout.write(
157
+ `projected ${report.events_consumed} events across ${report.owners_projected} owners\n cursor → ${report.cursor ?? "<none>"}\n${
158
+ report.owners.length
159
+ ? ` owners: ${report.owners.map((o) => o.slice(0, 8)).join(", ")}\n`
160
+ : ""
161
+ }`,
162
+ );
163
+ }
164
+ return 0;
165
+ }
166
+
167
+ async function handleStateAction(root: string, action: string, rest: string[]): Promise<number> {
168
+ const writer = await import("./state/heartbeat-writer.ts");
169
+ const [owner, ...args] = rest;
170
+ if (!owner) {
171
+ process.stderr.write(`agent-coord ${action}: missing <instance_id>\n`);
172
+ return 2;
173
+ }
174
+
175
+ switch (action) {
176
+ case "set-task": {
177
+ const task = args.join(" ");
178
+ const hb = writer.setTask(root, owner, task);
179
+ if (!hb) {
180
+ process.stderr.write(
181
+ `agent-coord set-task: no heartbeat at .harnery/active/${owner}.json\n`,
182
+ );
183
+ return 1;
184
+ }
185
+ process.stdout.write(
186
+ `${JSON.stringify({ instance_id: owner, task: hb.task ?? null, cleared: !task })}\n`,
187
+ );
188
+ return 0;
189
+ }
190
+ case "stamp-status-call": {
191
+ const hb = writer.stampStatusCheck(root, owner);
192
+ if (!hb) return 1;
193
+ process.stdout.write(
194
+ `${JSON.stringify({ instance_id: owner, last_status_at: hb.last_status_at })}\n`,
195
+ );
196
+ return 0;
197
+ }
198
+ case "set-turn-summary": {
199
+ const summary = args.join(" ");
200
+ const hb = writer.setTurnSummary(root, owner, summary);
201
+ if (!hb) return 1;
202
+ process.stdout.write(
203
+ `${JSON.stringify({ instance_id: owner, turn_summary: hb.turn_summary })}\n`,
204
+ );
205
+ return 0;
206
+ }
207
+ case "release-claim": {
208
+ const path = args[0];
209
+ if (!path) {
210
+ process.stderr.write("agent-coord release-claim: missing <path>\n");
211
+ return 2;
212
+ }
213
+ const hb = writer.releaseClaim(root, owner, path);
214
+ if (!hb) return 1;
215
+ process.stdout.write(
216
+ `${JSON.stringify({ instance_id: owner, files_touched: hb.files_touched })}\n`,
217
+ );
218
+ return 0;
219
+ }
220
+ case "kill-heartbeat": {
221
+ const ok = writer.killHeartbeat(root, owner);
222
+ process.stdout.write(`${JSON.stringify({ instance_id: owner, removed: ok })}\n`);
223
+ return ok ? 0 : 1;
224
+ }
225
+ case "heal-pidmap": {
226
+ const pidArg = args[0];
227
+ const pid = pidArg ? Number(pidArg) : process.ppid;
228
+ if (!Number.isFinite(pid)) {
229
+ process.stderr.write(`agent-coord heal-pidmap: invalid pid ${pidArg}\n`);
230
+ return 2;
231
+ }
232
+ writer.healPidmap(root, owner, pid);
233
+ process.stdout.write(`${JSON.stringify({ instance_id: owner, pid })}\n`);
234
+ return 0;
235
+ }
236
+ case "heal-heartbeat": {
237
+ // harness arrives as a `--harness=<h>` flag (not positional) so the live
238
+ // tool.pre_use heal and the manual `harn agents heal` path (which pass
239
+ // different positional counts) can both supply it without arg-order
240
+ // fragility. Positionals (sessionId, model) stay as-is once flags are
241
+ // filtered out.
242
+ const harness = args.find((a) => a.startsWith("--harness="))?.slice("--harness=".length);
243
+ const positional = args.filter((a) => !a.startsWith("--"));
244
+ const sessionId = positional[0];
245
+ const model = positional[1];
246
+ const hb = writer.healHeartbeat(root, owner, sessionId, model, harness);
247
+ process.stdout.write(`${JSON.stringify({ instance_id: owner, recreated: !!hb })}\n`);
248
+ return hb ? 0 : 1;
249
+ }
250
+ case "stamp-tool-activity": {
251
+ const toolName = args[0] ?? "";
252
+ const target = args.slice(1).join(" ");
253
+ const hb = writer.stampToolActivity(root, owner, toolName, target);
254
+ if (!hb) return 1;
255
+ process.stdout.write(
256
+ `${JSON.stringify({
257
+ instance_id: owner,
258
+ last_tool: hb.last_tool,
259
+ last_tool_target: hb.last_tool_target,
260
+ })}\n`,
261
+ );
262
+ return 0;
263
+ }
264
+ default:
265
+ process.stderr.write(`agent-coord: unknown state action ${action}\n`);
266
+ return 2;
267
+ }
268
+ }
269
+
270
+ async function handleScratchAction(root: string, action: string, rest: string[]): Promise<number> {
271
+ const scratch = await import("./state/scratch.ts");
272
+
273
+ if (action === "append-scratch") {
274
+ const [owner, category, ...bodyParts] = rest;
275
+ const body = bodyParts.join(" ");
276
+ if (!owner || !category || !body) {
277
+ process.stderr.write("agent-coord append-scratch <instance_id> <category> <body>\n");
278
+ return 2;
279
+ }
280
+ const result = scratch.appendScratch(root, owner, category, body);
281
+ if (!result.ok) {
282
+ process.stderr.write(`agent-coord append-scratch: ${result.reason}\n`);
283
+ return 1;
284
+ }
285
+ process.stdout.write(
286
+ `${JSON.stringify({ instance_id: owner, category, path: result.path })}\n`,
287
+ );
288
+ return 0;
289
+ }
290
+
291
+ if (action === "edit-scratchpad") {
292
+ const [owner, newBodyFile, ...summaryParts] = rest;
293
+ const summary = summaryParts.join(" ");
294
+ if (!owner || !newBodyFile) {
295
+ process.stderr.write(
296
+ "agent-coord edit-scratchpad <instance_id> <new-body-file> [<summary>]\n",
297
+ );
298
+ return 2;
299
+ }
300
+ if (!existsSync(newBodyFile)) {
301
+ process.stderr.write(`agent-coord edit-scratchpad: file not found: ${newBodyFile}\n`);
302
+ return 2;
303
+ }
304
+ const { readFileSync } = await import("node:fs");
305
+ const newBody = readFileSync(newBodyFile, "utf8");
306
+ const result = scratch.editScratchpad(root, owner, newBody, summary);
307
+ if (!result.ok) {
308
+ process.stderr.write(`agent-coord edit-scratchpad: ${result.reason}\n`);
309
+ return 1;
310
+ }
311
+ process.stdout.write(
312
+ `${JSON.stringify({ instance_id: owner, path: result.path, archive_path: result.archivePath })}\n`,
313
+ );
314
+ return 0;
315
+ }
316
+
317
+ process.stderr.write(`agent-coord: unknown scratch action ${action}\n`);
318
+ return 2;
319
+ }
320
+
321
+ async function handleCouncilAction(root: string, action: string, rest: string[]): Promise<number> {
322
+ const council = await import("./state/council.ts");
323
+
324
+ const [councilId, ...args] = rest;
325
+ if (!councilId) {
326
+ process.stderr.write(`agent-coord ${action}: missing <council_id>\n`);
327
+ return 2;
328
+ }
329
+
330
+ switch (action) {
331
+ case "council-advance": {
332
+ const force = args.includes("--force");
333
+ const result = council.advanceCouncil(root, councilId, { force });
334
+ if (!result.ok) {
335
+ process.stderr.write(`agent-coord council-advance: ${result.reason}\n`);
336
+ return 1;
337
+ }
338
+ process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
339
+ return 0;
340
+ }
341
+ case "council-close": {
342
+ const result = council.closeCouncil(root, councilId);
343
+ if (!result.ok) {
344
+ process.stderr.write(`agent-coord council-close: ${result.reason}\n`);
345
+ return 1;
346
+ }
347
+ process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
348
+ return 0;
349
+ }
350
+ case "council-archive": {
351
+ const result = council.archiveCouncil(root, councilId);
352
+ if (!result.ok) {
353
+ process.stderr.write(`agent-coord council-archive: ${result.reason}\n`);
354
+ return 1;
355
+ }
356
+ process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
357
+ return 0;
358
+ }
359
+ case "council-unarchive": {
360
+ const result = council.unarchiveCouncil(root, councilId);
361
+ if (!result.ok) {
362
+ process.stderr.write(`agent-coord council-unarchive: ${result.reason}\n`);
363
+ return 1;
364
+ }
365
+ process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
366
+ return 0;
367
+ }
368
+ case "council-delete": {
369
+ const result = council.deleteCouncil(root, councilId);
370
+ if (!result.ok) {
371
+ process.stderr.write(`agent-coord council-delete: ${result.reason}\n`);
372
+ return 1;
373
+ }
374
+ process.stdout.write(`${JSON.stringify({ council_id: councilId, ok: true })}\n`);
375
+ return 0;
376
+ }
377
+ case "council-set-steward": {
378
+ const steward = args[0] ?? "";
379
+ const stewardId = args[1] ?? "";
380
+ const result = council.setCouncilSteward(root, councilId, steward, stewardId);
381
+ if (!result.ok) {
382
+ process.stderr.write(`agent-coord council-set-steward: ${result.reason}\n`);
383
+ return 1;
384
+ }
385
+ process.stdout.write(
386
+ `${JSON.stringify({ council_id: councilId, steward: steward || null, ok: true })}\n`,
387
+ );
388
+ return 0;
389
+ }
390
+ }
391
+
392
+ process.stderr.write(`agent-coord: unknown council action ${action}\n`);
393
+ return 2;
394
+ }
395
+
396
+ async function handleAssignName(root: string, rest: string[]): Promise<number> {
397
+ const { assignName } = await import("./state/names.ts");
398
+ const [owner, kindArg] = rest;
399
+ if (!owner || !kindArg) {
400
+ process.stderr.write("agent-coord assign-name <instance_id> <session|subagent|transient>\n");
401
+ return 2;
402
+ }
403
+ if (kindArg !== "session" && kindArg !== "subagent" && kindArg !== "transient") {
404
+ process.stderr.write(`agent-coord assign-name: invalid kind ${kindArg}\n`);
405
+ return 2;
406
+ }
407
+ const name = assignName(root, owner, kindArg);
408
+ process.stdout.write(`${JSON.stringify({ instance_id: owner, name, kind: kindArg })}\n`);
409
+ return 0;
410
+ }
411
+
412
+ async function handlePostCommit(root: string): Promise<number> {
413
+ const raw = await readStdin();
414
+ let req: { owner?: string; prune?: string[] } = {};
415
+ try {
416
+ req = JSON.parse(raw);
417
+ } catch {
418
+ return 0;
419
+ }
420
+ const { groupUnclaim } = await import("./state/heartbeat-writer.ts");
421
+
422
+ // Session-group-wide unclaim. `owner` is the parent's session_id which is
423
+ // also the group key (parent + subagents share session_id).
424
+ if (req.owner && Array.isArray(req.prune)) {
425
+ for (const path of req.prune) {
426
+ try {
427
+ groupUnclaim(root, req.owner, path);
428
+ } catch {
429
+ /* best-effort */
430
+ }
431
+ }
432
+ }
433
+ return 0;
434
+ }
435
+
436
+ async function handlePostCheckout(root: string, _rest: string[]): Promise<number> {
437
+ const raw = await readStdin();
438
+ let req: { owner?: string; removed?: string[] } = {};
439
+ try {
440
+ req = JSON.parse(raw);
441
+ } catch {
442
+ return 0;
443
+ }
444
+ const { groupUnclaim } = await import("./state/heartbeat-writer.ts");
445
+ if (req.owner && Array.isArray(req.removed)) {
446
+ for (const path of req.removed) {
447
+ try {
448
+ groupUnclaim(root, req.owner, path);
449
+ } catch {
450
+ /* best-effort */
451
+ }
452
+ }
453
+ }
454
+ return 0;
455
+ }
456
+
457
+ async function handleShellMutationPaths(root: string, rest: string[]): Promise<number> {
458
+ const { shellMutationPaths } = await import("./state/shell-mutation.ts");
459
+ // --cmd "<string>" form; falls back to stdin if --cmd not supplied
460
+ let cmd: string | undefined;
461
+ for (let i = 0; i < rest.length; i++) {
462
+ const a = rest[i]!;
463
+ if (a === "--cmd") {
464
+ cmd = rest[i + 1];
465
+ i++;
466
+ }
467
+ }
468
+ if (cmd === undefined) cmd = await readStdin();
469
+ const paths = shellMutationPaths(cmd, root);
470
+ for (const p of paths) process.stdout.write(`${p}\n`);
471
+ return 0;
472
+ }
473
+
474
+ async function handleShellMutationClaimLog(root: string, rest: string[]): Promise<number> {
475
+ // Parse + log in one spawn, avoids per-line process spawn from the bash loop.
476
+ // Usage:
477
+ // agent-coord shell-mutation-claim-log --cmd "<string>" --owner <id> --platform <p>
478
+ const args: Record<string, string> = {};
479
+ for (let i = 0; i < rest.length; i++) {
480
+ const a = rest[i]!;
481
+ if (a.startsWith("--")) {
482
+ const key = a.slice(2);
483
+ const val = rest[i + 1];
484
+ if (val === undefined || val.startsWith("--")) {
485
+ args[key] = "true";
486
+ } else {
487
+ args[key] = val;
488
+ i++;
489
+ }
490
+ }
491
+ }
492
+ const cmd = args.cmd ?? "";
493
+ if (!cmd) return 0;
494
+ const platform = args.platform ?? "unknown";
495
+ const owner = args.owner ?? null;
496
+ const { shellMutationPaths } = await import("./state/shell-mutation.ts");
497
+ const { emit } = await import("./events/emit.ts");
498
+ const { readHeartbeat } = await import("./state/heartbeat-writer.ts");
499
+ const paths = shellMutationPaths(cmd, root);
500
+ const truncated = cmd.length > 80 ? cmd.slice(0, 80) : cmd;
501
+ // Warn-only peer-shell-mutation signal. Formerly a SHELL_CLAIM_CANDIDATE line
502
+ // in a log file; now a canonical decision.warn so the
503
+ // signal survives in events.ndjson. (The blocking claim-conflict path is
504
+ // separate: claim.conflict / verdict, and unaffected.)
505
+ const harness = platform === "cursor" ? "cursor" : platform === "codex" ? "codex" : "claude-code";
506
+ const hb = owner ? readHeartbeat(root, owner) : null;
507
+ for (const p of paths) {
508
+ try {
509
+ emit(root, {
510
+ event_type: "decision.warn",
511
+ instance_id: owner ?? "unknown",
512
+ session_id: hb?.session_id ?? owner ?? "unknown",
513
+ harness,
514
+ data: {
515
+ rule: "shell_mutation_candidate",
516
+ reason: `path=${p} cmd=${truncated} platform=${platform}`,
517
+ },
518
+ });
519
+ } catch {
520
+ /* telemetry only, never break the dispatcher */
521
+ }
522
+ }
523
+ return 0;
524
+ }
525
+
526
+ async function handleStaleSweep(root: string, _rest: string[]): Promise<number> {
527
+ const { staleSweep } = await import("./state/stale-sweep.ts");
528
+ const result = staleSweep(root);
529
+ process.stdout.write(`${JSON.stringify(result)}\n`);
530
+ return 0;
531
+ }
532
+
533
+ async function handlePromptContext(root: string, rest: string[]): Promise<number> {
534
+ const args: Record<string, string> = {};
535
+ for (let i = 0; i < rest.length; i++) {
536
+ const a = rest[i]!;
537
+ if (a.startsWith("--")) {
538
+ const key = a.slice(2);
539
+ const val = rest[i + 1];
540
+ if (val === undefined || val.startsWith("--")) {
541
+ args[key] = "true";
542
+ } else {
543
+ args[key] = val;
544
+ i++;
545
+ }
546
+ }
547
+ }
548
+ const instanceId = args.instance;
549
+ const sessionId = args.session ?? instanceId;
550
+ const agentName = args.name;
551
+ const taskNudge = args["task-nudge"] === "true";
552
+ if (!instanceId) {
553
+ process.stderr.write(
554
+ "agent-coord prompt-context --instance <id> [--session <id>] [--name <agent-name>] [--task-nudge]\n",
555
+ );
556
+ return 2;
557
+ }
558
+ const { renderPromptContext } = await import("./render/prompt-context.ts");
559
+ const text = renderPromptContext({
560
+ coordRoot: root,
561
+ instanceId,
562
+ sessionId: sessionId!,
563
+ agentName,
564
+ taskNudge,
565
+ });
566
+ process.stdout.write(text);
567
+ return 0;
568
+ }
569
+
570
+ async function handleSessionContext(root: string, rest: string[]): Promise<number> {
571
+ const args: Record<string, string> = {};
572
+ for (let i = 0; i < rest.length; i++) {
573
+ const a = rest[i]!;
574
+ if (a.startsWith("--")) {
575
+ const key = a.slice(2);
576
+ const val = rest[i + 1];
577
+ if (val === undefined || val.startsWith("--")) {
578
+ args[key] = "true";
579
+ } else {
580
+ args[key] = val;
581
+ i++;
582
+ }
583
+ }
584
+ }
585
+ const instanceId = args.instance;
586
+ const sessionId = args.session ?? instanceId;
587
+ const agentName = args.name;
588
+ const platformLabel = args["platform-label"];
589
+ if (!instanceId) {
590
+ process.stderr.write(
591
+ "agent-coord session-context --instance <id> [--session <id>] [--name <agent-name>] [--platform-label <label>]\n",
592
+ );
593
+ return 2;
594
+ }
595
+ const { renderSessionContext } = await import("./render/session-context.ts");
596
+ const text = renderSessionContext({
597
+ coordRoot: root,
598
+ instanceId,
599
+ sessionId: sessionId!,
600
+ agentName,
601
+ platformLabel,
602
+ });
603
+ process.stdout.write(text);
604
+ return 0;
605
+ }
606
+
607
+ async function handleCodexReplay(root: string, rest: string[]): Promise<number> {
608
+ const args: Record<string, string> = {};
609
+ for (let i = 0; i < rest.length; i++) {
610
+ const a = rest[i]!;
611
+ if (a.startsWith("--")) {
612
+ const key = a.slice(2);
613
+ const val = rest[i + 1];
614
+ if (val === undefined || val.startsWith("--")) {
615
+ args[key] = "true";
616
+ } else {
617
+ args[key] = val;
618
+ i++;
619
+ }
620
+ }
621
+ }
622
+ const jsonlPath = args.jsonl;
623
+ const sessionId = args.session;
624
+ const instanceId = args.owner ?? sessionId;
625
+ const lastMsg = args["last-message"];
626
+ if (!jsonlPath || !sessionId) {
627
+ process.stderr.write(
628
+ "agent-coord codex-replay --jsonl <path> --session <id> [--owner <id>] [--last-message <text>]\n",
629
+ );
630
+ return 2;
631
+ }
632
+ const { replayCodexJsonl } = await import("./codex-replay.ts");
633
+ const result = replayCodexJsonl({
634
+ coordRoot: root,
635
+ jsonlPath,
636
+ sessionId,
637
+ instanceId: instanceId!,
638
+ lastAssistantMessage: lastMsg,
639
+ });
640
+ process.stdout.write(`${JSON.stringify({ session_id: sessionId, emitted: result.emitted })}\n`);
641
+ return 0;
642
+ }
643
+
644
+ async function handleResolveName(root: string, rest: string[]): Promise<number> {
645
+ const { resolveName } = await import("./state/names.ts");
646
+ const [owner, session] = rest;
647
+ if (!owner) {
648
+ process.stderr.write("agent-coord resolve-name <instance_id> [<session_id>]\n");
649
+ return 2;
650
+ }
651
+ const resolved = resolveName(root, owner, session);
652
+ if (!resolved) {
653
+ process.stdout.write(`${JSON.stringify({ instance_id: owner, name: null, kind: null })}\n`);
654
+ return 0;
655
+ }
656
+ process.stdout.write(
657
+ `${JSON.stringify({ instance_id: owner, name: resolved.name, kind: resolved.kind })}\n`,
658
+ );
659
+ return 0;
660
+ }
661
+
662
+ async function handleEmitEvent(root: string, rest: string[]): Promise<number> {
663
+ const { emitAndProject } = await import("./cli-emit.ts");
664
+ const args: Record<string, string> = {};
665
+ for (let i = 0; i < rest.length; i++) {
666
+ const a = rest[i]!;
667
+ if (a.startsWith("--")) {
668
+ const key = a.slice(2);
669
+ const val = rest[i + 1];
670
+ if (val === undefined || val.startsWith("--")) {
671
+ args[key] = "true";
672
+ } else {
673
+ args[key] = val;
674
+ i++;
675
+ }
676
+ }
677
+ }
678
+
679
+ const eventType = args.type;
680
+ const instanceId = args.owner;
681
+ const sessionId = args.session;
682
+ const harness = args.harness as "claude-code" | "cursor" | "codex" | undefined;
683
+ const dataJson = args["data-json"] ?? "{}";
684
+
685
+ if (!eventType || !instanceId || !sessionId || !harness) {
686
+ process.stderr.write(
687
+ "agent-coord emit-event --type <T> --owner <id> --session <id> --harness <h> [--data-json '<json>']\n",
688
+ );
689
+ return 2;
690
+ }
691
+
692
+ let data: Record<string, unknown>;
693
+ try {
694
+ const parsed = JSON.parse(dataJson) as unknown;
695
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
696
+ data = parsed as Record<string, unknown>;
697
+ } else {
698
+ process.stderr.write("agent-coord emit-event: --data-json must encode an object\n");
699
+ return 2;
700
+ }
701
+ } catch (err) {
702
+ process.stderr.write(
703
+ `agent-coord emit-event: invalid --data-json (${err instanceof Error ? err.message : String(err)})\n`,
704
+ );
705
+ return 2;
706
+ }
707
+
708
+ const result = emitAndProject(
709
+ {
710
+ event_type: eventType,
711
+ instance_id: instanceId,
712
+ session_id: sessionId,
713
+ harness,
714
+ turn_id: args["turn-id"],
715
+ parent_session_id: args["parent-session-id"],
716
+ parent_turn_id: args["parent-turn-id"],
717
+ data,
718
+ },
719
+ { coordRoot: root },
720
+ );
721
+
722
+ if (!result) {
723
+ process.stderr.write("agent-coord emit-event: emission failed\n");
724
+ return 1;
725
+ }
726
+
727
+ process.stdout.write(`${JSON.stringify(result.envelope)}\n`);
728
+ return 0;
729
+ }
730
+
731
+ async function main(): Promise<number> {
732
+ const [subcommand, ...rest] = process.argv.slice(2);
733
+ const root = findCoordRoot(process.cwd());
734
+ if (!root) return 0;
735
+
736
+ if (subcommand === "verdict") {
737
+ return handleVerdict(root);
738
+ }
739
+
740
+ if (subcommand === "project") {
741
+ return handleProject(root, rest);
742
+ }
743
+
744
+ if (subcommand === "emit-event") {
745
+ return handleEmitEvent(root, rest);
746
+ }
747
+
748
+ if (
749
+ subcommand === "set-task" ||
750
+ subcommand === "stamp-status-call" ||
751
+ subcommand === "set-turn-summary" ||
752
+ subcommand === "release-claim" ||
753
+ subcommand === "kill-heartbeat" ||
754
+ subcommand === "heal-pidmap" ||
755
+ subcommand === "heal-heartbeat" ||
756
+ subcommand === "stamp-tool-activity"
757
+ ) {
758
+ return handleStateAction(root, subcommand, rest);
759
+ }
760
+
761
+ if (subcommand === "append-scratch" || subcommand === "edit-scratchpad") {
762
+ return handleScratchAction(root, subcommand, rest);
763
+ }
764
+
765
+ if (
766
+ subcommand === "council-advance" ||
767
+ subcommand === "council-close" ||
768
+ subcommand === "council-archive" ||
769
+ subcommand === "council-unarchive" ||
770
+ subcommand === "council-delete" ||
771
+ subcommand === "council-set-steward"
772
+ ) {
773
+ return handleCouncilAction(root, subcommand, rest);
774
+ }
775
+
776
+ if (subcommand === "assign-name") {
777
+ return handleAssignName(root, rest);
778
+ }
779
+
780
+ if (subcommand === "resolve-name") {
781
+ return handleResolveName(root, rest);
782
+ }
783
+
784
+ if (subcommand === "codex-replay") {
785
+ return handleCodexReplay(root, rest);
786
+ }
787
+
788
+ if (subcommand === "stale-sweep") {
789
+ return handleStaleSweep(root, rest);
790
+ }
791
+
792
+ if (subcommand === "session-context") {
793
+ return handleSessionContext(root, rest);
794
+ }
795
+
796
+ if (subcommand === "prompt-context") {
797
+ return handlePromptContext(root, rest);
798
+ }
799
+
800
+ if (subcommand === "shell-mutation-paths") {
801
+ return handleShellMutationPaths(root, rest);
802
+ }
803
+
804
+ if (subcommand === "shell-mutation-claim-log") {
805
+ return handleShellMutationClaimLog(root, rest);
806
+ }
807
+
808
+ if (subcommand === "post-commit") {
809
+ return handlePostCommit(root);
810
+ }
811
+
812
+ if (subcommand === "post-checkout") {
813
+ return handlePostCheckout(root, rest);
814
+ }
815
+
816
+ await logNoop(root, subcommand ?? "(none)", rest);
817
+ // Phase 1/2: silent on stdout for unknown subcommands, exit 0. Existing
818
+ // `harn agents …` callers keep working unchanged.
819
+ return 0;
820
+ }
821
+
822
+ main()
823
+ .then((code) => process.exit(code))
824
+ .catch((err) => {
825
+ try {
826
+ const root = findCoordRoot(process.cwd());
827
+ if (root) {
828
+ const path = join(root, ".harnery", "debug", "agent-coord.errors.ndjson");
829
+ appendFileSync(
830
+ path,
831
+ `${JSON.stringify({ ts: new Date().toISOString(), error: String(err) })}\n`,
832
+ );
833
+ }
834
+ } catch {
835
+ /* swallow */
836
+ }
837
+ process.exit(0);
838
+ });