@zerwiz/ymir 0.1.3 → 0.1.5

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 (850) hide show
  1. package/.agents/README.md +291 -0
  2. package/.agents/agents/bragi-marketer.md +111 -0
  3. package/.agents/agents/brokk.md +352 -0
  4. package/.agents/agents/forseti-reviewer.md +93 -0
  5. package/.agents/agents/frigg-consent.md +77 -0
  6. package/.agents/agents/groa-update.md +77 -0
  7. package/.agents/agents/hnoss-designer.md +69 -0
  8. package/.agents/agents/huginn-researcher.md +99 -0
  9. package/.agents/agents/jord-projects.md +78 -0
  10. package/.agents/agents/kvasir-scout.md +91 -0
  11. package/.agents/agents/mimir-planner.md +92 -0
  12. package/.agents/agents/muninn-stow.md +77 -0
  13. package/.agents/agents/saga-bearings.md +78 -0
  14. package/.agents/agents/sindri-developer.md +114 -0
  15. package/.agents/agents/snotra-documenter.md +105 -0
  16. package/.agents/agents/syn-recovery.md +78 -0
  17. package/.agents/agents/tyr-check.md +78 -0
  18. package/.agents/agents/urdh-hold.md +77 -0
  19. package/.agents/agents/volundr-smith.md +80 -0
  20. package/.agents/agents/vor-diagnostics.md +77 -0
  21. package/.agents/assets/agents/naming.md +50 -0
  22. package/.agents/assets/agents/registry.md +93 -0
  23. package/.agents/assets/agents/runtime.md +36 -0
  24. package/.agents/assets/agents/toon-tasks-cli.md +42 -0
  25. package/.agents/assets/templates/company_entity.template.md +37 -0
  26. package/.agents/backend/backends/cmux.sh +649 -0
  27. package/.agents/backend/backends/herdr-eventwait.py +157 -0
  28. package/.agents/backend/backends/herdr-workspace-move.py +114 -0
  29. package/.agents/backend/backends/herdr.sh +3386 -0
  30. package/.agents/backend/backends/orca.sh +290 -0
  31. package/.agents/backend/backends/tmux.sh +357 -0
  32. package/.agents/backend/backends/zellij.sh +700 -0
  33. package/.agents/backend/fm-afk-launch.sh +654 -0
  34. package/.agents/backend/fm-afk-return.sh +241 -0
  35. package/.agents/backend/fm-afk-start.sh +172 -0
  36. package/.agents/backend/fm-arm-command-policy.mjs +960 -0
  37. package/.agents/backend/fm-arm-pretool-check.sh +200 -0
  38. package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
  39. package/.agents/backend/fm-backend.sh +981 -0
  40. package/.agents/backend/fm-backlog-handoff.sh +909 -0
  41. package/.agents/backend/fm-backlog-receive.sh +187 -0
  42. package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
  43. package/.agents/backend/fm-bearings-board.sh +204 -0
  44. package/.agents/backend/fm-bearings-snapshot.sh +546 -0
  45. package/.agents/backend/fm-bootstrap.sh +1568 -0
  46. package/.agents/backend/fm-branch-outcome.sh +203 -0
  47. package/.agents/backend/fm-branch-prompt.sh +101 -0
  48. package/.agents/backend/fm-brief.sh +452 -0
  49. package/.agents/backend/fm-busy-event.sh +234 -0
  50. package/.agents/backend/fm-busy-lib.sh +986 -0
  51. package/.agents/backend/fm-captain-hold.sh +1001 -0
  52. package/.agents/backend/fm-cd-command-policy.mjs +153 -0
  53. package/.agents/backend/fm-cd-pretool-check.sh +190 -0
  54. package/.agents/backend/fm-check-lib.sh +74 -0
  55. package/.agents/backend/fm-check-register.sh +42 -0
  56. package/.agents/backend/fm-check-unregister.sh +52 -0
  57. package/.agents/backend/fm-classify-lib.sh +1770 -0
  58. package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
  59. package/.agents/backend/fm-composer-lib.sh +1416 -0
  60. package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
  61. package/.agents/backend/fm-config-push.sh +254 -0
  62. package/.agents/backend/fm-control-lib.sh +251 -0
  63. package/.agents/backend/fm-control.sh +878 -0
  64. package/.agents/backend/fm-crew-state.sh +625 -0
  65. package/.agents/backend/fm-cursor-lib.sh +243 -0
  66. package/.agents/backend/fm-decision-hold.sh +232 -0
  67. package/.agents/backend/fm-doc-audience-check.sh +269 -0
  68. package/.agents/backend/fm-dod-lib.sh +67 -0
  69. package/.agents/backend/fm-ensure-agents-md.sh +253 -0
  70. package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
  71. package/.agents/backend/fm-extension.mjs +2577 -0
  72. package/.agents/backend/fm-extension.sh +16 -0
  73. package/.agents/backend/fm-ff-lib.sh +420 -0
  74. package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
  75. package/.agents/backend/fm-fleet-sync.sh +458 -0
  76. package/.agents/backend/fm-fleet-view.sh +96 -0
  77. package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
  78. package/.agents/backend/fm-guard.sh +242 -0
  79. package/.agents/backend/fm-harness.sh +195 -0
  80. package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
  81. package/.agents/backend/fm-herdr-lab.sh +346 -0
  82. package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
  83. package/.agents/backend/fm-home-seed.sh +984 -0
  84. package/.agents/backend/fm-home-summary-refresh.sh +255 -0
  85. package/.agents/backend/fm-hook-host-lib.sh +36 -0
  86. package/.agents/backend/fm-inactive-reconcile.sh +531 -0
  87. package/.agents/backend/fm-inbox.sh +399 -0
  88. package/.agents/backend/fm-install-actionlint.sh +84 -0
  89. package/.agents/backend/fm-install-herdr.sh +96 -0
  90. package/.agents/backend/fm-install-shellcheck.sh +84 -0
  91. package/.agents/backend/fm-install-treehouse.sh +95 -0
  92. package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
  93. package/.agents/backend/fm-lease-lib.sh +218 -0
  94. package/.agents/backend/fm-lease.sh +189 -0
  95. package/.agents/backend/fm-line-cap-lib.sh +51 -0
  96. package/.agents/backend/fm-lint-workflows.sh +137 -0
  97. package/.agents/backend/fm-lint.sh +591 -0
  98. package/.agents/backend/fm-lock-lib.sh +104 -0
  99. package/.agents/backend/fm-lock.sh +107 -0
  100. package/.agents/backend/fm-marker-lib.sh +12 -0
  101. package/.agents/backend/fm-merge-local.sh +74 -0
  102. package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
  103. package/.agents/backend/fm-nm-run-lib.sh +124 -0
  104. package/.agents/backend/fm-on.sh +125 -0
  105. package/.agents/backend/fm-operational-input.sh +253 -0
  106. package/.agents/backend/fm-peek.sh +46 -0
  107. package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
  108. package/.agents/backend/fm-pr-check.sh +135 -0
  109. package/.agents/backend/fm-pr-lib.sh +1016 -0
  110. package/.agents/backend/fm-pr-merge.sh +707 -0
  111. package/.agents/backend/fm-pr-poll.sh +110 -0
  112. package/.agents/backend/fm-primary-scope-lib.sh +33 -0
  113. package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
  114. package/.agents/backend/fm-procevent-lavish.sh +628 -0
  115. package/.agents/backend/fm-procevent-lib.sh +892 -0
  116. package/.agents/backend/fm-procevent-quota.sh +290 -0
  117. package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
  118. package/.agents/backend/fm-procevent-when.sh +504 -0
  119. package/.agents/backend/fm-procevent.sh +1466 -0
  120. package/.agents/backend/fm-project-mode.sh +92 -0
  121. package/.agents/backend/fm-project-origin-lib.sh +180 -0
  122. package/.agents/backend/fm-promote.sh +282 -0
  123. package/.agents/backend/fm-public-followup-emit.sh +260 -0
  124. package/.agents/backend/fm-public-followup-lib.sh +398 -0
  125. package/.agents/backend/fm-public-followup.sh +1330 -0
  126. package/.agents/backend/fm-push-transition-lib.sh +168 -0
  127. package/.agents/backend/fm-quota-axi-lib.sh +93 -0
  128. package/.agents/backend/fm-quota-choose.sh +384 -0
  129. package/.agents/backend/fm-remote-delta-read.sh +189 -0
  130. package/.agents/backend/fm-remote-doctor.sh +800 -0
  131. package/.agents/backend/fm-remote-entrypoint.sh +193 -0
  132. package/.agents/backend/fm-remote-file.sh +234 -0
  133. package/.agents/backend/fm-remote-home-provision.sh +259 -0
  134. package/.agents/backend/fm-remote-home-seed.sh +259 -0
  135. package/.agents/backend/fm-remote-inherit-push.sh +90 -0
  136. package/.agents/backend/fm-remote-inherit.sh +179 -0
  137. package/.agents/backend/fm-remote-job-lib.sh +1241 -0
  138. package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
  139. package/.agents/backend/fm-remote-job-worker.sh +1124 -0
  140. package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
  141. package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
  142. package/.agents/backend/fm-review-diff.sh +158 -0
  143. package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
  144. package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
  145. package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
  146. package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
  147. package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
  148. package/.agents/backend/fm-secondmate-report.sh +88 -0
  149. package/.agents/backend/fm-send.sh +1059 -0
  150. package/.agents/backend/fm-session-lock-lib.sh +176 -0
  151. package/.agents/backend/fm-session-start.sh +970 -0
  152. package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
  153. package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
  154. package/.agents/backend/fm-sessionstart-run.sh +145 -0
  155. package/.agents/backend/fm-spawn.sh +3153 -0
  156. package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
  157. package/.agents/backend/fm-startup-memory-budget.sh +94 -0
  158. package/.agents/backend/fm-startup-network.sh +642 -0
  159. package/.agents/backend/fm-stow-cascade.sh +251 -0
  160. package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
  161. package/.agents/backend/fm-supervise-daemon.sh +1732 -0
  162. package/.agents/backend/fm-supervision-instructions.sh +215 -0
  163. package/.agents/backend/fm-supervision-lib.sh +90 -0
  164. package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
  165. package/.agents/backend/fm-tangle-lib.sh +53 -0
  166. package/.agents/backend/fm-task-inbox-lib.sh +402 -0
  167. package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
  168. package/.agents/backend/fm-teardown.sh +2922 -0
  169. package/.agents/backend/fm-test-isolation-proof.sh +588 -0
  170. package/.agents/backend/fm-test-run.sh +2191 -0
  171. package/.agents/backend/fm-timeout-lib.sh +141 -0
  172. package/.agents/backend/fm-timing-lib.sh +179 -0
  173. package/.agents/backend/fm-tmux-lib.sh +291 -0
  174. package/.agents/backend/fm-tool-update-check.sh +898 -0
  175. package/.agents/backend/fm-trace-context-lib.sh +227 -0
  176. package/.agents/backend/fm-transition-lib.sh +103 -0
  177. package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
  178. package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
  179. package/.agents/backend/fm-turnend-guard.sh +432 -0
  180. package/.agents/backend/fm-update.sh +110 -0
  181. package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
  182. package/.agents/backend/fm-voice-client.py +1373 -0
  183. package/.agents/backend/fm-voice-relay.py +1256 -0
  184. package/.agents/backend/fm-wake-drain.sh +592 -0
  185. package/.agents/backend/fm-wake-grant.sh +127 -0
  186. package/.agents/backend/fm-wake-lib.sh +1849 -0
  187. package/.agents/backend/fm-watch-arm.sh +602 -0
  188. package/.agents/backend/fm-watch-checkpoint.sh +109 -0
  189. package/.agents/backend/fm-watch.sh +1962 -0
  190. package/.agents/backend/fm-x-dismiss.sh +116 -0
  191. package/.agents/backend/fm-x-followup.sh +286 -0
  192. package/.agents/backend/fm-x-lib.sh +1000 -0
  193. package/.agents/backend/fm-x-link.sh +234 -0
  194. package/.agents/backend/fm-x-poll.sh +185 -0
  195. package/.agents/backend/fm-x-reply.sh +385 -0
  196. package/.agents/backend/fm_voice_frame.py +166 -0
  197. package/.agents/backend/fm_voice_records.py +574 -0
  198. package/.agents/backend/model-bridge.py +290 -0
  199. package/.agents/backend/opencode-go-bridge.py +30 -0
  200. package/.agents/bus/README.md +12 -0
  201. package/.agents/bus/messages.json +1 -0
  202. package/.agents/config/agents.machine.example.yaml +16 -0
  203. package/.agents/config/agents.yaml +50 -0
  204. package/.agents/config/agents.yaml.example +72 -0
  205. package/.agents/config/crew-dispatch.json +22 -0
  206. package/.agents/config/cron.yaml +9 -0
  207. package/.agents/config/eindri-dispatch.json +67 -0
  208. package/.agents/config/eindri-harness +1 -0
  209. package/.agents/config/startup-memory-budget +1 -0
  210. package/.agents/config/tailscale-sync.yaml +27 -0
  211. package/.agents/filebrowser/.filebrowser.json +6 -0
  212. package/.agents/filebrowser/README.md +11 -0
  213. package/.agents/gateway/README.md +12 -0
  214. package/.agents/github/README.md +15 -0
  215. package/.agents/github/webhooks/.gitkeep +0 -0
  216. package/.agents/github/workflows/.gitkeep +0 -0
  217. package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
  218. package/.agents/harness/opencode/plugins/package.json +4 -0
  219. package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
  220. package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
  221. package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
  222. package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
  223. package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
  224. package/.agents/memory/README.md +46 -0
  225. package/.agents/memory/kaia.engram +0 -0
  226. package/.agents/memory/kaia.engram-shm +0 -0
  227. package/.agents/memory/kaia.engram-wal +0 -0
  228. package/.agents/memory/runes_audit.md +5 -0
  229. package/.agents/memory/well/episodes.jsonl +367 -0
  230. package/.agents/memory/well/workspace.jsonl +27 -0
  231. package/.agents/migrations/0001-hodd-layout.sh +38 -0
  232. package/.agents/migrations/0002-a2a-mcp.sh +27 -0
  233. package/.agents/migrations/0003-private-data-separation.sh +111 -0
  234. package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
  235. package/.agents/sandbox/Dockerfile.utgard +19 -0
  236. package/.agents/sandbox/README.md +14 -0
  237. package/.agents/sandbox/utgard.config.json +14 -0
  238. package/.agents/skills/README.md +59 -0
  239. package/.agents/skills/bragi-marketing/README.md +3 -0
  240. package/.agents/skills/bragi-marketing/SKILL.md +82 -0
  241. package/.agents/skills/eindri-homes/README.md +3 -0
  242. package/.agents/skills/eindri-homes/SKILL.md +291 -0
  243. package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
  244. package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
  245. package/.agents/skills/frigg-consent/README.md +3 -0
  246. package/.agents/skills/frigg-consent/SKILL.md +56 -0
  247. package/.agents/skills/galdr-ymirsystem/README.md +3 -0
  248. package/.agents/skills/galdr-ymirsystem/SKILL.md +158 -0
  249. package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
  250. package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
  251. package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
  252. package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
  253. package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
  254. package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
  255. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +673 -0
  256. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
  257. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
  258. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
  259. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
  260. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
  261. package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +496 -0
  262. package/.agents/skills/galdr-ymirsystem/assets/installation.md +571 -0
  263. package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
  264. package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
  265. package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +161 -0
  266. package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +377 -0
  267. package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
  268. package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +129 -0
  269. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
  270. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
  271. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +96 -0
  272. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
  273. package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
  274. package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
  275. package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
  276. package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
  277. package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
  278. package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
  279. package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
  280. package/.agents/skills/galdr-ymirsystem/assets/smidja.md +175 -0
  281. package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
  282. package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
  283. package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
  284. package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +360 -0
  285. package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
  286. package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
  287. package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
  288. package/.agents/skills/gjallarhorn-relay/README.md +3 -0
  289. package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
  290. package/.agents/skills/groa-update/SKILL.md +70 -0
  291. package/.agents/skills/hamr-adapters/README.md +3 -0
  292. package/.agents/skills/hamr-adapters/SKILL.md +123 -0
  293. package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
  294. package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
  295. package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
  296. package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
  297. package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
  298. package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
  299. package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
  300. package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
  301. package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
  302. package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
  303. package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
  304. package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
  305. package/.agents/skills/herdr-panes/README.md +3 -0
  306. package/.agents/skills/herdr-panes/SKILL.md +92 -0
  307. package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
  308. package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
  309. package/.agents/skills/hnoss-design/DESIGN.md +19 -0
  310. package/.agents/skills/hnoss-design/README.md +3 -0
  311. package/.agents/skills/hnoss-design/SKILL.md +83 -0
  312. package/.agents/skills/hvild-afk/README.md +3 -0
  313. package/.agents/skills/hvild-afk/SKILL.md +241 -0
  314. package/.agents/skills/jord-projects/README.md +3 -0
  315. package/.agents/skills/jord-projects/SKILL.md +92 -0
  316. package/.agents/skills/lifecycle/SKILL.md +56 -0
  317. package/.agents/skills/lifecycle/smoke_test.sh +52 -0
  318. package/.agents/skills/lifecycle/start.sh +21 -0
  319. package/.agents/skills/lifecycle/status.sh +35 -0
  320. package/.agents/skills/lifecycle/stop.sh +16 -0
  321. package/.agents/skills/muninn-stow/README.md +3 -0
  322. package/.agents/skills/muninn-stow/SKILL.md +309 -0
  323. package/.agents/skills/no-mistakes/SKILL.md +379 -0
  324. package/.agents/skills/nornir-schedule/README.md +3 -0
  325. package/.agents/skills/nornir-schedule/SKILL.md +25 -0
  326. package/.agents/skills/nornir-schedule/assets/events.md +137 -0
  327. package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
  328. package/.agents/skills/nsr-compliance/README.md +3 -0
  329. package/.agents/skills/nsr-compliance/SKILL.md +29 -0
  330. package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
  331. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
  332. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
  333. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
  334. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
  335. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
  336. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
  337. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
  338. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
  339. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
  340. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
  341. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
  342. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
  343. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
  344. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
  345. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
  346. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
  347. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
  348. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
  349. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
  350. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
  351. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
  352. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
  353. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  354. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
  355. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
  356. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
  357. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
  358. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
  359. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
  360. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
  361. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
  362. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
  363. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
  364. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
  365. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
  366. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
  367. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
  368. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
  369. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
  370. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
  371. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
  372. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
  373. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
  374. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
  375. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
  376. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
  377. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
  378. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
  379. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
  380. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
  381. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
  382. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
  383. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
  384. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
  385. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
  386. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
  387. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
  388. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
  389. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
  390. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
  391. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
  392. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
  393. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
  394. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
  395. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
  396. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
  397. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
  398. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
  399. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
  400. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
  401. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
  402. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
  403. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
  404. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
  405. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
  406. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
  407. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
  408. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
  409. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
  410. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
  411. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
  412. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
  413. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
  414. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
  415. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
  416. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
  417. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
  418. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
  419. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
  420. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
  421. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
  422. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
  423. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
  424. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
  425. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
  426. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
  427. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
  428. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
  429. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
  430. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
  431. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
  432. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
  433. package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
  434. package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
  435. package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
  436. package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
  437. package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
  438. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
  439. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
  440. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
  441. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
  442. package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
  443. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
  444. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
  445. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
  446. package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
  447. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
  448. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
  449. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
  450. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
  451. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
  452. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
  453. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
  454. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  455. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
  456. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
  457. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
  458. package/.agents/skills/pr-ops/README.md +3 -0
  459. package/.agents/skills/pr-ops/SKILL.md +109 -0
  460. package/.agents/skills/ratatoskr-a2a/README.md +3 -0
  461. package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
  462. package/.agents/skills/rules-check-drift/SKILL.md +81 -0
  463. package/.agents/skills/saga-bearings/README.md +3 -0
  464. package/.agents/skills/saga-bearings/SKILL.md +26 -0
  465. package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
  466. package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
  467. package/.agents/skills/saga-bearings/assets/recap.md +56 -0
  468. package/.agents/skills/syn-recovery/README.md +3 -0
  469. package/.agents/skills/syn-recovery/SKILL.md +54 -0
  470. package/.agents/skills/tyr-check/README.md +3 -0
  471. package/.agents/skills/tyr-check/SKILL.md +218 -0
  472. package/.agents/skills/urdh-hold/README.md +3 -0
  473. package/.agents/skills/urdh-hold/SKILL.md +25 -0
  474. package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
  475. package/.agents/skills/urdh-hold/assets/hold.md +54 -0
  476. package/.agents/skills/vor-diagnostics/README.md +3 -0
  477. package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
  478. package/.agents/skills/ymir-host/README.md +3 -0
  479. package/.agents/skills/ymir-host/SKILL.md +34 -0
  480. package/.agents/skills/ymir-host/assets/desktop.md +36 -0
  481. package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
  482. package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
  483. package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
  484. package/.agents/skills/ymir-host/assets/update.md +64 -0
  485. package/.agents/tests/assets/board-render-harness.mjs +117 -0
  486. package/.agents/tests/cmux-test-safety.sh +51 -0
  487. package/.agents/tests/fixtures.sh +291 -0
  488. package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
  489. package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
  490. package/.agents/tests/fm-afk-launch.test.sh +961 -0
  491. package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
  492. package/.agents/tests/fm-afk-return.test.sh +283 -0
  493. package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
  494. package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
  495. package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
  496. package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
  497. package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
  498. package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
  499. package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
  500. package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
  501. package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
  502. package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
  503. package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
  504. package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
  505. package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
  506. package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
  507. package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
  508. package/.agents/tests/fm-backend-orca.test.sh +1357 -0
  509. package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
  510. package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
  511. package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
  512. package/.agents/tests/fm-backend.test.sh +1142 -0
  513. package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
  514. package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
  515. package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
  516. package/.agents/tests/fm-bearings-board.test.sh +414 -0
  517. package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
  518. package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
  519. package/.agents/tests/fm-bootstrap.test.sh +1178 -0
  520. package/.agents/tests/fm-branch-supervision.test.sh +552 -0
  521. package/.agents/tests/fm-brief.test.sh +785 -0
  522. package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
  523. package/.agents/tests/fm-busy-state.test.sh +465 -0
  524. package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
  525. package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
  526. package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
  527. package/.agents/tests/fm-check-unregister.test.sh +198 -0
  528. package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
  529. package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
  530. package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
  531. package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
  532. package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
  533. package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
  534. package/.agents/tests/fm-composer-ghost.test.sh +716 -0
  535. package/.agents/tests/fm-composer-lib.test.sh +666 -0
  536. package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
  537. package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
  538. package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
  539. package/.agents/tests/fm-control.test.sh +910 -0
  540. package/.agents/tests/fm-crew-state.test.sh +1610 -0
  541. package/.agents/tests/fm-cursor-harness.test.sh +404 -0
  542. package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
  543. package/.agents/tests/fm-cursor-primary.test.sh +708 -0
  544. package/.agents/tests/fm-daemon.test.sh +2740 -0
  545. package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
  546. package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
  547. package/.agents/tests/fm-extension-binding.test.sh +2187 -0
  548. package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
  549. package/.agents/tests/fm-fleet-sync.test.sh +721 -0
  550. package/.agents/tests/fm-gate-refuse.test.sh +356 -0
  551. package/.agents/tests/fm-gitignore-config.test.sh +89 -0
  552. package/.agents/tests/fm-gotmp.test.sh +230 -0
  553. package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
  554. package/.agents/tests/fm-grok-harness.test.sh +117 -0
  555. package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
  556. package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
  557. package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
  558. package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
  559. package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
  560. package/.agents/tests/fm-herdr-lab.test.sh +243 -0
  561. package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
  562. package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
  563. package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
  564. package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
  565. package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
  566. package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
  567. package/.agents/tests/fm-kimi-harness.test.sh +687 -0
  568. package/.agents/tests/fm-lint-workflows.test.sh +532 -0
  569. package/.agents/tests/fm-lint.test.sh +1023 -0
  570. package/.agents/tests/fm-muse-harness.test.sh +932 -0
  571. package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
  572. package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
  573. package/.agents/tests/fm-on.test.sh +507 -0
  574. package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
  575. package/.agents/tests/fm-operational-input.test.sh +160 -0
  576. package/.agents/tests/fm-peek-remote.test.sh +110 -0
  577. package/.agents/tests/fm-pending-reply.test.sh +1288 -0
  578. package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
  579. package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
  580. package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
  581. package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
  582. package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
  583. package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
  584. package/.agents/tests/fm-pr-merge.test.sh +2135 -0
  585. package/.agents/tests/fm-procevent-quota.test.sh +225 -0
  586. package/.agents/tests/fm-procevent-when.test.sh +406 -0
  587. package/.agents/tests/fm-procevent.test.sh +1745 -0
  588. package/.agents/tests/fm-project-origin.test.sh +110 -0
  589. package/.agents/tests/fm-public-followup.test.sh +2272 -0
  590. package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
  591. package/.agents/tests/fm-quota-choose.test.sh +626 -0
  592. package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
  593. package/.agents/tests/fm-remote-doctor.test.sh +625 -0
  594. package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
  595. package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
  596. package/.agents/tests/fm-remote-job.test.sh +768 -0
  597. package/.agents/tests/fm-remote-reply.test.sh +520 -0
  598. package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
  599. package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
  600. package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
  601. package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
  602. package/.agents/tests/fm-review-diff.test.sh +176 -0
  603. package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
  604. package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
  605. package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
  606. package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
  607. package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
  608. package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
  609. package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
  610. package/.agents/tests/fm-send-inbox.test.sh +352 -0
  611. package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
  612. package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
  613. package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
  614. package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
  615. package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
  616. package/.agents/tests/fm-send-settle.test.sh +147 -0
  617. package/.agents/tests/fm-send-strict.test.sh +241 -0
  618. package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
  619. package/.agents/tests/fm-session-start.test.sh +2515 -0
  620. package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
  621. package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
  622. package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
  623. package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
  624. package/.agents/tests/fm-spawn-batch.test.sh +148 -0
  625. package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
  626. package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
  627. package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
  628. package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
  629. package/.agents/tests/fm-startup-network.test.sh +712 -0
  630. package/.agents/tests/fm-stow-cascade.test.sh +370 -0
  631. package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
  632. package/.agents/tests/fm-supervision-events.test.sh +156 -0
  633. package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
  634. package/.agents/tests/fm-tangle-guard.test.sh +277 -0
  635. package/.agents/tests/fm-task-delivery.test.sh +420 -0
  636. package/.agents/tests/fm-task-inbox.test.sh +513 -0
  637. package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
  638. package/.agents/tests/fm-teardown.test.sh +2665 -0
  639. package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
  640. package/.agents/tests/fm-test-fixtures.test.sh +132 -0
  641. package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
  642. package/.agents/tests/fm-test-run.test.sh +1196 -0
  643. package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
  644. package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
  645. package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
  646. package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
  647. package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
  648. package/.agents/tests/fm-transition-lib.test.sh +54 -0
  649. package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
  650. package/.agents/tests/fm-update.test.sh +304 -0
  651. package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
  652. package/.agents/tests/fm-voice-relay.test.sh +4741 -0
  653. package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
  654. package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
  655. package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
  656. package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
  657. package/.agents/tests/fm-wake-queue.test.sh +1224 -0
  658. package/.agents/tests/fm-watch-arm.test.sh +821 -0
  659. package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
  660. package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
  661. package/.agents/tests/fm-watch-triage.test.sh +3889 -0
  662. package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
  663. package/.agents/tests/fm-x-mode.test.sh +3042 -0
  664. package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
  665. package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
  666. package/.agents/tests/herdr-test-safety.sh +42 -0
  667. package/.agents/tests/lib.sh +315 -0
  668. package/.agents/tests/remote-herdr-fixture.sh +125 -0
  669. package/.agents/tests/secondmate-helpers.sh +193 -0
  670. package/.agents/tests/smoke.test.sh +93 -0
  671. package/.agents/tests/wake-helpers.sh +338 -0
  672. package/.agents/tests/zellij-test-safety.sh +60 -0
  673. package/.agents/tools/README.md +10 -0
  674. package/.agents/tools/bin/tasks-cli +267 -0
  675. package/.agents/tools/tasks-cli.ts +216 -0
  676. package/AGENTS.md +454 -0
  677. package/CHANGELOG.md +881 -0
  678. package/LICENSE +1 -1
  679. package/NOTICE +63 -0
  680. package/README.md +113 -39
  681. package/RULES/01-domains.md +51 -0
  682. package/RULES/02-agents.md +78 -0
  683. package/RULES/03-houses.md +61 -0
  684. package/RULES/04-hoard.md +65 -0
  685. package/RULES/05-platforms.md +67 -0
  686. package/RULES/06-append-only.md +58 -0
  687. package/RULES/07-config.md +45 -0
  688. package/RULES/08-delivery-gate.md +75 -0
  689. package/RULES/README.md +20 -0
  690. package/bin/README.md +107 -0
  691. package/bin/a2a-mcp.sh +98 -0
  692. package/bin/a2a-serve.py +103 -0
  693. package/bin/a2a-serve.sh +8 -0
  694. package/bin/a2a-talk.sh +50 -0
  695. package/bin/a2abridge-ensure.sh +108 -0
  696. package/bin/agent-run.sh +43 -0
  697. package/bin/agents-config.sh +233 -0
  698. package/bin/apodex-smoke-test.sh +92 -0
  699. package/bin/bifrost-bridge.sh +112 -0
  700. package/bin/bifrost-ingress.sh +77 -0
  701. package/bin/bootstrap-macos.sh +107 -0
  702. package/bin/bootstrap-windows.ps1 +112 -0
  703. package/bin/branch-guard.sh +64 -0
  704. package/bin/brokk +73 -0
  705. package/bin/brokk-classify-lib.sh +1770 -0
  706. package/bin/brokk-lease-lib.sh +222 -0
  707. package/bin/brokk-lease.sh +193 -0
  708. package/bin/brokk-lint.sh +65 -0
  709. package/bin/brokk-send.sh +38 -0
  710. package/bin/brokk-update.sh +9 -0
  711. package/bin/brokk-wake-grant.sh +127 -0
  712. package/bin/brokk-wake-lib.sh +1849 -0
  713. package/bin/changelog-guard.sh +165 -0
  714. package/bin/crash-sense.sh +160 -0
  715. package/bin/desktop-place.sh +224 -0
  716. package/bin/docs-guard.sh +39 -0
  717. package/bin/editor-place.sh +207 -0
  718. package/bin/eindri-acclaim.sh +34 -0
  719. package/bin/eindri-control.sh +28 -0
  720. package/bin/eindri-role.sh +83 -0
  721. package/bin/eindri-seen.sh +41 -0
  722. package/bin/eindri-send.sh +15 -0
  723. package/bin/eindri-start.sh +85 -0
  724. package/bin/eindri-watch.sh +57 -0
  725. package/bin/einherjar-spawn.sh +576 -0
  726. package/bin/eir-doctor.sh +112 -0
  727. package/bin/erindi-brief.sh +271 -0
  728. package/bin/fleet-apply.sh +68 -0
  729. package/bin/github-deploy.sh +81 -0
  730. package/bin/gjallarhorn-expose.sh +102 -0
  731. package/bin/gjallarhorn-notify.sh +58 -0
  732. package/bin/gjallarhorn-purge.sh +54 -0
  733. package/bin/gjallarhorn-tunnel.sh +72 -0
  734. package/bin/gleipnir-lock-lib.sh +284 -0
  735. package/bin/groa-update.sh +122 -0
  736. package/bin/hall-snapshot.sh +92 -0
  737. package/bin/hamr-harness.sh +246 -0
  738. package/bin/heimdall-ssh-keys.sh +374 -0
  739. package/bin/herdr-agents.py +44 -0
  740. package/bin/herdr-ensure.sh +104 -0
  741. package/bin/herdr-run.sh +431 -0
  742. package/bin/hermes-ensure.sh +88 -0
  743. package/bin/hoard-lib.sh +25 -0
  744. package/bin/hodd.sh +78 -0
  745. package/bin/host-sense.sh +199 -0
  746. package/bin/huginn-research-worker.sh +108 -0
  747. package/bin/local-model-lock.sh +51 -0
  748. package/bin/mimir-bridge.py +226 -0
  749. package/bin/mimir-bridge.sh +87 -0
  750. package/bin/mimir-ingest.sh +131 -0
  751. package/bin/mimir.sh +113 -0
  752. package/bin/mjollnir-webhook.sh +59 -0
  753. package/bin/mjollnir.sh +85 -0
  754. package/bin/model-hardware.sh +82 -0
  755. package/bin/model-resolve.sh +147 -0
  756. package/bin/models-detect.sh +178 -0
  757. package/bin/nornir-cron-start.sh +162 -0
  758. package/bin/nornir-job-daily-briefing.sh +191 -0
  759. package/bin/nornir-job-git-sync.sh +152 -0
  760. package/bin/nornir-job-memory-housekeeping.sh +114 -0
  761. package/bin/nornir-job-observer.sh +166 -0
  762. package/bin/nornir-job-skillopt-sleep.sh +76 -0
  763. package/bin/omarchy-hook-install.sh +60 -0
  764. package/bin/omarchy-install.sh +148 -0
  765. package/bin/omarchy-plugins.sh +174 -0
  766. package/bin/omarchy-sense.sh +149 -0
  767. package/bin/perm-guard.sh +30 -0
  768. package/bin/pi-agent.sh +68 -0
  769. package/bin/pi-ensure.sh +77 -0
  770. package/bin/pi-local.sh +37 -0
  771. package/bin/pi-seat.sh +63 -0
  772. package/bin/prereq-ensure.sh +166 -0
  773. package/bin/private-guard.sh +38 -0
  774. package/bin/project-git.sh +55 -0
  775. package/bin/public-guard.sh +70 -0
  776. package/bin/ratatoskr.sh +49 -0
  777. package/bin/realm-lib.sh +32 -0
  778. package/bin/repo-scrub.sh +62 -0
  779. package/bin/rodd-operational-input.sh +250 -0
  780. package/bin/runes-append.sh +198 -0
  781. package/bin/saga-session-start.sh +172 -0
  782. package/bin/saga-sessionstart-run.sh +79 -0
  783. package/bin/saga-wake-drain.sh +41 -0
  784. package/bin/secret-guard.sh +63 -0
  785. package/bin/sessrumnir-ensure.sh +100 -0
  786. package/bin/sessrumnir-sync.sh +237 -0
  787. package/bin/sessrumnir.sh +91 -0
  788. package/bin/skillopt-setup.sh +76 -0
  789. package/bin/skuld-branch-outcome.sh +203 -0
  790. package/bin/skuld-branch-prompt.sh +101 -0
  791. package/bin/smidja-bootstrap.sh +90 -0
  792. package/bin/smidja-observe.sh +86 -0
  793. package/bin/syn-arm-pretool-check.sh +57 -0
  794. package/bin/syn-asset-pretool-check.sh +76 -0
  795. package/bin/syn-cd-pretool-check.sh +28 -0
  796. package/bin/syn-guard-pretool-check.sh +83 -0
  797. package/bin/syn-turnend-guard.sh +36 -0
  798. package/bin/syn-watch-arm.sh +99 -0
  799. package/bin/tailscale-sync.sh +190 -0
  800. package/bin/telegram-bot.sh +69 -0
  801. package/bin/toolchain.sh +74 -0
  802. package/bin/utgard.sh +111 -0
  803. package/bin/valhalla.sh +115 -0
  804. package/bin/valknut-load.sh +260 -0
  805. package/bin/vor-crew-state.sh +124 -0
  806. package/bin/wedge-notify.sh +47 -0
  807. package/bin/workspace-provision.sh +80 -0
  808. package/bin/workspace-rag.sh +150 -0
  809. package/bin/wyrd-db.sh +47 -0
  810. package/bin/yggdrasil.sh +152 -0
  811. package/bin/ymir-edit.sh +145 -0
  812. package/bin/ymir-install.sh +641 -0
  813. package/bin/ymir-invite.sh +93 -0
  814. package/bin/ymir-isolation.sh +88 -0
  815. package/bin/ymir-migrate.sh +61 -0
  816. package/bin/ymir-platform.sh +348 -0
  817. package/bin/ymir-say.sh +97 -0
  818. package/bin/ymir-setup-auth.sh +108 -0
  819. package/bin/ymir-validate.sh +173 -0
  820. package/bin/ymir.js +74 -16
  821. package/docs/Architecture.md +339 -0
  822. package/docs/design.md +351 -0
  823. package/docs/installations/README.md +63 -0
  824. package/docs/installations/ymir-home.md +93 -0
  825. package/docs/integration.md +105 -0
  826. package/docs/lore.md +1056 -0
  827. package/docs/research/agent-harnesses.md +290 -0
  828. package/docs/runbooks/README.md +19 -0
  829. package/docs/runbooks/a2a.md +91 -0
  830. package/docs/runbooks/agent-permissions.md +99 -0
  831. package/docs/runbooks/agents.md +57 -0
  832. package/docs/runbooks/models.md +69 -0
  833. package/docs/runbooks/realm-onboarding.md +60 -0
  834. package/docs/runbooks/secrets-and-hoard.md +60 -0
  835. package/docs/runbooks/updates-and-migrations.md +110 -0
  836. package/docs/runbooks/worktrees-isolation.md +72 -0
  837. package/docs/session-start.md +105 -0
  838. package/docs/skillopt-integration.md +321 -0
  839. package/docs/sync-system.md +66 -0
  840. package/docs/workspaces.md +104 -0
  841. package/docs/ymir-rut.md +260 -0
  842. package/package.json +44 -14
  843. package/packaging/build.sh +74 -0
  844. package/packaging/macos/Ymir Installer.command +43 -0
  845. package/packaging/windows/ymir-setup.nsi +74 -0
  846. package/scripts/README.md +22 -0
  847. package/scripts/electron.sh +210 -0
  848. package/scripts/start.sh +167 -0
  849. package/scripts/stop.sh +113 -0
  850. package/install.sh +0 -35
