harnery 0.40.0 → 0.41.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 (441) hide show
  1. package/README.md +1 -1
  2. package/dist/commander.js +1 -0
  3. package/dist/commands/agents.d.ts.map +1 -1
  4. package/dist/commands/agents.js +25 -19
  5. package/dist/commands/browse-ai.d.ts.map +1 -1
  6. package/dist/commands/browse-ai.js +4 -0
  7. package/dist/commands/browse.d.ts.map +1 -1
  8. package/dist/commands/browse.js +141 -10
  9. package/dist/commands/checkpoint.js +8 -12
  10. package/dist/commands/deinit.d.ts +1 -1
  11. package/dist/commands/deinit.d.ts.map +1 -1
  12. package/dist/commands/deinit.js +8 -4
  13. package/dist/commands/docs.d.ts.map +1 -1
  14. package/dist/commands/docs.js +2 -0
  15. package/dist/commands/doctor.js +10 -2
  16. package/dist/commands/fetch.d.ts.map +1 -1
  17. package/dist/commands/fetch.js +10 -0
  18. package/dist/commands/init.d.ts +19 -3
  19. package/dist/commands/init.d.ts.map +1 -1
  20. package/dist/commands/init.js +232 -104
  21. package/dist/commands/instructions.js +5 -4
  22. package/dist/commands/ledger-v3.d.ts.map +1 -1
  23. package/dist/commands/ledger-v3.js +2 -1
  24. package/dist/commands/webview.d.ts +4 -0
  25. package/dist/commands/webview.d.ts.map +1 -0
  26. package/dist/commands/webview.js +94 -0
  27. package/dist/core/adapter.d.ts +50 -1
  28. package/dist/core/adapter.d.ts.map +1 -1
  29. package/dist/core/adapter.js +59 -1
  30. package/dist/core/adapters/attestation.d.ts.map +1 -1
  31. package/dist/core/adapters/attestation.js +3 -2
  32. package/dist/core/adapters/profiles.d.ts +20 -3
  33. package/dist/core/adapters/profiles.d.ts.map +1 -1
  34. package/dist/core/adapters/profiles.js +32 -1
  35. package/dist/core/adapters/registry.d.ts.map +1 -1
  36. package/dist/core/adapters/registry.js +33 -0
  37. package/dist/core/agents/canonical-emit.d.ts +1 -1
  38. package/dist/core/agents/canonical-emit.d.ts.map +1 -1
  39. package/dist/core/agents/canonical-emit.js +2 -7
  40. package/dist/core/agents/cli.js +18 -16
  41. package/dist/core/agents/coord-client.d.ts.map +1 -1
  42. package/dist/core/agents/coord-client.js +7 -2
  43. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
  44. package/dist/core/agents/rules/claim-conflict.js +3 -9
  45. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
  46. package/dist/core/agents/rules/commit-conflict.js +2 -8
  47. package/dist/core/agents/rules/stop-hook.d.ts +2 -0
  48. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
  49. package/dist/core/agents/rules/stop-hook.js +1 -1
  50. package/dist/core/agents/session-events.d.ts +6 -2
  51. package/dist/core/agents/session-events.d.ts.map +1 -1
  52. package/dist/core/agents/session-events.js +105 -26
  53. package/dist/core/agents/session-finalization-state-v3.d.ts.map +1 -1
  54. package/dist/core/agents/session-finalization-state-v3.js +2 -1
  55. package/dist/core/agents/session-finalizer-v3.d.ts.map +1 -1
  56. package/dist/core/agents/session-finalizer-v3.js +5 -4
  57. package/dist/core/agents/session-name-display.d.ts +28 -0
  58. package/dist/core/agents/session-name-display.d.ts.map +1 -1
  59. package/dist/core/agents/session-name-display.js +43 -0
  60. package/dist/core/agents/state/heartbeat-reader.d.ts +7 -0
  61. package/dist/core/agents/state/heartbeat-reader.d.ts.map +1 -1
  62. package/dist/core/agents/state/heartbeat-writer.d.ts +10 -0
  63. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
  64. package/dist/core/agents/state/heartbeat-writer.js +21 -2
  65. package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
  66. package/dist/core/agents/state/live-coordination-view.js +1 -0
  67. package/dist/core/agents/state/live-coordination-writer.js +3 -0
  68. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
  69. package/dist/core/agents/state/stale-sweep.js +2 -10
  70. package/dist/core/agents/turn-changes.d.ts +17 -0
  71. package/dist/core/agents/turn-changes.d.ts.map +1 -0
  72. package/dist/core/agents/turn-changes.js +76 -0
  73. package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
  74. package/dist/core/artifacts/delivery-card.js +2 -1
  75. package/dist/core/artifacts/index.d.ts.map +1 -1
  76. package/dist/core/artifacts/index.js +4 -3
  77. package/dist/core/config.d.ts +5 -0
  78. package/dist/core/config.d.ts.map +1 -1
  79. package/dist/core/config.js +14 -0
  80. package/dist/core/context/index.d.ts.map +1 -1
  81. package/dist/core/context/index.js +2 -1
  82. package/dist/core/conversations/archive.d.ts.map +1 -1
  83. package/dist/core/conversations/archive.js +4 -3
  84. package/dist/core/diagnostics/bundle.d.ts.map +1 -1
  85. package/dist/core/diagnostics/bundle.js +3 -2
  86. package/dist/core/events/legacy-storage/canary.d.ts.map +1 -1
  87. package/dist/core/events/legacy-storage/canary.js +5 -4
  88. package/dist/core/events/legacy-storage/compression.d.ts.map +1 -1
  89. package/dist/core/events/legacy-storage/compression.js +2 -1
  90. package/dist/core/events/v3/adapter-id.d.ts +10 -2
  91. package/dist/core/events/v3/adapter-id.d.ts.map +1 -1
  92. package/dist/core/events/v3/adapter-id.js +16 -0
  93. package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
  94. package/dist/core/events/v3/authority-outbox.js +2 -1
  95. package/dist/core/events/v3/bootstrap.d.ts.map +1 -1
  96. package/dist/core/events/v3/bootstrap.js +5 -4
  97. package/dist/core/events/v3/capabilities.d.ts.map +1 -1
  98. package/dist/core/events/v3/capabilities.js +17 -0
  99. package/dist/core/events/v3/contract.d.ts +5 -0
  100. package/dist/core/events/v3/contract.d.ts.map +1 -1
  101. package/dist/core/events/v3/contract.js +7 -1
  102. package/dist/core/events/v3/control-witness.d.ts.map +1 -1
  103. package/dist/core/events/v3/control-witness.js +3 -2
  104. package/dist/core/events/v3/coordination-transaction-recovery.d.ts.map +1 -1
  105. package/dist/core/events/v3/coordination-transaction-recovery.js +4 -3
  106. package/dist/core/events/v3/fingerprint-keys.d.ts.map +1 -1
  107. package/dist/core/events/v3/fingerprint-keys.js +7 -6
  108. package/dist/core/events/v3/generated.d.ts +1 -1
  109. package/dist/core/events/v3/generated.js +1 -1
  110. package/dist/core/events/v3/live-feed.d.ts.map +1 -1
  111. package/dist/core/events/v3/live-feed.js +5 -4
  112. package/dist/core/events/v3/producers/command-recorder.d.ts.map +1 -1
  113. package/dist/core/events/v3/producers/command-recorder.js +8 -7
  114. package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
  115. package/dist/core/events/v3/producers/coordination-recorder.js +8 -7
  116. package/dist/core/events/v3/producers/diagnostic-summaries.d.ts.map +1 -1
  117. package/dist/core/events/v3/producers/diagnostic-summaries.js +5 -4
  118. package/dist/core/events/v3/producers/hook-base.d.ts +1 -0
  119. package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
  120. package/dist/core/events/v3/producers/hook-base.js +10 -4
  121. package/dist/core/events/v3/producers/hook.d.ts +2 -0
  122. package/dist/core/events/v3/producers/hook.d.ts.map +1 -1
  123. package/dist/core/events/v3/producers/hook.js +16 -1
  124. package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
  125. package/dist/core/events/v3/producers/intake.js +16 -6
  126. package/dist/core/events/v3/producers/recorder.d.ts +2 -4
  127. package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
  128. package/dist/core/events/v3/producers/recorder.js +109 -101
  129. package/dist/core/events/v3/recovery.d.ts.map +1 -1
  130. package/dist/core/events/v3/recovery.js +4 -3
  131. package/dist/core/events/v3/support-storage/inventory.d.ts.map +1 -1
  132. package/dist/core/events/v3/support-storage/inventory.js +2 -1
  133. package/dist/core/events/v3/support-storage/maintenance.d.ts.map +1 -1
  134. package/dist/core/events/v3/support-storage/maintenance.js +4 -3
  135. package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
  136. package/dist/core/events/v3/support-storage/pack-reader.js +4 -3
  137. package/dist/core/events/v3/support-storage/pack-writer.d.ts.map +1 -1
  138. package/dist/core/events/v3/support-storage/pack-writer.js +6 -5
  139. package/dist/core/events/v3/tool-line-changes.d.ts +8 -0
  140. package/dist/core/events/v3/tool-line-changes.d.ts.map +1 -0
  141. package/dist/core/events/v3/tool-line-changes.js +135 -0
  142. package/dist/core/events/v3/writer.d.ts.map +1 -1
  143. package/dist/core/events/v3/writer.js +8 -7
  144. package/dist/core/governor/planning.d.ts.map +1 -1
  145. package/dist/core/governor/planning.js +6 -5
  146. package/dist/core/governor/service.d.ts.map +1 -1
  147. package/dist/core/governor/service.js +12 -11
  148. package/dist/core/governor/state.d.ts.map +1 -1
  149. package/dist/core/governor/state.js +7 -6
  150. package/dist/core/guard/evaluator.js +1 -0
  151. package/dist/core/guard/storage.d.ts.map +1 -1
  152. package/dist/core/guard/storage.js +3 -2
  153. package/dist/core/hooks/adapter/behaviors/claude-code.d.ts +4 -0
  154. package/dist/core/hooks/adapter/behaviors/claude-code.d.ts.map +1 -0
  155. package/dist/core/hooks/adapter/behaviors/claude-code.js +31 -0
  156. package/dist/core/hooks/adapter/behaviors/codex.d.ts +15 -0
  157. package/dist/core/hooks/adapter/behaviors/codex.d.ts.map +1 -0
  158. package/dist/core/hooks/adapter/behaviors/codex.js +82 -0
  159. package/dist/core/hooks/adapter/behaviors/cursor.d.ts +7 -0
  160. package/dist/core/hooks/adapter/behaviors/cursor.d.ts.map +1 -0
  161. package/dist/core/hooks/adapter/behaviors/cursor.js +35 -0
  162. package/dist/core/hooks/adapter/behaviors/index.d.ts +11 -0
  163. package/dist/core/hooks/adapter/behaviors/index.d.ts.map +1 -0
  164. package/dist/core/hooks/adapter/behaviors/index.js +20 -0
  165. package/dist/core/hooks/adapter/behaviors/openclaw.d.ts +7 -0
  166. package/dist/core/hooks/adapter/behaviors/openclaw.d.ts.map +1 -0
  167. package/dist/core/hooks/adapter/behaviors/openclaw.js +34 -0
  168. package/dist/core/hooks/adapter/behaviors/opencode.d.ts +7 -0
  169. package/dist/core/hooks/adapter/behaviors/opencode.d.ts.map +1 -0
  170. package/dist/core/hooks/adapter/behaviors/opencode.js +34 -0
  171. package/dist/core/hooks/adapter/behaviors/types.d.ts +89 -0
  172. package/dist/core/hooks/adapter/behaviors/types.d.ts.map +1 -0
  173. package/dist/core/hooks/adapter/behaviors/types.js +1 -0
  174. package/dist/core/hooks/adapter/detect.d.ts +1 -1
  175. package/dist/core/hooks/adapter/detect.d.ts.map +1 -1
  176. package/dist/core/hooks/adapter/detect.js +2 -3
  177. package/dist/core/hooks/adapter/events.d.ts +29 -1
  178. package/dist/core/hooks/adapter/events.d.ts.map +1 -1
  179. package/dist/core/hooks/adapter/events.js +31 -0
  180. package/dist/core/hooks/adapter/opencode-plugin.d.ts +60 -0
  181. package/dist/core/hooks/adapter/opencode-plugin.d.ts.map +1 -0
  182. package/dist/core/hooks/adapter/opencode-plugin.js +129 -0
  183. package/dist/core/hooks/adapter/output.d.ts +7 -0
  184. package/dist/core/hooks/adapter/output.d.ts.map +1 -1
  185. package/dist/core/hooks/adapter/output.js +8 -1
  186. package/dist/core/hooks/adapter/wiring.d.ts +15 -1
  187. package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
  188. package/dist/core/hooks/adapter/wiring.js +74 -11
  189. package/dist/core/hooks/cli.js +635 -529
  190. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -1
  191. package/dist/core/hooks/effects/image-capture.js +2 -1
  192. package/dist/core/hooks/prompt-context/contract.d.ts.map +1 -1
  193. package/dist/core/hooks/prompt-context/contract.js +1 -0
  194. package/dist/core/hooks/resolve/transcript.d.ts +1 -1
  195. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
  196. package/dist/core/hooks/resolve/transcript.js +35 -4
  197. package/dist/core/hooks/session-name-presence.d.ts +3 -1
  198. package/dist/core/hooks/session-name-presence.d.ts.map +1 -1
  199. package/dist/core/hooks/session-name-presence.js +7 -2
  200. package/dist/core/hooks/shell-waiter.d.ts +2 -0
  201. package/dist/core/hooks/shell-waiter.d.ts.map +1 -0
  202. package/dist/core/hooks/shell-waiter.js +173 -0
  203. package/dist/core/resources/service.d.ts.map +1 -1
  204. package/dist/core/resources/service.js +5 -4
  205. package/dist/core/semantic/review.d.ts.map +1 -1
  206. package/dist/core/semantic/review.js +5 -4
  207. package/dist/core/semantic/service.d.ts.map +1 -1
  208. package/dist/core/semantic/service.js +15 -14
  209. package/dist/core/semantic/storage.d.ts.map +1 -1
  210. package/dist/core/semantic/storage.js +5 -4
  211. package/dist/core/storage/atomic-json.d.ts.map +1 -1
  212. package/dist/core/storage/atomic-json.js +7 -3
  213. package/dist/core/storage/durable-history.d.ts.map +1 -1
  214. package/dist/core/storage/durable-history.js +6 -5
  215. package/dist/core/storage/maintenance-providers.d.ts.map +1 -1
  216. package/dist/core/storage/maintenance-providers.js +2 -1
  217. package/dist/core/storage/maintenance.d.ts.map +1 -1
  218. package/dist/core/storage/maintenance.js +7 -6
  219. package/dist/core/storage/metrics.d.ts.map +1 -1
  220. package/dist/core/storage/metrics.js +5 -1
  221. package/dist/core/storage/modes.d.ts +17 -0
  222. package/dist/core/storage/modes.d.ts.map +1 -0
  223. package/dist/core/storage/modes.js +47 -0
  224. package/dist/core/storage/process-log.d.ts.map +1 -1
  225. package/dist/core/storage/process-log.js +3 -2
  226. package/dist/core/storage/receipts.d.ts.map +1 -1
  227. package/dist/core/storage/receipts.js +4 -3
  228. package/dist/core/storage/segments.d.ts.map +1 -1
  229. package/dist/core/storage/segments.js +7 -6
  230. package/dist/core/supervisor/hook-health.d.ts +2 -1
  231. package/dist/core/supervisor/hook-health.d.ts.map +1 -1
  232. package/dist/core/supervisor/hook-health.js +4 -3
  233. package/dist/core/supervisor/service.d.ts.map +1 -1
  234. package/dist/core/supervisor/service.js +7 -6
  235. package/dist/core/work/state.d.ts.map +1 -1
  236. package/dist/core/work/state.js +8 -7
  237. package/dist/core/workflow/adapters.d.ts +7 -0
  238. package/dist/core/workflow/adapters.d.ts.map +1 -1
  239. package/dist/core/workflow/adapters.js +9 -0
  240. package/dist/core/workflow/approvals.d.ts.map +1 -1
  241. package/dist/core/workflow/approvals.js +6 -5
  242. package/dist/core/workflow/billing.d.ts.map +1 -1
  243. package/dist/core/workflow/billing.js +33 -4
  244. package/dist/core/workflow/durable-record.d.ts.map +1 -1
  245. package/dist/core/workflow/durable-record.js +4 -3
  246. package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
  247. package/dist/core/workflow/live-session-v3.js +2 -3
  248. package/dist/core/workflow/run-state.d.ts.map +1 -1
  249. package/dist/core/workflow/run-state.js +3 -2
  250. package/dist/core/workflow/spawn-opencode.d.ts +36 -0
  251. package/dist/core/workflow/spawn-opencode.d.ts.map +1 -0
  252. package/dist/core/workflow/spawn-opencode.js +132 -0
  253. package/dist/core/workflow/transcript.d.ts.map +1 -1
  254. package/dist/core/workflow/transcript.js +3 -2
  255. package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
  256. package/dist/core/workflow/workspaces/leases.js +5 -4
  257. package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
  258. package/dist/core/workflow/workspaces/local-git.js +3 -2
  259. package/dist/core/workflow/workspaces/paths.d.ts.map +1 -1
  260. package/dist/core/workflow/workspaces/paths.js +2 -1
  261. package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
  262. package/dist/core/workflow/workspaces/state.js +10 -9
  263. package/dist/lazy-commands.js +8 -1
  264. package/dist/lib/agent-browser/client.d.ts +9 -0
  265. package/dist/lib/agent-browser/client.d.ts.map +1 -1
  266. package/dist/lib/agent-browser/client.js +13 -0
  267. package/dist/lib/browser/client.d.ts +35 -1
  268. package/dist/lib/browser/client.d.ts.map +1 -1
  269. package/dist/lib/browser/client.js +117 -8
  270. package/dist/lib/browser/identity.d.ts +25 -0
  271. package/dist/lib/browser/identity.d.ts.map +1 -0
  272. package/dist/lib/browser/identity.js +66 -0
  273. package/dist/lib/browser/index.d.ts +2 -1
  274. package/dist/lib/browser/index.d.ts.map +1 -1
  275. package/dist/lib/browser/index.js +2 -1
  276. package/dist/lib/browser/launch-args.d.ts +26 -0
  277. package/dist/lib/browser/launch-args.d.ts.map +1 -1
  278. package/dist/lib/browser/launch-args.js +41 -1
  279. package/dist/lib/browser/user-agent.d.ts +53 -0
  280. package/dist/lib/browser/user-agent.d.ts.map +1 -0
  281. package/dist/lib/browser/user-agent.js +79 -0
  282. package/dist/lib/docs-metadata-managed.d.ts +1 -0
  283. package/dist/lib/docs-metadata-managed.d.ts.map +1 -1
  284. package/dist/lib/docs-metadata-managed.js +17 -1
  285. package/dist/lib/identities/assume.d.ts.map +1 -1
  286. package/dist/lib/identities/assume.js +4 -9
  287. package/dist/lib/instructions/apply.d.ts.map +1 -1
  288. package/dist/lib/instructions/apply.js +2 -0
  289. package/dist/lib/pace/index.d.ts +2 -0
  290. package/dist/lib/pace/index.d.ts.map +1 -0
  291. package/dist/lib/pace/index.js +1 -0
  292. package/dist/lib/pace/pace.d.ts +95 -0
  293. package/dist/lib/pace/pace.d.ts.map +1 -0
  294. package/dist/lib/pace/pace.js +313 -0
  295. package/dist/lib/tunnel/forward-headers.d.ts +11 -0
  296. package/dist/lib/tunnel/forward-headers.d.ts.map +1 -0
  297. package/dist/lib/tunnel/forward-headers.js +17 -0
  298. package/dist/lib/tunnel/gate.js +15 -10
  299. package/dist/lib/webview/index.d.ts +2 -0
  300. package/dist/lib/webview/index.d.ts.map +1 -0
  301. package/dist/lib/webview/index.js +1 -0
  302. package/dist/lib/webview/webview.d.ts +116 -0
  303. package/dist/lib/webview/webview.d.ts.map +1 -0
  304. package/dist/lib/webview/webview.js +289 -0
  305. package/opencode-plugin/package.json +19 -0
  306. package/opencode-plugin/src/index.ts +791 -0
  307. package/package.json +11 -4
  308. package/schemas/config.schema.json +13 -0
  309. package/schemas/event-v3.schema.json +164 -0
  310. package/src/commander.ts +7 -0
  311. package/src/commands/agents.ts +33 -18
  312. package/src/commands/browse-ai.ts +8 -0
  313. package/src/commands/browse.ts +189 -10
  314. package/src/commands/checkpoint.ts +7 -9
  315. package/src/commands/deinit.ts +9 -4
  316. package/src/commands/docs.ts +2 -0
  317. package/src/commands/doctor.ts +11 -2
  318. package/src/commands/fetch.ts +14 -0
  319. package/src/commands/init.ts +281 -119
  320. package/src/commands/instructions.ts +8 -4
  321. package/src/commands/ledger-v3.ts +2 -1
  322. package/src/commands/webview.ts +159 -0
  323. package/src/core/adapter.ts +102 -1
  324. package/src/core/adapters/attestation.ts +3 -2
  325. package/src/core/adapters/profiles.ts +42 -1
  326. package/src/core/adapters/registry.ts +37 -0
  327. package/src/core/agents/canonical-emit.ts +2 -5
  328. package/src/core/agents/cli.ts +18 -17
  329. package/src/core/agents/coord-client.ts +9 -3
  330. package/src/core/agents/rules/claim-conflict.ts +3 -8
  331. package/src/core/agents/rules/commit-conflict.ts +2 -7
  332. package/src/core/agents/rules/stop-hook.ts +3 -1
  333. package/src/core/agents/session-events.ts +120 -26
  334. package/src/core/agents/session-finalization-state-v3.ts +2 -1
  335. package/src/core/agents/session-finalizer-v3.ts +5 -4
  336. package/src/core/agents/session-name-display.ts +48 -0
  337. package/src/core/agents/state/heartbeat-reader.ts +7 -0
  338. package/src/core/agents/state/heartbeat-writer.ts +24 -2
  339. package/src/core/agents/state/live-coordination-view.ts +1 -0
  340. package/src/core/agents/state/live-coordination-writer.ts +3 -0
  341. package/src/core/agents/state/stale-sweep.ts +2 -9
  342. package/src/core/agents/turn-changes.ts +97 -0
  343. package/src/core/artifacts/delivery-card.ts +2 -1
  344. package/src/core/artifacts/index.ts +4 -3
  345. package/src/core/config.ts +23 -0
  346. package/src/core/context/index.ts +2 -1
  347. package/src/core/conversations/archive.ts +4 -3
  348. package/src/core/diagnostics/bundle.ts +3 -2
  349. package/src/core/events/legacy-storage/canary.ts +5 -4
  350. package/src/core/events/legacy-storage/compression.ts +2 -1
  351. package/src/core/events/v3/adapter-id.ts +29 -1
  352. package/src/core/events/v3/authority-outbox.ts +2 -1
  353. package/src/core/events/v3/bootstrap.ts +5 -4
  354. package/src/core/events/v3/capabilities.ts +17 -0
  355. package/src/core/events/v3/contract.ts +9 -1
  356. package/src/core/events/v3/control-witness.ts +3 -2
  357. package/src/core/events/v3/coordination-transaction-recovery.ts +4 -3
  358. package/src/core/events/v3/fingerprint-keys.ts +7 -6
  359. package/src/core/events/v3/generated.ts +1 -1
  360. package/src/core/events/v3/live-feed.ts +5 -4
  361. package/src/core/events/v3/producers/command-recorder.ts +8 -6
  362. package/src/core/events/v3/producers/coordination-recorder.ts +8 -7
  363. package/src/core/events/v3/producers/diagnostic-summaries.ts +5 -4
  364. package/src/core/events/v3/producers/hook-base.ts +11 -4
  365. package/src/core/events/v3/producers/hook.ts +19 -1
  366. package/src/core/events/v3/producers/intake.ts +18 -6
  367. package/src/core/events/v3/producers/recorder.ts +123 -114
  368. package/src/core/events/v3/recovery.ts +4 -3
  369. package/src/core/events/v3/support-storage/inventory.ts +2 -1
  370. package/src/core/events/v3/support-storage/maintenance.ts +8 -3
  371. package/src/core/events/v3/support-storage/pack-reader.ts +4 -3
  372. package/src/core/events/v3/support-storage/pack-writer.ts +6 -5
  373. package/src/core/events/v3/tool-line-changes.ts +139 -0
  374. package/src/core/events/v3/writer.ts +8 -7
  375. package/src/core/governor/planning.ts +6 -5
  376. package/src/core/governor/service.ts +12 -11
  377. package/src/core/governor/state.ts +7 -6
  378. package/src/core/guard/evaluator.ts +1 -0
  379. package/src/core/guard/storage.ts +3 -2
  380. package/src/core/hooks/adapter/behaviors/claude-code.ts +33 -0
  381. package/src/core/hooks/adapter/behaviors/codex.ts +96 -0
  382. package/src/core/hooks/adapter/behaviors/cursor.ts +37 -0
  383. package/src/core/hooks/adapter/behaviors/index.ts +31 -0
  384. package/src/core/hooks/adapter/behaviors/openclaw.ts +36 -0
  385. package/src/core/hooks/adapter/behaviors/opencode.ts +36 -0
  386. package/src/core/hooks/adapter/behaviors/types.ts +110 -0
  387. package/src/core/hooks/adapter/detect.ts +2 -3
  388. package/src/core/hooks/adapter/events.ts +53 -1
  389. package/src/core/hooks/adapter/opencode-plugin.ts +177 -0
  390. package/src/core/hooks/adapter/output.ts +8 -1
  391. package/src/core/hooks/adapter/wiring.ts +85 -11
  392. package/src/core/hooks/cli.ts +733 -537
  393. package/src/core/hooks/effects/image-capture.ts +2 -1
  394. package/src/core/hooks/prompt-context/contract.ts +1 -0
  395. package/src/core/hooks/resolve/transcript.ts +42 -5
  396. package/src/core/hooks/session-name-presence.ts +10 -3
  397. package/src/core/hooks/shell-waiter.ts +166 -0
  398. package/src/core/resources/service.ts +5 -4
  399. package/src/core/semantic/review.ts +5 -4
  400. package/src/core/semantic/service.ts +15 -14
  401. package/src/core/semantic/storage.ts +5 -4
  402. package/src/core/storage/atomic-json.ts +7 -3
  403. package/src/core/storage/durable-history.ts +6 -5
  404. package/src/core/storage/maintenance-providers.ts +2 -1
  405. package/src/core/storage/maintenance.ts +7 -6
  406. package/src/core/storage/metrics.ts +5 -1
  407. package/src/core/storage/modes.ts +53 -0
  408. package/src/core/storage/process-log.ts +3 -2
  409. package/src/core/storage/receipts.ts +4 -3
  410. package/src/core/storage/segments.ts +7 -6
  411. package/src/core/supervisor/hook-health.ts +4 -4
  412. package/src/core/supervisor/service.ts +7 -6
  413. package/src/core/work/state.ts +8 -7
  414. package/src/core/workflow/adapters.ts +15 -0
  415. package/src/core/workflow/approvals.ts +6 -5
  416. package/src/core/workflow/billing.ts +34 -4
  417. package/src/core/workflow/durable-record.ts +4 -3
  418. package/src/core/workflow/live-session-v3.ts +2 -3
  419. package/src/core/workflow/run-state.ts +3 -2
  420. package/src/core/workflow/spawn-opencode.ts +150 -0
  421. package/src/core/workflow/transcript.ts +3 -2
  422. package/src/core/workflow/workspaces/leases.ts +5 -4
  423. package/src/core/workflow/workspaces/local-git.ts +3 -2
  424. package/src/core/workflow/workspaces/paths.ts +2 -1
  425. package/src/core/workflow/workspaces/state.ts +10 -9
  426. package/src/lazy-commands.ts +11 -1
  427. package/src/lib/agent-browser/client.ts +18 -0
  428. package/src/lib/browser/client.ts +147 -9
  429. package/src/lib/browser/identity.ts +64 -0
  430. package/src/lib/browser/index.ts +15 -1
  431. package/src/lib/browser/launch-args.ts +53 -1
  432. package/src/lib/browser/user-agent.ts +130 -0
  433. package/src/lib/docs-metadata-managed.ts +21 -0
  434. package/src/lib/identities/assume.ts +7 -7
  435. package/src/lib/instructions/apply.ts +2 -0
  436. package/src/lib/pace/index.ts +14 -0
  437. package/src/lib/pace/pace.ts +370 -0
  438. package/src/lib/tunnel/forward-headers.ts +16 -0
  439. package/src/lib/tunnel/gate.ts +18 -10
  440. package/src/lib/webview/index.ts +13 -0
  441. package/src/lib/webview/webview.ts +457 -0