@@ -0,0 +1,649 @@
1
+ #!/usr/bin/env bash
2
+ # bin/backends/cmux.sh - the cmux session-provider adapter (EXPERIMENTAL).
3
+ #
4
+ # Design: data/cmux-backend-feasibility-c7/report.md (adapter design sketch,
5
+ # section 4) plus the live-app verification pass recorded in
6
+ # docs/cmux-backend.md (real cmux 0.64.17, macOS aarch64, 2026-07-03). cmux is
7
+ # a session provider ONLY, exactly like herdr/zellij: the worktree provider
8
+ # stays treehouse. Sourced only through bin/fm-backend.sh's fm_backend_source
9
+ # in normal operation; the unit tests source it directly.
10
+ #
11
+ # Container shape: cmux has no "session" layer to multiplex the way
12
+ # tmux/herdr/zellij do - there is just "the app" (one running GUI instance).
13
+ # ONE cmux workspace PER TASK (mirrors tmux's one-window-per-task / zellij's
14
+ # one-tab-per-task), with exactly one surface inside it. cmux has no session
15
+ # layer, so workspace titles are scoped by firstmate home and installation
16
+ # path inside this adapter.
17
+ #
18
+ # Target string shape: "<workspace_uuid>:<surface_uuid>" - both bare UUIDs
19
+ # with no embedded colon, so splitting on the FIRST colon is trivially
20
+ # correct (mirrors herdr's/zellij's target-string convention).
21
+ #
22
+ # GUI-first, macOS-only (docs/cmux-backend.md "Setup"): explicit selection or
23
+ # runtime auto-detection when firstmate itself is already running inside a
24
+ # cmux-spawned terminal (primary CMUX_WORKSPACE_ID marker, with documented
25
+ # macOS fallback signals for wrapper-stripped claude). Unlike Orca, cmux is a
26
+ # pure session provider (treehouse still owns the worktree) and Escape IS
27
+ # natively supported.
28
+ #
29
+ # Empirical findings from the live verification pass (docs/cmux-backend.md has
30
+ # the full evidence log) that shaped this adapter, several of which diverge
31
+ # from the original design sketch's speculation:
32
+ #
33
+ # 1. `send` (literal) does NOT auto-submit - confirmed, matches every other
34
+ # backend's "literal-then-separate-Enter" contract.
35
+ # 2. Surface cwd is CREATION-TIME-FROZEN (zellij-shape), not live-tracking
36
+ # (herdr-shape): `workspace list`'s `current_directory` field reflects a
37
+ # `cd` run directly in the surface's own top-level shell, but stays
38
+ # frozen at wherever that shell was when it launched a foreground
39
+ # subshell (exactly what `treehouse get` does) - verified live: a nested
40
+ # `bash -c 'cd /Users && exec bash'` left `current_directory` reporting
41
+ # the PARENT shell's last cwd, never following into the subshell. Fixed
42
+ # with zellij's own pwd-marker-probe workaround, reused verbatim in
43
+ # spirit (fm_backend_cmux_current_path below).
44
+ # 3. `read-screen --lines N` has NO herdr-style small-N empty-result bug -
45
+ # verified N=1..10 all return correctly-clamped, non-empty content. The
46
+ # "fetch generous, trim locally" pattern is still used for consistency
47
+ # and because the actual viewport height (not a bug - real behavior) can
48
+ # still cap a single `read-screen` call below a caller's requested bound.
49
+ # A DIFFERENT, unanticipated read-screen pitfall surfaced only once real
50
+ # spawn-shaped call sequences were exercised (not caught by the original
51
+ # Phase 1 pass, which happened to test against surfaces that already had
52
+ # output): read-screen against a genuinely FRESH surface that has never
53
+ # been written to yet fails outright with `internal_error: Failed to
54
+ # read terminal text`, for every --lines value and no matter how long
55
+ # you wait, until at least one `send` actually writes to it - after
56
+ # which it becomes reliably readable forever. This ruled out read-screen
57
+ # as fm_backend_cmux_target_ready's liveness probe (the design sketch's
58
+ # original suggestion): the very first send on a freshly created task
59
+ # would fail its own pre-flight readiness check. `list-panes` has no such
60
+ # gap and is used instead (fm_backend_cmux_surface_exists), mirroring
61
+ # zellij's own structural pane_exists check.
62
+ # 4. Closing a workspace's LAST surface is a THIRD shape, matching neither
63
+ # herdr (auto-closes the workspace) nor zellij (leaves a ghost tab):
64
+ # `close-surface` REFUSES outright with a typed error
65
+ # (`invalid_state: Cannot close the last surface`), leaving both the
66
+ # surface and the workspace untouched. `close-workspace` removes the
67
+ # whole workspace (surface included) only when it is not the last
68
+ # workspace in its window. `fm_backend_cmux_kill` handles the documented
69
+ # last-in-window exception below, while still reclaiming every surface in
70
+ # the task workspace.
71
+ # 5. Workspace ids do NOT survive an app relaunch - verified via source
72
+ # (`Sources/Workspace.swift`'s only initializer unconditionally sets
73
+ # `self.id = UUID()`, with no restored-id parameter, unlike surfaces'
74
+ # `restoredSurfaceId ?? UUID()` path scoped to same-run object reuse).
75
+ # No live app restart of the captain's own content was performed to
76
+ # confirm this; see docs/cmux-backend.md for the reasoning. Recovery
77
+ # therefore uses scoped-title matching from the caller-facing fm-<id>
78
+ # label, never a stored uuid, mirroring herdr's/zellij's own recovery
79
+ # posture.
80
+ # 6. NO title uniqueness enforcement for workspaces OR surfaces/tabs -
81
+ # verified live (two workspaces, and two surfaces in one workspace, all
82
+ # created successfully sharing one title). The duplicate check below is
83
+ # ours, mirroring every other adapter, and uses home-scoped titles so a
84
+ # shared cmux app cannot cross-match another firstmate home's task.
85
+ #
86
+ # Unanticipated finding, load-bearing for this adapter: the control socket
87
+ # defaults to `socketControlMode=cmuxOnly`, which REJECTS any CLI process
88
+ # not spawned inside cmux itself ("Access denied - only processes started
89
+ # inside cmux can connect"). Since firstmate always drives cmux from an
90
+ # external shell, `automation.socketControlMode` must be one of the three
91
+ # externally-viable modes (docs/cmux-backend.md "Setup" owns the full
92
+ # matrix, verified from cmux source): `automation` (RECOMMENDED - same-user
93
+ # external clients, no shared secret), `password` (works, needs
94
+ # config/cmux-socket-password or CMUX_SOCKET_PASSWORD supplied on every
95
+ # invocation), or `allowAll` (works, but opens the socket to every local
96
+ # user - not recommended). `off` and `cmuxOnly` can never work externally.
97
+ # A configured password is harmless under non-password modes: cmux's own
98
+ # CLI sends `auth` preemptively and tolerates the server's "Unknown
99
+ # command 'auth'" reply (cli/cmux.swift, authenticateSocketClientIfNeeded).
100
+ #
101
+ # Requires: cmux (CLI, bundled inside cmux.app - not guaranteed to be on PATH;
102
+ # see fm_backend_cmux_bin), jq (JSON parsing). Bootstrap detects these through
103
+ # fm_backend_required_tools only when cmux is the resolved backend; this adapter
104
+ # also gates them again before spawning.
105
+
106
+ # FM_HOME fallback: every real caller already sets FM_HOME as a global before
107
+ # sourcing fm-backend.sh (which sources this file); this exists only so this
108
+ # file's own unit tests, which source it directly, resolve sanely. Mirrors
109
+ # bin/backends/zellij.sh's identical fallback.
110
+ FM_BACKEND_CMUX_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
111
+ FM_ROOT="${FM_ROOT_OVERRIDE:-${FM_ROOT:-$FM_BACKEND_CMUX_ROOT}}"
112
+ FM_HOME="${FM_HOME:-${FM_ROOT_OVERRIDE:-$FM_ROOT}}"
113
+
114
+ # shellcheck source=bin/fm-backend-hometag-lib.sh
115
+ . "$FM_BACKEND_CMUX_ROOT/bin/fm-backend-hometag-lib.sh"
116
+
117
+ # Shared composer-content classifier (empty|pending|unknown, and the fleet-wide
118
+ # dead-shell-vs-agent-composer rule). Owned by bin/fm-composer-lib.sh, reused by
119
+ # every backend so the decision cannot drift.
120
+ # shellcheck source=bin/fm-composer-lib.sh
121
+ . "$FM_BACKEND_CMUX_ROOT/bin/fm-composer-lib.sh"
122
+
123
+ # Verified minimum: the version the live pass ran against (docs/cmux-backend.md).
124
+ FM_BACKEND_CMUX_MIN_MAJOR=0
125
+ FM_BACKEND_CMUX_MIN_MINOR=64
126
+
127
+ # fm_backend_cmux_bin: resolve the cmux CLI binary. cmux does not reliably
128
+ # land on PATH after a plain app install - it ships an OPTIONAL "install CLI"
129
+ # action (`Sources/App/CmuxCLIPathInstaller.swift`, symlinking
130
+ # /usr/local/bin/cmux -> the bundled binary) that a fresh install has not
131
+ # necessarily run. Prefer PATH (respects an operator's own setup, e.g. after
132
+ # running that install action), fall back to the well-known bundle path.
133
+ FM_BACKEND_CMUX_BUNDLE_BIN="${FM_BACKEND_CMUX_BUNDLE_BIN:-/Applications/cmux.app/Contents/Resources/bin/cmux}"
134
+ fm_backend_cmux_bin() {
135
+ if command -v cmux >/dev/null 2>&1; then
136
+ printf 'cmux'
137
+ return 0
138
+ fi
139
+ if [ -x "$FM_BACKEND_CMUX_BUNDLE_BIN" ]; then
140
+ printf '%s' "$FM_BACKEND_CMUX_BUNDLE_BIN"
141
+ return 0
142
+ fi
143
+ return 1
144
+ }
145
+
146
+ fm_backend_cmux_tool_check() {
147
+ fm_backend_cmux_bin >/dev/null 2>&1 || { echo "error: backend=cmux selected but the 'cmux' CLI was not found on PATH or at $FM_BACKEND_CMUX_BUNDLE_BIN (https://cmux.com)" >&2; return 1; }
148
+ command -v jq >/dev/null 2>&1 || { echo "error: backend=cmux selected but 'jq' is not installed (required to parse cmux's JSON output)" >&2; return 1; }
149
+ return 0
150
+ }
151
+
152
+ # fm_backend_cmux_password: the optional socket password from
153
+ # config/cmux-socket-password (first non-empty line), or empty. Read fresh
154
+ # from the effective config dir on every call, mirroring the rest of backend
155
+ # config resolution.
156
+ # Never overrides an operator's own ambient CMUX_SOCKET_PASSWORD when the file
157
+ # is absent - fm_backend_cmux_cli only exports this when it resolves non-empty.
158
+ fm_backend_cmux_password() {
159
+ local config_dir="${FM_CONFIG_OVERRIDE:-$FM_HOME/config}" f line
160
+ f="$config_dir/cmux-socket-password"
161
+ [ -f "$f" ] || return 0
162
+ while IFS= read -r line || [ -n "$line" ]; do
163
+ if [ -n "$line" ]; then
164
+ printf '%s' "$line"
165
+ return 0
166
+ fi
167
+ done < "$f"
168
+ }
169
+
170
+ # fm_backend_cmux_cli: run `cmux <args...>`, quieted (suppresses legacy-alias
171
+ # notices) and with the configured socket password exported only when one is
172
+ # actually configured, so an operator's own ambient CMUX_SOCKET_PASSWORD is
173
+ # never clobbered with an empty value.
174
+ fm_backend_cmux_cli() { # <cmux-subcommand-and-args...>
175
+ local bin pw
176
+ bin=$(fm_backend_cmux_bin) || return 1
177
+ pw=$(fm_backend_cmux_password)
178
+ if [ -n "$pw" ]; then
179
+ CMUX_QUIET=1 CMUX_SOCKET_PASSWORD="$pw" "$bin" "$@"
180
+ else
181
+ CMUX_QUIET=1 "$bin" "$@"
182
+ fi
183
+ }
184
+
185
+ # fm_backend_cmux_version_check: refuse loudly on a missing/incompatible cmux
186
+ # client. `cmux version` needs no socket (verified: works even when the
187
+ # control socket is unreachable), so this is a pure client-version gate,
188
+ # separate from reachability/auth (fm_backend_cmux_ping_state below).
189
+ fm_backend_cmux_version_check() {
190
+ fm_backend_cmux_tool_check || return 1
191
+ local raw ver major rest minor
192
+ raw=$(fm_backend_cmux_cli version 2>/dev/null) || { echo "error: 'cmux version' failed; is cmux installed correctly?" >&2; return 1; }
193
+ ver=$(printf '%s' "$raw" | awk '{print $2}')
194
+ case "$ver" in
195
+ ''|*[!0-9.]*)
196
+ echo "error: could not parse a cmux version from '$raw'; refusing to use an unverified cmux build" >&2
197
+ return 1
198
+ ;;
199
+ esac
200
+ major=${ver%%.*}
201
+ rest=${ver#*.}
202
+ minor=${rest%%.*}
203
+ case "$major" in ''|*[!0-9]*) major=0 ;; esac
204
+ case "$minor" in ''|*[!0-9]*) minor=0 ;; esac
205
+ if [ "$major" -lt "$FM_BACKEND_CMUX_MIN_MAJOR" ] || { [ "$major" -eq "$FM_BACKEND_CMUX_MIN_MAJOR" ] && [ "$minor" -lt "$FM_BACKEND_CMUX_MIN_MINOR" ]; }; then
206
+ echo "error: cmux $ver is older than the verified minimum $FM_BACKEND_CMUX_MIN_MAJOR.$FM_BACKEND_CMUX_MIN_MINOR; update cmux before using backend=cmux" >&2
207
+ return 1
208
+ fi
209
+ return 0
210
+ }
211
+
212
+ # fm_backend_cmux_ping_state: classify socket reachability/auth from `cmux
213
+ # ping`'s own text, since a missing/rejected connection is a normal, expected
214
+ # outcome here (never treated as a scripting bug) - ok|denied|unauth|down|error.
215
+ # The three auth-shaped server replies (verified from cmux source,
216
+ # Sources/TerminalController.swift): "Authentication required" (password mode,
217
+ # no password presented), "Password mode is enabled but no socket password"
218
+ # (password mode, app side has no password configured), and "Invalid password"
219
+ # (password mode, wrong password presented) all classify as unauth - each is a
220
+ # password-configuration problem on one side or the other, never fixable by
221
+ # relaunching the app.
222
+ fm_backend_cmux_ping_state() {
223
+ local out
224
+ out=$(fm_backend_cmux_cli ping 2>&1)
225
+ if [ "$out" = "PONG" ]; then
226
+ printf 'ok'
227
+ return 0
228
+ fi
229
+ case "$out" in
230
+ *'only processes started inside cmux can connect'*) printf 'denied' ;;
231
+ *'Password mode is enabled but no socket password'*|*'Authentication required'*|*'Invalid password'*) printf 'unauth' ;;
232
+ *'Socket not found'*) printf 'down' ;;
233
+ *) printf 'error' ;;
234
+ esac
235
+ }
236
+
237
+ # fm_backend_cmux_refuse_denied / fm_backend_cmux_refuse_unauth: the two
238
+ # fail-fast auth refusals, factored so the pre-launch and post-launch checks
239
+ # cannot drift. Each names every externally-viable socket mode (automation
240
+ # RECOMMENDED, password, allowAll - docs/cmux-backend.md "Setup" owns the
241
+ # matrix) plus the config/backend opt-out for a caller who only landed on
242
+ # cmux via auto-detection.
243
+ fm_backend_cmux_refuse_denied() {
244
+ echo "error: backend=cmux socket rejected the connection (automation.socketControlMode is cmuxOnly, the default, which never admits an external CLI like firstmate). In cmux Settings > Automation set Socket Control Mode to 'Automation mode' (recommended - same-user external clients, no password), or 'Password mode' plus config/cmux-socket-password/CMUX_SOCKET_PASSWORD, or 'Full open access' (NOT recommended - admits every local user) - see docs/cmux-backend.md 'Setup' - or set config/backend to tmux (or pass --backend tmux) if you did not mean to use cmux." >&2
245
+ }
246
+
247
+ fm_backend_cmux_refuse_unauth() {
248
+ echo "error: backend=cmux socket requires a password (automation.socketControlMode=password) but none is configured for this caller, or the configured one was rejected. Set config/cmux-socket-password or export CMUX_SOCKET_PASSWORD to the password from cmux Settings > Automation, or switch Socket Control Mode to 'Automation mode' (recommended - no password needed) - see docs/cmux-backend.md 'Setup' - or set config/backend to tmux (or pass --backend tmux) if you did not mean to use cmux." >&2
249
+ }
250
+
251
+ # fm_backend_cmux_ensure_running: launch cmux (mirrors the CLI's own
252
+ # `connectClient`/`launchApp` `open -a cmux` fallback) only when the socket is
253
+ # simply not up yet (`down`); an auth failure (`denied`/`unauth`) is a
254
+ # configuration problem a relaunch cannot fix, so it fails fast with an
255
+ # actionable pointer to docs/cmux-backend.md instead of retry-looping. A
256
+ # launch that never becomes reachable also names the `off` mode (socket
257
+ # listener disabled entirely - no listener ever comes up, no matter how long
258
+ # the app has been running), since that is indistinguishable from a slow
259
+ # launch on the wire.
260
+ fm_backend_cmux_ensure_running() {
261
+ local state i
262
+ state=$(fm_backend_cmux_ping_state)
263
+ case "$state" in
264
+ ok) return 0 ;;
265
+ denied)
266
+ fm_backend_cmux_refuse_denied
267
+ return 1
268
+ ;;
269
+ unauth)
270
+ fm_backend_cmux_refuse_unauth
271
+ return 1
272
+ ;;
273
+ esac
274
+ open -a cmux >/dev/null 2>&1 || { echo "error: failed to launch cmux ('open -a cmux' failed)" >&2; return 1; }
275
+ for i in $(seq 1 20); do
276
+ state=$(fm_backend_cmux_ping_state)
277
+ case "$state" in
278
+ ok) return 0 ;;
279
+ denied)
280
+ fm_backend_cmux_refuse_denied
281
+ return 1
282
+ ;;
283
+ unauth)
284
+ fm_backend_cmux_refuse_unauth
285
+ return 1
286
+ ;;
287
+ esac
288
+ sleep 0.5
289
+ done
290
+ echo "error: cmux did not become reachable within 10s of launch. If the app is already running, its Socket Control Mode may be 'Off' (no control socket at all) - set it to 'Automation mode' (recommended) in Settings > Automation, see docs/cmux-backend.md 'Setup'." >&2
291
+ return 1
292
+ }
293
+
294
+ # fm_backend_cmux_container_ensure: the full spawn-time container-ensure
295
+ # sequence (version gate, reachability/launch-if-needed). No per-home
296
+ # container to stand up - cmux has no session layer (unlike herdr/zellij),
297
+ # the app itself is the only container. Nothing to echo; callers proceed
298
+ # straight to fm_backend_cmux_create_task.
299
+ fm_backend_cmux_container_ensure() {
300
+ fm_backend_cmux_version_check || return 1
301
+ fm_backend_cmux_ensure_running || return 1
302
+ return 0
303
+ }
304
+
305
+ # fm_backend_cmux_home_label: readable home prefix plus a short hash of the
306
+ # resolved FM_ROOT path. cmux has one app-global workspace namespace, so the
307
+ # path hash distinguishes every firstmate installation, including multiple
308
+ # primary homes. Moving an installation changes this tag and old cmux titles
309
+ # stop matching; task meta already records absolute worktree paths, so repo
310
+ # relocation is already outside the supported recovery contract. Derivation
311
+ # itself lives in bin/fm-backend-hometag-lib.sh, shared with zellij's
312
+ # identical shared-namespace collision fix (docs/zellij-backend.md
313
+ # "Home-scoped tab titles").
314
+ fm_backend_cmux_home_label() {
315
+ fm_backend_hometag
316
+ }
317
+
318
+ fm_backend_cmux_scoped_title() { # <fm-task-label>
319
+ local label=$1 rest home
320
+ home=$(fm_backend_cmux_home_label)
321
+ case "$label" in
322
+ fm-*) rest=${label#fm-} ;;
323
+ *) rest=$label ;;
324
+ esac
325
+ printf 'fm-%s-%s' "$home" "$rest"
326
+ }
327
+
328
+ # fm_backend_cmux_workspace_id_for_label: the live workspace id whose title
329
+ # equals <label>, or empty. cmux enforces no title uniqueness (finding #6),
330
+ # so this adopts the FIRST match `jq` returns, mirroring herdr's/zellij's own
331
+ # duplicate-check posture.
332
+ fm_backend_cmux_workspace_id_for_label() { # <label>
333
+ local label=$1
334
+ fm_backend_cmux_cli workspace list --json --id-format uuids 2>/dev/null \
335
+ | jq -r --arg want "$label" '.workspaces[]? | select(.title == $want) | .id' 2>/dev/null | head -1
336
+ }
337
+
338
+ fm_backend_cmux_surface_id_for_workspace() { # <workspace_id>
339
+ local wsid=$1
340
+ fm_backend_cmux_cli list-panes --workspace "$wsid" --json --id-format uuids 2>/dev/null \
341
+ | jq -r '.panes[0] // {} | .selected_surface_id // (.surface_ids[0] // empty)' 2>/dev/null
342
+ }
343
+
344
+ # fm_backend_cmux_create_task: create the task's workspace (one surface),
345
+ # refusing an existing live <label> (finding #6: cmux enforces no uniqueness
346
+ # itself). Resolves the fresh workspace's default surface via one list-panes
347
+ # call (finding: a freshly created workspace already has exactly one surface,
348
+ # so no separate new-surface call is needed). --focus false is passed for
349
+ # defense in depth though verified to already be the default (finding:
350
+ # workspace/surface/pane create all default focus to false) - no
351
+ # focus-restore dance is needed, unlike zellij. Echoes "<workspace_id>
352
+ # <surface_id>" on success.
353
+ fm_backend_cmux_create_task() { # <label> <cwd>
354
+ local label=$1 cwd=$2 title dup out wsid sfid
355
+ title=$(fm_backend_cmux_scoped_title "$label")
356
+ dup=$(fm_backend_cmux_workspace_id_for_label "$title")
357
+ if [ -n "$dup" ]; then
358
+ echo "error: cmux workspace '$title' already exists" >&2
359
+ return 1
360
+ fi
361
+ out=$(fm_backend_cmux_cli new-workspace --name "$title" --cwd "$cwd" --focus false --id-format uuids 2>&1) || {
362
+ echo "error: cmux new-workspace failed for '$title': $out" >&2
363
+ return 1
364
+ }
365
+ wsid=$(fm_backend_cmux_workspace_id_for_label "$title")
366
+ [ -n "$wsid" ] || { echo "error: could not resolve a cmux workspace id for '$title' after creation" >&2; return 1; }
367
+ sfid=$(fm_backend_cmux_surface_id_for_workspace "$wsid")
368
+ [ -n "$sfid" ] || { echo "error: could not resolve the default surface for cmux workspace '$title' ($wsid)" >&2; return 1; }
369
+ printf '%s %s' "$wsid" "$sfid"
370
+ }
371
+
372
+ # fm_backend_cmux_parse_target: split "<workspace_uuid>:<surface_uuid>" on the
373
+ # FIRST colon (neither UUID contains a colon, so this is unambiguous). Sets
374
+ # FM_BACKEND_CMUX_WORKSPACE and FM_BACKEND_CMUX_SURFACE for the caller.
375
+ fm_backend_cmux_parse_target() { # <target>
376
+ local target=$1
377
+ FM_BACKEND_CMUX_WORKSPACE=${target%%:*}
378
+ FM_BACKEND_CMUX_SURFACE=${target#*:}
379
+ [ -n "$FM_BACKEND_CMUX_WORKSPACE" ] && [ -n "$FM_BACKEND_CMUX_SURFACE" ] && [ "$FM_BACKEND_CMUX_SURFACE" != "$target" ]
380
+ }
381
+
382
+ # fm_backend_cmux_surface_exists: does <surface_id> currently appear as one of
383
+ # <workspace_id>'s surfaces, per list-panes? Structural existence check, never
384
+ # a content read.
385
+ #
386
+ # Verified real-cmux pitfall NOT anticipated by the design sketch: read-screen
387
+ # against a genuinely fresh surface that has never been written to yet fails
388
+ # with a typed `internal_error: Failed to read terminal text` - EVERY
389
+ # read-screen call fails this way (with or without --lines, any value,
390
+ # regardless of how long you wait) until at least one `send` has actually
391
+ # written to the surface, at which point it becomes reliably readable. This
392
+ # would make read-screen unusable as fm_backend_cmux_target_ready's liveness
393
+ # probe: the very first send_literal on a freshly created task's surface
394
+ # would fail its own readiness pre-check before ever getting to write
395
+ # anything. list-panes has no such gap (verified: correct, immediate output
396
+ # on a completely untouched fresh surface), so it is the liveness primitive
397
+ # instead - mirroring zellij's own pane_exists check
398
+ # (fm_backend_zellij_pane_exists) rather than the design sketch's original
399
+ # read-screen-based suggestion.
400
+ fm_backend_cmux_surface_exists() { # <workspace_id> <surface_id>
401
+ local wsid=$1 sfid=$2
402
+ fm_backend_cmux_cli list-panes --workspace "$wsid" --json --id-format uuids 2>/dev/null \
403
+ | jq -e --arg s "$sfid" '[.panes[]? | select(.surface_ids // [] | index($s))] | length > 0' >/dev/null 2>&1
404
+ }
405
+
406
+ # fm_backend_cmux_target_ready: parse the target and verify it is live via
407
+ # fm_backend_cmux_surface_exists (never read-screen - see that function's
408
+ # header for the fresh-surface pitfall this avoids). When the caller knows
409
+ # the owning firstmate task label, refresh stale workspace/surface ids by label.
410
+ fm_backend_cmux_target_ready() { # <target> [expected-label]
411
+ local expected_label=${2:-} expected_title title wsid sfid
412
+ fm_backend_cmux_parse_target "$1" || return 1
413
+ if [ -n "$expected_label" ]; then
414
+ expected_title=$(fm_backend_cmux_scoped_title "$expected_label")
415
+ title=$(fm_backend_cmux_cli workspace list --json --id-format uuids 2>/dev/null | jq -r --arg id "$FM_BACKEND_CMUX_WORKSPACE" '.workspaces[]? | select(.id == $id) | .title' 2>/dev/null)
416
+ if [ "$title" = "$expected_title" ]; then
417
+ fm_backend_cmux_surface_exists "$FM_BACKEND_CMUX_WORKSPACE" "$FM_BACKEND_CMUX_SURFACE" && return 0
418
+ wsid=$FM_BACKEND_CMUX_WORKSPACE
419
+ elif [ -n "$title" ]; then
420
+ return 1
421
+ else
422
+ wsid=$(fm_backend_cmux_workspace_id_for_label "$expected_title")
423
+ [ -n "$wsid" ] || return 1
424
+ fi
425
+ sfid=$(fm_backend_cmux_surface_id_for_workspace "$wsid")
426
+ [ -n "$sfid" ] || return 1
427
+ FM_BACKEND_CMUX_WORKSPACE=$wsid
428
+ FM_BACKEND_CMUX_SURFACE=$sfid
429
+ return 0
430
+ fi
431
+ fm_backend_cmux_surface_exists "$FM_BACKEND_CMUX_WORKSPACE" "$FM_BACKEND_CMUX_SURFACE"
432
+ }
433
+
434
+ # fm_backend_cmux_current_path: the live foreground process's cwd, or empty on
435
+ # any error. Mirrors fm_backend_zellij_current_path's active pwd-marker-probe
436
+ # workaround (bin/backends/zellij.sh:306-347) verbatim in spirit.
437
+ #
438
+ # Verified pitfall (finding #2 above): cmux's `current_directory` field DOES
439
+ # reflect a `cd` run directly in the surface's own top-level shell, but stays
440
+ # FROZEN at whatever directory that shell was in when it launched `treehouse
441
+ # get` as a foreground command - it never follows that command's own internal
442
+ # `cd` into the acquired worktree. cmux's control socket exposes no
443
+ # live-process cwd field either (unlike herdr's `foreground_cwd`), so passive
444
+ # polling cannot solve this here any more than it could for zellij. Active
445
+ # probe instead: print the surface's `$PWD` with a unique marker (atomically
446
+ # submitted via send_text_line), briefly settle, then capture and read only
447
+ # that marker line. Scoped to fm-spawn.sh's own worktree-discovery poll loop.
448
+ fm_backend_cmux_current_path() { # <target> [expected-label]
449
+ local target=$1 expected_label=${2:-} out line marker_begin="__FM_CMUX_CWD_BEGIN__" marker_end="__FM_CMUX_CWD_END__" in_block=0 chunk="" last=""
450
+ fm_backend_cmux_target_ready "$target" "$expected_label" || return 0
451
+ fm_backend_cmux_send_text_line "$target" "printf '%s\n' '$marker_begin'; pwd; printf '%s\n' '$marker_end'" "$expected_label" || return 0
452
+ sleep 0.3
453
+ out=$(fm_backend_cmux_capture "$target" 200 "$expected_label") || return 0
454
+ while IFS= read -r line; do
455
+ if [ "$line" = "$marker_begin" ]; then
456
+ in_block=1
457
+ chunk=""
458
+ continue
459
+ fi
460
+ if [ "$line" = "$marker_end" ]; then
461
+ case "$chunk" in /*) last=$chunk ;; esac
462
+ in_block=0
463
+ continue
464
+ fi
465
+ [ "$in_block" -eq 1 ] && chunk="$chunk$line"
466
+ done <<EOF
467
+ $out
468
+ EOF
469
+ printf '%s' "$last"
470
+ }
471
+
472
+ # fm_backend_cmux_send_literal: send TEXT as literal, UNSUBMITTED input - the
473
+ # caller sends Enter separately. Verified live (finding #1): `send` does NOT
474
+ # auto-submit, matching every other backend's contract exactly.
475
+ fm_backend_cmux_send_literal() { # <target> <text> [expected-label]
476
+ fm_backend_cmux_target_ready "$1" "${3:-}" || return 1
477
+ fm_backend_cmux_cli send --workspace "$FM_BACKEND_CMUX_WORKSPACE" --surface "$FM_BACKEND_CMUX_SURFACE" -- "$2" >/dev/null 2>&1
478
+ }
479
+
480
+ # fm_backend_cmux_normalize_key: map firstmate's key vocabulary (Enter,
481
+ # Escape, C-c) onto cmux's `send-key` names. Verified empirically: enter,
482
+ # escape, and ctrl-c all work directly (lowercase, hyphenated). cmux's own
483
+ # key vocabulary is genuinely richer (ctrl-d/ctrl-z/ctrl-\\, semantic aliases
484
+ # sigint/sigtstp/sigquit - `TerminalSurface+Input.swift`), but firstmate's
485
+ # shared vocabulary across backends only needs these three today.
486
+ fm_backend_cmux_normalize_key() { # <key>
487
+ case "$1" in
488
+ Enter|enter) printf 'enter' ;;
489
+ Escape|escape|Esc|esc) printf 'escape' ;;
490
+ C-c|c-c|ctrl+c|Ctrl+c|Ctrl+C|ctrl-c) printf 'ctrl-c' ;;
491
+ # C-u clears a composer line. fm-send.sh's muse interrupt path needs it to
492
+ # drop the prompt muse restores into the composer after Escape.
493
+ C-u|c-u|ctrl+u|Ctrl+u|Ctrl+U|ctrl-u) printf 'ctrl-u' ;;
494
+ *) printf '%s' "$1" ;;
495
+ esac
496
+ }
497
+
498
+ # fm_backend_cmux_send_key: one named special key. Escape IS natively
499
+ # supported here (unlike Orca, docs/orca-backend.md), so it is wired directly.
500
+ fm_backend_cmux_send_key() { # <target> <key> [expected-label]
501
+ fm_backend_cmux_target_ready "$1" "${3:-}" || return 1
502
+ local key
503
+ key=$(fm_backend_cmux_normalize_key "$2")
504
+ fm_backend_cmux_cli send-key --workspace "$FM_BACKEND_CMUX_WORKSPACE" --surface "$FM_BACKEND_CMUX_SURFACE" "$key" >/dev/null 2>&1
505
+ }
506
+
507
+ # fm_backend_cmux_send_text_line: send one line of TEXT then submit.
508
+ fm_backend_cmux_send_text_line() { # <target> <text> [expected-label]
509
+ fm_backend_cmux_send_literal "$1" "$2" "${3:-}" || return 1
510
+ fm_backend_cmux_send_key "$1" Enter "${3:-}" && return 0
511
+ fm_backend_cmux_send_key "$1" C-c "${3:-}" >/dev/null 2>&1 && return 1
512
+ return 2
513
+ }
514
+
515
+ # fm_backend_cmux_capture: bounded plain-text surface capture. No herdr-style
516
+ # small-N empty-result bug was found (finding #3), but "fetch generous, trim
517
+ # locally" is kept anyway: a single read-screen call is still bounded by the
518
+ # surface's actual current viewport height regardless of the requested
519
+ # --lines value, so a caller asking for more than the viewport can see would
520
+ # otherwise silently get less than it asked for with no way to tell why.
521
+ fm_backend_cmux_capture() { # <target> <lines> [expected-label]
522
+ fm_backend_cmux_target_ready "$1" "${3:-}" || return 1
523
+ local lines=${2:-200} fetch raw out
524
+ case "$lines" in ''|*[!0-9]*) lines=200 ;; esac
525
+ fetch=$lines
526
+ case "$fetch" in ''|*[!0-9]*) fetch=200 ;; *) [ "$fetch" -ge 200 ] || fetch=200 ;; esac
527
+ raw=$(fm_backend_cmux_cli read-screen --workspace "$FM_BACKEND_CMUX_WORKSPACE" --surface "$FM_BACKEND_CMUX_SURFACE" --scrollback --lines "$fetch" --json 2>/dev/null) || return 1
528
+ out=$(printf '%s' "$raw" | jq -r '.text // empty' 2>/dev/null) || return 1
529
+ printf '%s' "$out" | tail -n "$lines"
530
+ }
531
+
532
+ # fm_backend_cmux_composer_capture: the cmux composer screen - a bounded
533
+ # plain-text tail of the surface. cmux's `read-screen` is plain text by
534
+ # construction (its --help: "Read terminal text from a surface as plain
535
+ # text"), which is why the capability descriptor below declares styled=0: the
536
+ # shared classifier then degrades a glyph row carrying trailing text to
537
+ # `unknown` instead of misreading an idle suggestion as unsent input.
538
+ fm_backend_cmux_composer_capture() { # <target> [expected-label]
539
+ fm_backend_cmux_capture "$1" "$FM_COMPOSER_CAPTURE_LINES" "${2:-}"
540
+ }
541
+
542
+ # fm_backend_cmux_composer_caps: static capability facts, not logic (see the
543
+ # capability model in bin/fm-composer-lib.sh).
544
+ fm_backend_cmux_composer_caps() {
545
+ printf 'styled=0\ncursor=0\nidentity=0\nrows=%s\n' "$FM_COMPOSER_CAPTURE_LINES"
546
+ }
547
+
548
+ # fm_backend_cmux_composer_state: thin adapter - capture plus capabilities in,
549
+ # shared verdict out. Every shape (including the borderless claude row this
550
+ # adapter once carried its own NBSP workaround for) lives in
551
+ # bin/fm-composer-lib.sh, so a new harness shape is taught there once and
552
+ # never here. cmux has no identity probe, so the classifier's identity
553
+ # sentinel resolves to unknown.
554
+ fm_backend_cmux_composer_state() { # <target> [expected-label] -> empty|pending|pending-unproven|unknown
555
+ local cap verdict
556
+ cap=$(fm_backend_cmux_composer_capture "$1" "${2:-}") || { printf 'unknown'; return 0; }
557
+ verdict=$(fm_composer_classify_screen "$(fm_backend_cmux_composer_caps)" "$cap")
558
+ [ "$verdict" != need-identity ] || verdict=unknown
559
+ printf '%s' "$verdict"
560
+ }
561
+
562
+ # fm_backend_cmux_send_text_submit: type <text> into <target> once (raw,
563
+ # unsubmitted, via send_literal), then drive the shared verify-and-retry-Enter
564
+ # loop (bin/fm-composer-lib.sh: fm_composer_submit_retry_core) against the
565
+ # shared composer verdict. Echoes empty|pending|unknown|send-failed, a subset
566
+ # of the proof-carrying submit vocabulary.
567
+ fm_backend_cmux_send_text_submit() { # <target> <text> <retries> <enter-sleep> <settle> [expected-label]
568
+ local target=$1 text=$2 retries=$3 sleep_s=$4 settle=$5 expected_label=${6:-}
569
+ fm_backend_cmux_parse_target "$target" || { printf 'unknown'; return 0; }
570
+ fm_backend_cmux_send_literal "$target" "$text" "$expected_label" || { printf 'send-failed'; return 0; }
571
+ sleep "$settle"
572
+ fm_composer_submit_retry_core fm_backend_cmux_send_key fm_backend_cmux_composer_state \
573
+ "$target" "$retries" "$sleep_s" "$expected_label"
574
+ }
575
+
576
+ # fm_backend_cmux_window_of_workspace: echo "<window_id> <workspace_count>" for
577
+ # the window that contains <workspace_id>, or nothing if it is not found live.
578
+ # `workspace list --json` with no `--window` is scoped to the CURRENT window
579
+ # only (verified live), so the containing window is found by walking every
580
+ # window from `list-windows --json` and asking each for its own scoped list.
581
+ # The count comes from the same scoped workspace list that confirms membership.
582
+ fm_backend_cmux_window_of_workspace() { # <workspace_id> -> "<window_id> <count>"
583
+ local wsid=$1 wins wid wss count
584
+ wins=$(fm_backend_cmux_cli list-windows --json --id-format uuids 2>/dev/null) || return 0
585
+ while IFS= read -r wid; do
586
+ [ -n "$wid" ] || continue
587
+ wss=$(fm_backend_cmux_cli workspace list --json --id-format uuids --window "$wid" 2>/dev/null) || continue
588
+ count=$(printf '%s' "$wss" | jq -er --arg id "$wsid" '
589
+ (.workspaces // []) as $workspaces
590
+ | select(any($workspaces[]?; .id == $id))
591
+ | ($workspaces | length)
592
+ ' 2>/dev/null) || continue
593
+ printf '%s %s' "$wid" "$count"
594
+ return 0
595
+ done < <(printf '%s' "$wins" | jq -r '.[]? | .id' 2>/dev/null)
596
+ }
597
+
598
+ # fm_backend_cmux_kill: remove the task's whole workspace, best-effort (mirrors
599
+ # every other backend's `kill` `|| true` contract). A cmux task owns one
600
+ # workspace, so teardown reclaims that workspace and all of its surfaces.
601
+ #
602
+ # The selected-workspace teardown bug (docs/cmux-backend.md "Closing the last
603
+ # workspace in a window"): cmux keeps every window at >=1 workspace, so
604
+ # `close-workspace` on the ONLY workspace in its window silently no-ops - it
605
+ # still returns `OK`, but the workspace stays, which is exactly what left a
606
+ # selected task workspace open at teardown (the last workspace in a window is
607
+ # always the selected one). `close-window`/`window.close` cannot rescue it
608
+ # either: a window holding a live terminal session cannot be closed over the
609
+ # control socket (verified: returns success-shaped output, closes nothing).
610
+ # The reliable primitive is close-workspace on a NON-last workspace, so when the
611
+ # target is the last one in its window a throwaway sibling is created first,
612
+ # leaving that window a fresh default workspace (never an fm-<home>- title, so
613
+ # recovery/list_live ignore it) - cmux's own "closed the last tab" outcome.
614
+ fm_backend_cmux_kill() { # <target> [unused] [expected-label]
615
+ local expected_label=${3:-} wsid wininfo win count
616
+ if [ -n "$expected_label" ]; then
617
+ fm_backend_cmux_target_ready "$1" "$expected_label" || return 0
618
+ else
619
+ fm_backend_cmux_parse_target "$1" || return 0
620
+ fi
621
+ wsid=$FM_BACKEND_CMUX_WORKSPACE
622
+ wininfo=$(fm_backend_cmux_window_of_workspace "$wsid")
623
+ win=${wininfo%% *}
624
+ count=${wininfo##* }
625
+ if [ -n "$win" ] && [ "$count" = 1 ]; then
626
+ fm_backend_cmux_cli new-workspace --window "$win" --focus false --id-format uuids >/dev/null 2>&1 || true
627
+ fi
628
+ fm_backend_cmux_cli close-workspace --workspace "$wsid" >/dev/null 2>&1 || true
629
+ }
630
+
631
+ # fm_backend_cmux_list_live: recovery/orphan discovery. Lists every workspace
632
+ # whose title is scoped to this firstmate home, by TITLE - never by trusting a
633
+ # stored uuid, since workspace ids do NOT survive an app relaunch (finding #5).
634
+ # One "<workspace_id>:<surface_id>\t<fm-id>" line per live task workspace.
635
+ # Read-only: an unreachable cmux simply lists nothing.
636
+ fm_backend_cmux_list_live() {
637
+ local wss wsid title sfid home prefix plain
638
+ home=$(fm_backend_cmux_home_label)
639
+ prefix="fm-$home-"
640
+ wss=$(fm_backend_cmux_cli workspace list --json --id-format uuids 2>/dev/null) || return 0
641
+ while IFS=$'\t' read -r wsid title; do
642
+ [ -n "$wsid" ] || continue
643
+ plain=${title#"$prefix"}
644
+ [ -n "$plain" ] || continue
645
+ sfid=$(fm_backend_cmux_surface_id_for_workspace "$wsid")
646
+ [ -n "$sfid" ] || continue
647
+ printf '%s:%s\tfm-%s\n' "$wsid" "$sfid" "$plain"
648
+ done < <(printf '%s' "$wss" | jq -r --arg prefix "$prefix" '.workspaces[]? | select(.title | startswith($prefix)) | "\(.id)\t\(.title)"' 2>/dev/null)
649
+ }