@@ -7,9 +7,13 @@
7
7
  * null and every hook no-ops forever.
8
8
  * 2. Register the agent-hook entries in the adapter settings file.
9
9
  *
10
- * Wires whichever adapter `--adapter` names (Claude Code `.claude/settings.json`,
11
- * Cursor `.cursor/hooks.json`, or Codex `.codex/hooks.json`): the per-adapter
12
- * file path, event list, and hook-entry shape all come from ADAPTER_SPECS.
10
+ * Wires every adapter in the resolved set (Claude Code `.claude/settings.json`,
11
+ * Cursor `.cursor/hooks.json`, Codex `.codex/hooks.json`, or the OpenCode plugin
12
+ * directory `.opencode/plugins/harnery/`): the per-adapter file path, event list,
13
+ * and hook-entry shape all come from ADAPTER_SPECS. `--adapter` is repeatable (or
14
+ * comma-separated, or `all`); with no flag, init wires every adapter whose CLI is
15
+ * installed on this machine, plus any adapter the project already has wired, else
16
+ * claude-code.
13
17
  *
14
18
  * `harn init` does both, non-destructively: it merges hook entries into an
15
19
  * existing settings file (preserving any other hooks) and skips entries that are
@@ -34,8 +38,14 @@ import {
34
38
  sha256V3,
35
39
  } from "../core/events/v3/index.ts";
36
40
  import { ADAPTER_SPECS, type AdapterId, type AdapterSpec } from "../core/hooks/adapter/events.ts";
41
+ import {
42
+ checkOpenCodePlugin,
43
+ installOpenCodePlugin,
44
+ } from "../core/hooks/adapter/opencode-plugin.ts";
37
45
  import {
38
46
  agentHookPathForProject,
47
+ applyCursorStopLoopLimit,
48
+ CURSOR_STOP_FOLLOWUP_LOOP_LIMIT,
39
49
  commandWiresSubcommand,
40
50
  diffWiring,
41
51
  groupCommands,
@@ -44,7 +54,9 @@ import {
44
54
  isAgentHookCommand,
45
55
  makeEntry,
46
56
  type SettingsFile,
57
+ summarizeAdapterWiring,
47
58
  } from "../core/hooks/adapter/wiring.ts";
59
+ import { detectInstalledAdapters } from "../core/workflow/adapters.ts";
48
60
  import { applyIndexerExclusions } from "../lib/indexer-exclusions.ts";
49
61
  import {
50
62
  type ApplyResult,
@@ -58,13 +70,140 @@ import { HostAddendumError } from "../lib/instructions/host-addendum.ts";
58
70
  const HARNERY_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
59
71
 
60
72
  interface InitOpts {
61
- adapter: string;
73
+ adapter: string[];
62
74
  dryRun?: boolean;
63
75
  check?: boolean;
64
76
  instructionsOnly?: boolean;
65
77
  projectRoot?: string;
66
78
  }
67
79
 
80
+ const ADAPTER_IDS = Object.keys(ADAPTER_SPECS) as AdapterId[];
81
+
82
+ /** Parse explicit `--adapter` values; `all` expands and order is preserved, deduplicated. */
83
+ function parseAdapterIds(requested: string[]): { adapters: AdapterId[]; error?: string } {
84
+ const adapters: AdapterId[] = [];
85
+ for (const raw of requested) {
86
+ const ids = raw === "all" ? ADAPTER_IDS : [raw as AdapterId];
87
+ for (const id of ids) {
88
+ if (!(id in ADAPTER_SPECS)) {
89
+ return {
90
+ adapters: [],
91
+ error: `Unknown adapter '${raw}'. Expected: ${ADAPTER_IDS.join(" | ")} | all.`,
92
+ };
93
+ }
94
+ if (!adapters.includes(id)) adapters.push(id);
95
+ }
96
+ }
97
+ return { adapters };
98
+ }
99
+
100
+ /**
101
+ * Which adapters `init` should wire. An explicit `--adapter` wins. Without one:
102
+ * every adapter whose CLI is installed on this machine, plus every adapter the
103
+ * project already has wired (committed wiring stays current on a box whose CLI is
104
+ * absent, e.g. CI). A machine with neither falls back to the historical
105
+ * claude-code default. `installed` is injectable so tests never key on the host's
106
+ * toolchain.
107
+ */
108
+ export function resolveInitAdapters(
109
+ projectRoot: string,
110
+ requested: string[],
111
+ installed?: AdapterId[],
112
+ ): { adapters: AdapterId[]; error?: string } {
113
+ if (requested.length > 0) return parseAdapterIds(requested);
114
+ const detected = installed ?? detectInstalledAdapters();
115
+ const wired = summarizeAdapterWiring(projectRoot).wired;
116
+ const adapters = ADAPTER_IDS.filter((id) => detected.includes(id) || wired.includes(id));
117
+ return { adapters: adapters.length > 0 ? adapters : ["claude-code"] };
118
+ }
119
+
120
+ /** Drift check for one adapter's runtime wiring (settings hooks or the OpenCode plugin). */
121
+ function checkAdapterHooks(
122
+ projectRoot: string,
123
+ adapter: AdapterId,
124
+ bin: string,
125
+ ): { drift: boolean; error: boolean; issues: string[] } {
126
+ const spec = ADAPTER_SPECS[adapter];
127
+ const agentHook = agentHookPathForProject(projectRoot, HARNERY_ROOT);
128
+ if (spec.installMode === "opencode-plugin") {
129
+ // No hooks map to diff: the plugin directory is the wiring.
130
+ const plugin = checkOpenCodePlugin(projectRoot, { binName: bin, agentHook });
131
+ if (plugin.status === "missing") {
132
+ return {
133
+ drift: true,
134
+ error: false,
135
+ issues: [`${spec.settingsFile}: missing (re-run \`init --adapter opencode\`)`],
136
+ };
137
+ }
138
+ if (plugin.status === "foreign") return { drift: false, error: true, issues: plugin.issues };
139
+ if (plugin.status === "stale") return { drift: true, error: false, issues: plugin.issues };
140
+ return { drift: false, error: false, issues: [] };
141
+ }
142
+ const settingsPath = resolve(projectRoot, spec.settingsFile);
143
+ if (!existsSync(settingsPath)) {
144
+ return { drift: true, error: false, issues: [`${spec.settingsFile}: missing (re-run init)`] };
145
+ }
146
+ const issues: string[] = [];
147
+ let drift = false;
148
+ try {
149
+ const settings = JSON.parse(readFileSync(settingsPath, "utf8")) as SettingsFile;
150
+ const hookDiff = diffWiring(settings, spec, { agentHookPath: agentHook, adapter });
151
+ drift =
152
+ hookDiff.missing.length > 0 ||
153
+ hookDiff.stale.length > 0 ||
154
+ hookDiff.duplicates.length > 0 ||
155
+ hookDiff.misplaced.length > 0 ||
156
+ hookDiff.orphans.length > 0 ||
157
+ hookDiff.invalidTopLevelKeys.length > 0 ||
158
+ hookDiff.invalidEventKeys.length > 0;
159
+ if (hookDiff.missing.length > 0) {
160
+ issues.push(
161
+ `${spec.settingsFile}: missing hooks (${hookDiff.missing.map((e) => e.settingsKey).join(", ")})`,
162
+ );
163
+ }
164
+ if (hookDiff.stale.length > 0) {
165
+ issues.push(
166
+ `${spec.settingsFile}: stale hook commands (${hookDiff.stale.map((e) => e.settingsKey).join(", ")})`,
167
+ );
168
+ }
169
+ if (hookDiff.duplicates.length > 0) {
170
+ issues.push(
171
+ `${spec.settingsFile}: duplicate hooks (${hookDiff.duplicates.map((e) => e.settingsKey).join(", ")})`,
172
+ );
173
+ }
174
+ if (hookDiff.misplaced.length > 0) {
175
+ issues.push(
176
+ `${spec.settingsFile}: hooks under the wrong event (${hookDiff.misplaced.map((e) => e.subcommand).join(", ")})`,
177
+ );
178
+ }
179
+ if (hookDiff.orphans.length > 0) {
180
+ issues.push(`${spec.settingsFile}: obsolete hooks (${hookDiff.orphans.join(", ")})`);
181
+ }
182
+ if (hookDiff.invalidTopLevelKeys.length > 0) {
183
+ issues.push(
184
+ `${spec.settingsFile}: invalid fields (${hookDiff.invalidTopLevelKeys.join(", ")})`,
185
+ );
186
+ }
187
+ if (hookDiff.invalidEventKeys.length > 0) {
188
+ issues.push(
189
+ `${spec.settingsFile}: unsupported events (${hookDiff.invalidEventKeys.join(", ")})`,
190
+ );
191
+ }
192
+ } catch (error) {
193
+ return {
194
+ drift: false,
195
+ error: true,
196
+ issues: [`${spec.settingsFile}: invalid JSON (${(error as Error).message})`],
197
+ };
198
+ }
199
+ return { drift, error: false, issues };
200
+ }
201
+
202
+ /** Preserve first-seen order while dropping repeats (one pass per adapter repeats lines). */
203
+ function dedupe(values: string[]): string[] {
204
+ return [...new Set(values)];
205
+ }
206
+
68
207
  export function registerInitCommand(program: Command, emit: EmitContext, binName?: string): void {
69
208
  program
70
209
  .command("init")
@@ -74,7 +213,19 @@ export function registerInitCommand(program: Command, emit: EmitContext, binName
74
213
  "(idempotent; safe to re-run). Use --dry-run to preview, --check to " +
75
214
  "report drift without writing (exit 0 fresh / 2 drift / 1 error).",
76
215
  )
77
- .option("--adapter <id>", "claude-code | cursor | codex", "claude-code")
216
+ .option(
217
+ "--adapter <id>",
218
+ "claude-code | cursor | codex | opencode | all; repeatable or comma-separated. " +
219
+ "Default: every adapter installed on this machine plus every adapter already wired, else claude-code",
220
+ (value: string, previous: string[]) =>
221
+ previous.concat(
222
+ value
223
+ .split(",")
224
+ .map((id) => id.trim())
225
+ .filter(Boolean),
226
+ ),
227
+ [] as string[],
228
+ )
78
229
  .option("--dry-run", "Show what would change without writing")
79
230
  .option("--check", "Report managed-surface drift without writing; exit 0/2/1")
80
231
  .option(
@@ -83,14 +234,6 @@ export function registerInitCommand(program: Command, emit: EmitContext, binName
83
234
  )
84
235
  .option("--project-root <path>", "Project root (default: git toplevel, else cwd)")
85
236
  .action((opts: InitOpts) => {
86
- const adapter = opts.adapter as AdapterId;
87
- const spec = ADAPTER_SPECS[adapter];
88
- if (!spec) {
89
- emit.text(`Unknown adapter '${opts.adapter}'. Expected: claude-code | cursor | codex.`);
90
- emit.setExitCode(1);
91
- return;
92
- }
93
-
94
237
  const projectRoot = resolve(opts.projectRoot ?? gitTopLevel() ?? process.cwd());
95
238
  // A binName already pinned in this project's config.jsonc beats the
96
239
  // invoking CLI's name: the pin is a committed, deliberate declaration;
@@ -105,6 +248,15 @@ export function registerInitCommand(program: Command, emit: EmitContext, binName
105
248
  // before every coord-root, config, ledger, adapter-settings, and Git-hook
106
249
  // operation so its no-runtime-writes contract is structural.
107
250
  if (opts.instructionsOnly === true) {
251
+ // Narrow by design: host sync tooling depends on one deterministic
252
+ // guidance layout, so this mode stays single-adapter and never detects.
253
+ const parsed = parseAdapterIds(opts.adapter);
254
+ if (parsed.error) {
255
+ emit.text(parsed.error);
256
+ emit.setExitCode(1);
257
+ return;
258
+ }
259
+ const adapter = parsed.adapters[0] ?? "claude-code";
108
260
  if (opts.check === true) {
109
261
  const { status, issues } = checkInstructions(projectRoot, { binName: bin, adapter });
110
262
  const head =
@@ -137,65 +289,31 @@ export function registerInitCommand(program: Command, emit: EmitContext, binName
137
289
  return;
138
290
  }
139
291
 
292
+ const resolved = resolveInitAdapters(projectRoot, opts.adapter);
293
+ if (resolved.error) {
294
+ emit.text(resolved.error);
295
+ emit.setExitCode(1);
296
+ return;
297
+ }
298
+ const adapters = resolved.adapters;
299
+
140
300
  // ── --check: read-only drift report on every init-managed surface ─────
141
301
  if (opts.check === true) {
142
- const { status, issues } = checkInstructions(projectRoot, { binName: bin, adapter });
302
+ const issues: string[] = [];
303
+ let instructionStatus: "fresh" | "drift" | "error" = "fresh";
143
304
  let hookCheckError = false;
144
305
  let hookDrift = false;
145
- const settingsPath = resolve(projectRoot, spec.settingsFile);
146
- const agentHook = agentHookPathForProject(projectRoot, HARNERY_ROOT);
147
- if (!existsSync(settingsPath)) {
148
- hookDrift = true;
149
- issues.push(`${spec.settingsFile}: missing (re-run init)`);
150
- } else {
151
- try {
152
- const settings = JSON.parse(readFileSync(settingsPath, "utf8")) as SettingsFile;
153
- const hookDiff = diffWiring(settings, spec, { agentHookPath: agentHook, adapter });
154
- hookDrift =
155
- hookDiff.missing.length > 0 ||
156
- hookDiff.stale.length > 0 ||
157
- hookDiff.duplicates.length > 0 ||
158
- hookDiff.misplaced.length > 0 ||
159
- hookDiff.orphans.length > 0 ||
160
- hookDiff.invalidTopLevelKeys.length > 0 ||
161
- hookDiff.invalidEventKeys.length > 0;
162
- if (hookDiff.missing.length > 0) {
163
- issues.push(
164
- `${spec.settingsFile}: missing hooks (${hookDiff.missing.map((e) => e.settingsKey).join(", ")})`,
165
- );
166
- }
167
- if (hookDiff.stale.length > 0) {
168
- issues.push(
169
- `${spec.settingsFile}: stale hook commands (${hookDiff.stale.map((e) => e.settingsKey).join(", ")})`,
170
- );
171
- }
172
- if (hookDiff.duplicates.length > 0) {
173
- issues.push(
174
- `${spec.settingsFile}: duplicate hooks (${hookDiff.duplicates.map((e) => e.settingsKey).join(", ")})`,
175
- );
176
- }
177
- if (hookDiff.misplaced.length > 0) {
178
- issues.push(
179
- `${spec.settingsFile}: hooks under the wrong event (${hookDiff.misplaced.map((e) => e.subcommand).join(", ")})`,
180
- );
181
- }
182
- if (hookDiff.orphans.length > 0) {
183
- issues.push(`${spec.settingsFile}: obsolete hooks (${hookDiff.orphans.join(", ")})`);
184
- }
185
- if (hookDiff.invalidTopLevelKeys.length > 0) {
186
- issues.push(
187
- `${spec.settingsFile}: invalid fields (${hookDiff.invalidTopLevelKeys.join(", ")})`,
188
- );
189
- }
190
- if (hookDiff.invalidEventKeys.length > 0) {
191
- issues.push(
192
- `${spec.settingsFile}: unsupported events (${hookDiff.invalidEventKeys.join(", ")})`,
193
- );
194
- }
195
- } catch (error) {
196
- hookCheckError = true;
197
- issues.push(`${spec.settingsFile}: invalid JSON (${(error as Error).message})`);
306
+ for (const adapter of adapters) {
307
+ const instructions = checkInstructions(projectRoot, { binName: bin, adapter });
308
+ if (instructions.status === "error") instructionStatus = "error";
309
+ else if (instructions.status === "drift" && instructionStatus !== "error") {
310
+ instructionStatus = "drift";
198
311
  }
312
+ issues.push(...instructions.issues);
313
+ const hooks = checkAdapterHooks(projectRoot, adapter, bin);
314
+ hookCheckError = hookCheckError || hooks.error;
315
+ hookDrift = hookDrift || hooks.drift;
316
+ issues.push(...hooks.issues);
199
317
  }
200
318
  const gitHooks = checkGitHooks(projectRoot);
201
319
  if (gitHooks.status !== "fresh") issues.push(...gitHooks.issues);
@@ -206,21 +324,26 @@ export function registerInitCommand(program: Command, emit: EmitContext, binName
206
324
  issues.push(`event ledger V3 is runtime-stale (${ledgerRuntimeIssues.join(", ")})`);
207
325
  }
208
326
  const merged =
209
- status === "error" || hookCheckError
327
+ instructionStatus === "error" || hookCheckError
210
328
  ? "error"
211
329
  : hookDrift ||
330
+ instructionStatus === "drift" ||
212
331
  gitHooks.status !== "fresh" ||
213
332
  ledger.state !== "active" ||
214
333
  ledgerRuntimeIssues.length > 0
215
334
  ? "drift"
216
- : status;
335
+ : "fresh";
217
336
  const head =
218
337
  merged === "fresh"
219
338
  ? "harn init --check: hooks + instructions + skills + event ledger V3 are current"
220
339
  : merged === "drift"
221
340
  ? "harn init --check: drift found (re-run `init` to refresh)"
222
341
  : "harn init --check: error";
223
- const lines = issues.length ? `\n${issues.map((i) => ` ✗ ${i}`).join("\n")}` : "";
342
+ const lines = issues.length
343
+ ? `\n${dedupe(issues)
344
+ .map((i) => ` ✗ ${i}`)
345
+ .join("\n")}`
346
+ : "";
224
347
  emit.text(`${head}${lines}`);
225
348
  emit.setExitCode(merged === "fresh" ? 0 : merged === "drift" ? 2 : 1);
226
349
  return;
@@ -305,60 +428,88 @@ export function registerInitCommand(program: Command, emit: EmitContext, binName
305
428
  }
306
429
 
307
430
  // ── 2. adapter hooks ───────────────────────────────────────────────────
308
- const settingsPath = resolve(projectRoot, spec.settingsFile);
309
- const agentHook = agentHookPathForProject(projectRoot, HARNERY_ROOT);
431
+ for (const adapter of adapters) {
432
+ const spec = ADAPTER_SPECS[adapter];
433
+ const settingsPath = resolve(projectRoot, spec.settingsFile);
434
+ const agentHook = agentHookPathForProject(projectRoot, HARNERY_ROOT);
310
435
 
311
- let settings: SettingsFile;
312
- if (existsSync(settingsPath)) {
313
- try {
314
- settings = JSON.parse(readFileSync(settingsPath, "utf8")) as SettingsFile;
315
- } catch (err) {
316
- emit.text(
317
- `✗ ${rel(projectRoot, settingsPath)} exists but isn't valid JSON; refusing to ` +
318
- `overwrite. Fix it and re-run.\n (${(err as Error).message})`,
436
+ if (spec.installMode === "opencode-plugin") {
437
+ // OpenCode has no hooks map. Install the plugin directory instead; the
438
+ // instructions + skills step below still runs (AGENTS.md and
439
+ // .agents/skills are read natively, so no shim or mirror is needed).
440
+ const plugin = installOpenCodePlugin(projectRoot, { binName: bin, agentHook, dryRun });
441
+ if (plugin.error) {
442
+ emit.text(plugin.error);
443
+ emit.setExitCode(1);
444
+ return;
445
+ }
446
+ actions.push(...plugin.actions);
447
+ } else {
448
+ let settings: SettingsFile;
449
+ if (existsSync(settingsPath)) {
450
+ try {
451
+ settings = JSON.parse(readFileSync(settingsPath, "utf8")) as SettingsFile;
452
+ } catch (err) {
453
+ emit.text(
454
+ `✗ ${rel(projectRoot, settingsPath)} exists but isn't valid JSON; refusing to ` +
455
+ `overwrite. Fix it and re-run.\n (${(err as Error).message})`,
456
+ );
457
+ emit.setExitCode(1);
458
+ return;
459
+ }
460
+ } else {
461
+ settings = {};
462
+ }
463
+ const { wired, already, removed, upgraded } = wireHooks(
464
+ settings,
465
+ spec,
466
+ agentHook,
467
+ adapter,
319
468
  );
320
- emit.setExitCode(1);
321
- return;
322
- }
323
- } else {
324
- settings = {};
325
- }
326
- const { wired, already, removed, upgraded } = wireHooks(settings, spec, agentHook, adapter);
327
469
 
328
- if (wired === 0 && removed === 0 && upgraded === 0) {
329
- actions.push(
330
- `· all ${spec.events.length} ${adapter} hooks already wired in ${rel(projectRoot, settingsPath)}`,
331
- );
332
- } else if (dryRun) {
333
- actions.push(
334
- `+ would wire ${wired} hook(s), upgrade ${upgraded} stale command(s), and remove ` +
335
- `${removed} obsolete/duplicate command(s) in ${rel(projectRoot, settingsPath)} (${already} already present)`,
336
- );
337
- } else {
338
- mkdirSync(dirname(settingsPath), { recursive: true });
339
- writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
340
- actions.push(
341
- `+ wired ${wired} hook(s), upgraded ${upgraded} stale command(s), and removed ` +
342
- `${removed} obsolete/duplicate command(s) in ${rel(projectRoot, settingsPath)} (${already} already present)`,
343
- );
470
+ if (wired === 0 && removed === 0 && upgraded === 0) {
471
+ actions.push(
472
+ `· all ${spec.events.length} ${adapter} hooks already wired in ${rel(projectRoot, settingsPath)}`,
473
+ );
474
+ } else if (dryRun) {
475
+ actions.push(
476
+ `+ would wire ${wired} hook(s), upgrade ${upgraded} stale command(s), and remove ` +
477
+ `${removed} obsolete/duplicate command(s) in ${rel(projectRoot, settingsPath)} (${already} already present)`,
478
+ );
479
+ } else {
480
+ mkdirSync(dirname(settingsPath), { recursive: true });
481
+ writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
482
+ actions.push(
483
+ `+ wired ${wired} hook(s), upgraded ${upgraded} stale command(s), and removed ` +
484
+ `${removed} obsolete/duplicate command(s) in ${rel(projectRoot, settingsPath)} (${already} already present)`,
485
+ );
486
+ }
487
+ const authorizationReview = codexHookReviewAction(adapter);
488
+ if (authorizationReview) actions.push(authorizationReview);
489
+ }
344
490
  }
345
- const authorizationReview = codexHookReviewAction(adapter);
346
- if (authorizationReview) actions.push(authorizationReview);
347
491
 
348
492
  // ── 3. agent-facing instructions block + skills ────────────────────────
349
- // A misconfigured host addendum aborts here rather than half-writing: the
350
- // apply step validates the configured source before it touches a file, so
351
- // a bad path leaves the repo exactly as it found it.
352
- let applied: ApplyResult;
353
- try {
354
- applied = applyInstructions(projectRoot, { binName: bin, adapter, dryRun });
355
- } catch (err) {
356
- if (!(err instanceof HostAddendumError)) throw err;
357
- emit.text(`${bin} init: ${err.message}`);
358
- emit.setExitCode(1);
359
- return;
493
+ // Idempotent per adapter: the AGENTS.md block is adapter-independent, the
494
+ // harn-* skills land in each adapter's directory, and the CLAUDE.md shim and
495
+ // Cursor rule exist only for their own adapter. A misconfigured host addendum
496
+ // aborts here rather than half-writing: the apply step validates the
497
+ // configured source before it touches a file, so a bad path leaves the repo
498
+ // exactly as it found it.
499
+ const warnings: string[] = [];
500
+ for (const adapter of adapters) {
501
+ let applied: ApplyResult;
502
+ try {
503
+ applied = applyInstructions(projectRoot, { binName: bin, adapter, dryRun });
504
+ } catch (err) {
505
+ if (!(err instanceof HostAddendumError)) throw err;
506
+ emit.text(`${bin} init: ${err.message}`);
507
+ emit.setExitCode(1);
508
+ return;
509
+ }
510
+ actions.push(...applied.actions);
511
+ warnings.push(...applied.warnings);
360
512
  }
361
- actions.push(...applied.actions);
362
513
 
363
514
  // ── 4. git-hook managed regions ────────────────────────────────────────
364
515
  // Same lifecycle contract as the instructions block: the coordination
@@ -367,7 +518,9 @@ export function registerInitCommand(program: Command, emit: EmitContext, binName
367
518
  const gitHooks = applyGitHooks(projectRoot, { dryRun });
368
519
  actions.push(...gitHooks.actions);
369
520
 
370
- emit.text(render(projectRoot, dryRun, actions, [...applied.warnings, ...gitHooks.warnings]));
521
+ emit.text(
522
+ render(projectRoot, dryRun, dedupe(actions), dedupe([...warnings, ...gitHooks.warnings])),
523
+ );
371
524
  });
372
525
  }
373
526
 
@@ -476,7 +629,15 @@ export function wireHooks(
476
629
  continue;
477
630
  }
478
631
  const current = settings.hooks[settingsKey] ?? [];
479
- current.push(makeEntry(spec.entryShape, command));
632
+ current.push(
633
+ makeEntry(
634
+ spec.entryShape,
635
+ command,
636
+ spec.entryShape === "cursor" && subcommand === "stop"
637
+ ? { loop_limit: CURSOR_STOP_FOLLOWUP_LOOP_LIMIT }
638
+ : undefined,
639
+ ),
640
+ );
480
641
  settings.hooks[settingsKey] = current;
481
642
  wired++;
482
643
  }
@@ -510,6 +671,7 @@ function normalizeEventGroup(
510
671
  group.command = canonical;
511
672
  upgraded++;
512
673
  }
674
+ if (applyCursorStopLoopLimit(group, subcommand)) upgraded++;
513
675
  }
514
676
  }
515
677
  if ("hooks" in group && Array.isArray(group.hooks)) {
@@ -27,7 +27,7 @@ export function registerInstructionsCommand(program: Command, emit: EmitContext)
27
27
  command
28
28
  .command("manifest [paths...]")
29
29
  .description("Emit a machine-readable bundle manifest for one or more working paths")
30
- .option("--adapter <adapter>", "claude-code, cursor, codex, or all", "all")
30
+ .option("--adapter <adapter>", "claude-code, cursor, codex, opencode, or all", "all")
31
31
  .option("--format <type>", "Output format: json or table", "json")
32
32
  .option("-o, --output <file>", "Write the complete JSON manifest to a file")
33
33
  .action((paths: string[], options: ManifestOptions) => {
@@ -71,9 +71,13 @@ export function registerInstructionsCommand(program: Command, emit: EmitContext)
71
71
  }
72
72
 
73
73
  function parseAdapters(value: string): Adapter[] {
74
- if (value === "all") return ["claude-code", "cursor", "codex"];
75
- if (value === "claude-code" || value === "cursor" || value === "codex") return [value];
76
- throw new Error(`unsupported adapter '${value}' (expected claude-code, cursor, codex, or all)`);
74
+ if (value === "all") return ["claude-code", "cursor", "codex", "opencode"];
75
+ if (value === "claude-code" || value === "cursor" || value === "codex" || value === "opencode") {
76
+ return [value];
77
+ }
78
+ throw new Error(
79
+ `unsupported adapter '${value}' (expected claude-code, cursor, codex, opencode, or all)`,
80
+ );
77
81
  }
78
82
 
79
83
  function enrichBundle(bundle: ReturnType<typeof buildInstructionBundle>, coordRoot: string) {
@@ -33,6 +33,7 @@ import {
33
33
  verifyEventV3SupportTransactionShadow,
34
34
  writeEventV3SupportTransactionShadow,
35
35
  } from "../core/events/v3/support-storage/index.ts";
36
+ import { stateDirMode } from "../core/storage/modes.ts";
36
37
 
37
38
  type SupportEvidence = Record<
38
39
  string,
@@ -272,7 +273,7 @@ export function registerLedgerV3Command(
272
273
  }
273
274
  const root = resolve(options.root ?? coordRoot(context));
274
275
  const transactions = transactionRoot(root);
275
- mkdirSync(transactions, { recursive: true, mode: 0o700 });
276
+ mkdirSync(transactions, { recursive: true, mode: stateDirMode() });
276
277
  emit.data(
277
278
  await planEventV3SupportTransaction({
278
279
  transaction_root: transactions,