@zerwiz/ymir 0.1.4 → 0.1.7

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 (865) 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/README.md +74 -0
  27. package/.agents/backend/backends/cmux.sh +649 -0
  28. package/.agents/backend/backends/herdr-eventwait.py +157 -0
  29. package/.agents/backend/backends/herdr-workspace-move.py +114 -0
  30. package/.agents/backend/backends/herdr.sh +3386 -0
  31. package/.agents/backend/backends/orca.sh +290 -0
  32. package/.agents/backend/backends/tmux.sh +357 -0
  33. package/.agents/backend/backends/zellij.sh +700 -0
  34. package/.agents/backend/fm-afk-launch.sh +654 -0
  35. package/.agents/backend/fm-afk-return.sh +241 -0
  36. package/.agents/backend/fm-afk-start.sh +172 -0
  37. package/.agents/backend/fm-arm-command-policy.mjs +960 -0
  38. package/.agents/backend/fm-arm-pretool-check.sh +200 -0
  39. package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
  40. package/.agents/backend/fm-backend.sh +981 -0
  41. package/.agents/backend/fm-backlog-handoff.sh +909 -0
  42. package/.agents/backend/fm-backlog-receive.sh +187 -0
  43. package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
  44. package/.agents/backend/fm-bearings-board.sh +204 -0
  45. package/.agents/backend/fm-bearings-snapshot.sh +546 -0
  46. package/.agents/backend/fm-bootstrap.sh +1568 -0
  47. package/.agents/backend/fm-branch-outcome.sh +203 -0
  48. package/.agents/backend/fm-branch-prompt.sh +101 -0
  49. package/.agents/backend/fm-brief.sh +452 -0
  50. package/.agents/backend/fm-busy-event.sh +234 -0
  51. package/.agents/backend/fm-busy-lib.sh +986 -0
  52. package/.agents/backend/fm-captain-hold.sh +1001 -0
  53. package/.agents/backend/fm-cd-command-policy.mjs +153 -0
  54. package/.agents/backend/fm-cd-pretool-check.sh +190 -0
  55. package/.agents/backend/fm-check-lib.sh +74 -0
  56. package/.agents/backend/fm-check-register.sh +42 -0
  57. package/.agents/backend/fm-check-unregister.sh +52 -0
  58. package/.agents/backend/fm-classify-lib.sh +1770 -0
  59. package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
  60. package/.agents/backend/fm-composer-lib.sh +1416 -0
  61. package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
  62. package/.agents/backend/fm-config-push.sh +254 -0
  63. package/.agents/backend/fm-control-lib.sh +251 -0
  64. package/.agents/backend/fm-control.sh +878 -0
  65. package/.agents/backend/fm-crew-state.sh +625 -0
  66. package/.agents/backend/fm-cursor-lib.sh +243 -0
  67. package/.agents/backend/fm-decision-hold.sh +232 -0
  68. package/.agents/backend/fm-doc-audience-check.sh +269 -0
  69. package/.agents/backend/fm-dod-lib.sh +67 -0
  70. package/.agents/backend/fm-ensure-agents-md.sh +253 -0
  71. package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
  72. package/.agents/backend/fm-extension.mjs +2577 -0
  73. package/.agents/backend/fm-extension.sh +16 -0
  74. package/.agents/backend/fm-ff-lib.sh +420 -0
  75. package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
  76. package/.agents/backend/fm-fleet-sync.sh +458 -0
  77. package/.agents/backend/fm-fleet-view.sh +96 -0
  78. package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
  79. package/.agents/backend/fm-guard.sh +242 -0
  80. package/.agents/backend/fm-harness.sh +195 -0
  81. package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
  82. package/.agents/backend/fm-herdr-lab.sh +346 -0
  83. package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
  84. package/.agents/backend/fm-home-seed.sh +984 -0
  85. package/.agents/backend/fm-home-summary-refresh.sh +255 -0
  86. package/.agents/backend/fm-hook-host-lib.sh +36 -0
  87. package/.agents/backend/fm-inactive-reconcile.sh +531 -0
  88. package/.agents/backend/fm-inbox.sh +399 -0
  89. package/.agents/backend/fm-install-actionlint.sh +84 -0
  90. package/.agents/backend/fm-install-herdr.sh +96 -0
  91. package/.agents/backend/fm-install-shellcheck.sh +84 -0
  92. package/.agents/backend/fm-install-treehouse.sh +95 -0
  93. package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
  94. package/.agents/backend/fm-lease-lib.sh +218 -0
  95. package/.agents/backend/fm-lease.sh +189 -0
  96. package/.agents/backend/fm-line-cap-lib.sh +51 -0
  97. package/.agents/backend/fm-lint-workflows.sh +137 -0
  98. package/.agents/backend/fm-lint.sh +591 -0
  99. package/.agents/backend/fm-lock-lib.sh +104 -0
  100. package/.agents/backend/fm-lock.sh +107 -0
  101. package/.agents/backend/fm-marker-lib.sh +12 -0
  102. package/.agents/backend/fm-merge-local.sh +74 -0
  103. package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
  104. package/.agents/backend/fm-nm-run-lib.sh +124 -0
  105. package/.agents/backend/fm-on.sh +125 -0
  106. package/.agents/backend/fm-operational-input.sh +253 -0
  107. package/.agents/backend/fm-peek.sh +46 -0
  108. package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
  109. package/.agents/backend/fm-pr-check.sh +135 -0
  110. package/.agents/backend/fm-pr-lib.sh +1016 -0
  111. package/.agents/backend/fm-pr-merge.sh +707 -0
  112. package/.agents/backend/fm-pr-poll.sh +110 -0
  113. package/.agents/backend/fm-primary-scope-lib.sh +33 -0
  114. package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
  115. package/.agents/backend/fm-procevent-lavish.sh +628 -0
  116. package/.agents/backend/fm-procevent-lib.sh +892 -0
  117. package/.agents/backend/fm-procevent-quota.sh +290 -0
  118. package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
  119. package/.agents/backend/fm-procevent-when.sh +504 -0
  120. package/.agents/backend/fm-procevent.sh +1466 -0
  121. package/.agents/backend/fm-project-mode.sh +92 -0
  122. package/.agents/backend/fm-project-origin-lib.sh +180 -0
  123. package/.agents/backend/fm-promote.sh +282 -0
  124. package/.agents/backend/fm-public-followup-emit.sh +260 -0
  125. package/.agents/backend/fm-public-followup-lib.sh +398 -0
  126. package/.agents/backend/fm-public-followup.sh +1330 -0
  127. package/.agents/backend/fm-push-transition-lib.sh +168 -0
  128. package/.agents/backend/fm-quota-axi-lib.sh +93 -0
  129. package/.agents/backend/fm-quota-choose.sh +384 -0
  130. package/.agents/backend/fm-remote-delta-read.sh +189 -0
  131. package/.agents/backend/fm-remote-doctor.sh +800 -0
  132. package/.agents/backend/fm-remote-entrypoint.sh +193 -0
  133. package/.agents/backend/fm-remote-file.sh +234 -0
  134. package/.agents/backend/fm-remote-home-provision.sh +259 -0
  135. package/.agents/backend/fm-remote-home-seed.sh +259 -0
  136. package/.agents/backend/fm-remote-inherit-push.sh +90 -0
  137. package/.agents/backend/fm-remote-inherit.sh +179 -0
  138. package/.agents/backend/fm-remote-job-lib.sh +1241 -0
  139. package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
  140. package/.agents/backend/fm-remote-job-worker.sh +1124 -0
  141. package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
  142. package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
  143. package/.agents/backend/fm-review-diff.sh +158 -0
  144. package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
  145. package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
  146. package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
  147. package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
  148. package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
  149. package/.agents/backend/fm-secondmate-report.sh +88 -0
  150. package/.agents/backend/fm-send.sh +1059 -0
  151. package/.agents/backend/fm-session-lock-lib.sh +176 -0
  152. package/.agents/backend/fm-session-start.sh +970 -0
  153. package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
  154. package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
  155. package/.agents/backend/fm-sessionstart-run.sh +145 -0
  156. package/.agents/backend/fm-spawn.sh +3153 -0
  157. package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
  158. package/.agents/backend/fm-startup-memory-budget.sh +94 -0
  159. package/.agents/backend/fm-startup-network.sh +642 -0
  160. package/.agents/backend/fm-stow-cascade.sh +251 -0
  161. package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
  162. package/.agents/backend/fm-supervise-daemon.sh +1732 -0
  163. package/.agents/backend/fm-supervision-instructions.sh +215 -0
  164. package/.agents/backend/fm-supervision-lib.sh +90 -0
  165. package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
  166. package/.agents/backend/fm-tangle-lib.sh +53 -0
  167. package/.agents/backend/fm-task-inbox-lib.sh +402 -0
  168. package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
  169. package/.agents/backend/fm-teardown.sh +2922 -0
  170. package/.agents/backend/fm-test-isolation-proof.sh +588 -0
  171. package/.agents/backend/fm-test-run.sh +2191 -0
  172. package/.agents/backend/fm-timeout-lib.sh +141 -0
  173. package/.agents/backend/fm-timing-lib.sh +179 -0
  174. package/.agents/backend/fm-tmux-lib.sh +291 -0
  175. package/.agents/backend/fm-tool-update-check.sh +898 -0
  176. package/.agents/backend/fm-trace-context-lib.sh +227 -0
  177. package/.agents/backend/fm-transition-lib.sh +103 -0
  178. package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
  179. package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
  180. package/.agents/backend/fm-turnend-guard.sh +432 -0
  181. package/.agents/backend/fm-update.sh +110 -0
  182. package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
  183. package/.agents/backend/fm-voice-client.py +1373 -0
  184. package/.agents/backend/fm-voice-relay.py +1256 -0
  185. package/.agents/backend/fm-wake-drain.sh +592 -0
  186. package/.agents/backend/fm-wake-grant.sh +127 -0
  187. package/.agents/backend/fm-wake-lib.sh +1849 -0
  188. package/.agents/backend/fm-watch-arm.sh +602 -0
  189. package/.agents/backend/fm-watch-checkpoint.sh +109 -0
  190. package/.agents/backend/fm-watch.sh +1962 -0
  191. package/.agents/backend/fm-x-dismiss.sh +116 -0
  192. package/.agents/backend/fm-x-followup.sh +286 -0
  193. package/.agents/backend/fm-x-lib.sh +1000 -0
  194. package/.agents/backend/fm-x-link.sh +234 -0
  195. package/.agents/backend/fm-x-poll.sh +185 -0
  196. package/.agents/backend/fm-x-reply.sh +385 -0
  197. package/.agents/backend/fm_voice_frame.py +166 -0
  198. package/.agents/backend/fm_voice_records.py +574 -0
  199. package/.agents/backend/model-bridge.py +360 -0
  200. package/.agents/backend/opencode-go-bridge.py +30 -0
  201. package/.agents/bus/README.md +12 -0
  202. package/.agents/bus/messages.json +1 -0
  203. package/.agents/config/agents.machine.example.yaml +16 -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 +11 -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/filebrowser/.filebrowser.json +6 -0
  211. package/.agents/filebrowser/README.md +11 -0
  212. package/.agents/gateway/README.md +12 -0
  213. package/.agents/github/README.md +15 -0
  214. package/.agents/github/webhooks/.gitkeep +0 -0
  215. package/.agents/github/workflows/.gitkeep +0 -0
  216. package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
  217. package/.agents/harness/opencode/plugins/package.json +4 -0
  218. package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
  219. package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
  220. package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
  221. package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
  222. package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
  223. package/.agents/memory/README.md +46 -0
  224. package/.agents/memory/runes_audit.md +5 -0
  225. package/.agents/migrations/0001-hodd-layout.sh +38 -0
  226. package/.agents/migrations/0002-a2a-mcp.sh +27 -0
  227. package/.agents/migrations/0003-private-data-separation.sh +121 -0
  228. package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
  229. package/.agents/migrations/0005-roots-out-of-tree.sh +158 -0
  230. package/.agents/sandbox/Dockerfile.utgard +19 -0
  231. package/.agents/sandbox/README.md +14 -0
  232. package/.agents/sandbox/utgard.config.json +14 -0
  233. package/.agents/skills/README.md +59 -0
  234. package/.agents/skills/bragi-marketing/README.md +3 -0
  235. package/.agents/skills/bragi-marketing/SKILL.md +82 -0
  236. package/.agents/skills/eindri-homes/README.md +3 -0
  237. package/.agents/skills/eindri-homes/SKILL.md +291 -0
  238. package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
  239. package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
  240. package/.agents/skills/frigg-consent/README.md +3 -0
  241. package/.agents/skills/frigg-consent/SKILL.md +56 -0
  242. package/.agents/skills/galdr-ymirsystem/README.md +3 -0
  243. package/.agents/skills/galdr-ymirsystem/SKILL.md +159 -0
  244. package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
  245. package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
  246. package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
  247. package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
  248. package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
  249. package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
  250. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +736 -0
  251. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
  252. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
  253. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
  254. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
  255. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
  256. package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +539 -0
  257. package/.agents/skills/galdr-ymirsystem/assets/installation.md +763 -0
  258. package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
  259. package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
  260. package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +163 -0
  261. package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +415 -0
  262. package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
  263. package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +133 -0
  264. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
  265. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
  266. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +101 -0
  267. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
  268. package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
  269. package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
  270. package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
  271. package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
  272. package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
  273. package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
  274. package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
  275. package/.agents/skills/galdr-ymirsystem/assets/smidja.md +180 -0
  276. package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
  277. package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
  278. package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
  279. package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +503 -0
  280. package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
  281. package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
  282. package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
  283. package/.agents/skills/gjallarhorn-relay/README.md +3 -0
  284. package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
  285. package/.agents/skills/groa-update/SKILL.md +90 -0
  286. package/.agents/skills/hamr-adapters/README.md +3 -0
  287. package/.agents/skills/hamr-adapters/SKILL.md +123 -0
  288. package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
  289. package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
  290. package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
  291. package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
  292. package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
  293. package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
  294. package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
  295. package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
  296. package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
  297. package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
  298. package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
  299. package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
  300. package/.agents/skills/herdr-panes/README.md +3 -0
  301. package/.agents/skills/herdr-panes/SKILL.md +92 -0
  302. package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
  303. package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
  304. package/.agents/skills/hnoss-design/DESIGN.md +19 -0
  305. package/.agents/skills/hnoss-design/README.md +3 -0
  306. package/.agents/skills/hnoss-design/SKILL.md +83 -0
  307. package/.agents/skills/hvild-afk/README.md +3 -0
  308. package/.agents/skills/hvild-afk/SKILL.md +241 -0
  309. package/.agents/skills/jord-projects/README.md +3 -0
  310. package/.agents/skills/jord-projects/SKILL.md +92 -0
  311. package/.agents/skills/lifecycle/SKILL.md +56 -0
  312. package/.agents/skills/lifecycle/smoke_test.sh +57 -0
  313. package/.agents/skills/lifecycle/start.sh +21 -0
  314. package/.agents/skills/lifecycle/status.sh +35 -0
  315. package/.agents/skills/lifecycle/stop.sh +16 -0
  316. package/.agents/skills/muninn-stow/README.md +3 -0
  317. package/.agents/skills/muninn-stow/SKILL.md +309 -0
  318. package/.agents/skills/no-mistakes/SKILL.md +379 -0
  319. package/.agents/skills/nornir-schedule/README.md +3 -0
  320. package/.agents/skills/nornir-schedule/SKILL.md +25 -0
  321. package/.agents/skills/nornir-schedule/assets/events.md +137 -0
  322. package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
  323. package/.agents/skills/nsr-compliance/README.md +3 -0
  324. package/.agents/skills/nsr-compliance/SKILL.md +29 -0
  325. package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
  326. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
  327. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
  328. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
  329. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
  330. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
  331. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
  332. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
  333. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
  334. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
  335. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
  336. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
  337. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
  338. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
  339. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
  340. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
  341. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
  342. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
  343. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
  344. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
  345. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
  346. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
  347. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
  348. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  349. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
  350. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
  351. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
  352. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
  353. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
  354. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
  355. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
  356. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
  357. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
  358. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
  359. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
  360. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
  361. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
  362. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
  363. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
  364. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
  365. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
  366. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
  367. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
  368. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
  369. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
  370. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
  371. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
  372. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
  373. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
  374. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
  375. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
  376. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
  377. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
  378. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
  379. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
  380. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
  381. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
  382. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
  383. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
  384. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
  385. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
  386. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
  387. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
  388. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
  389. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
  390. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
  391. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
  392. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
  393. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
  394. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
  395. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
  396. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
  397. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
  398. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
  399. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
  400. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
  401. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
  402. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
  403. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
  404. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
  405. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
  406. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
  407. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
  408. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
  409. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
  410. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
  411. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
  412. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
  413. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
  414. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
  415. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
  416. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
  417. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
  418. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
  419. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
  420. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
  421. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
  422. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
  423. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
  424. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
  425. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
  426. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
  427. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
  428. package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
  429. package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
  430. package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
  431. package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
  432. package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
  433. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
  434. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
  435. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
  436. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
  437. package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
  438. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
  439. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
  440. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
  441. package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
  442. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
  443. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
  444. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
  445. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
  446. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
  447. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
  448. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
  449. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  450. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
  451. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
  452. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
  453. package/.agents/skills/pr-ops/README.md +3 -0
  454. package/.agents/skills/pr-ops/SKILL.md +109 -0
  455. package/.agents/skills/ratatoskr-a2a/README.md +3 -0
  456. package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
  457. package/.agents/skills/rules-check-drift/SKILL.md +81 -0
  458. package/.agents/skills/saga-bearings/README.md +3 -0
  459. package/.agents/skills/saga-bearings/SKILL.md +26 -0
  460. package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
  461. package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
  462. package/.agents/skills/saga-bearings/assets/recap.md +56 -0
  463. package/.agents/skills/syn-recovery/README.md +3 -0
  464. package/.agents/skills/syn-recovery/SKILL.md +54 -0
  465. package/.agents/skills/tyr-check/README.md +3 -0
  466. package/.agents/skills/tyr-check/SKILL.md +218 -0
  467. package/.agents/skills/urdh-hold/README.md +3 -0
  468. package/.agents/skills/urdh-hold/SKILL.md +25 -0
  469. package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
  470. package/.agents/skills/urdh-hold/assets/hold.md +54 -0
  471. package/.agents/skills/vor-diagnostics/README.md +3 -0
  472. package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
  473. package/.agents/skills/ymir-host/README.md +3 -0
  474. package/.agents/skills/ymir-host/SKILL.md +35 -0
  475. package/.agents/skills/ymir-host/assets/desktop.md +36 -0
  476. package/.agents/skills/ymir-host/assets/install.md +69 -0
  477. package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
  478. package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
  479. package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
  480. package/.agents/skills/ymir-host/assets/update.md +64 -0
  481. package/.agents/tests/assets/board-render-harness.mjs +117 -0
  482. package/.agents/tests/cmux-test-safety.sh +51 -0
  483. package/.agents/tests/fixtures.sh +291 -0
  484. package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
  485. package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
  486. package/.agents/tests/fm-afk-launch.test.sh +961 -0
  487. package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
  488. package/.agents/tests/fm-afk-return.test.sh +283 -0
  489. package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
  490. package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
  491. package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
  492. package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
  493. package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
  494. package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
  495. package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
  496. package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
  497. package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
  498. package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
  499. package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
  500. package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
  501. package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
  502. package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
  503. package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
  504. package/.agents/tests/fm-backend-orca.test.sh +1357 -0
  505. package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
  506. package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
  507. package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
  508. package/.agents/tests/fm-backend.test.sh +1142 -0
  509. package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
  510. package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
  511. package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
  512. package/.agents/tests/fm-bearings-board.test.sh +414 -0
  513. package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
  514. package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
  515. package/.agents/tests/fm-bootstrap.test.sh +1178 -0
  516. package/.agents/tests/fm-branch-supervision.test.sh +552 -0
  517. package/.agents/tests/fm-brief.test.sh +785 -0
  518. package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
  519. package/.agents/tests/fm-busy-state.test.sh +465 -0
  520. package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
  521. package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
  522. package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
  523. package/.agents/tests/fm-check-unregister.test.sh +198 -0
  524. package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
  525. package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
  526. package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
  527. package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
  528. package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
  529. package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
  530. package/.agents/tests/fm-composer-ghost.test.sh +716 -0
  531. package/.agents/tests/fm-composer-lib.test.sh +666 -0
  532. package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
  533. package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
  534. package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
  535. package/.agents/tests/fm-control.test.sh +910 -0
  536. package/.agents/tests/fm-crew-state.test.sh +1610 -0
  537. package/.agents/tests/fm-cursor-harness.test.sh +404 -0
  538. package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
  539. package/.agents/tests/fm-cursor-primary.test.sh +708 -0
  540. package/.agents/tests/fm-daemon.test.sh +2740 -0
  541. package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
  542. package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
  543. package/.agents/tests/fm-extension-binding.test.sh +2187 -0
  544. package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
  545. package/.agents/tests/fm-fleet-sync.test.sh +721 -0
  546. package/.agents/tests/fm-gate-refuse.test.sh +356 -0
  547. package/.agents/tests/fm-gitignore-config.test.sh +89 -0
  548. package/.agents/tests/fm-gotmp.test.sh +230 -0
  549. package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
  550. package/.agents/tests/fm-grok-harness.test.sh +117 -0
  551. package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
  552. package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
  553. package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
  554. package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
  555. package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
  556. package/.agents/tests/fm-herdr-lab.test.sh +243 -0
  557. package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
  558. package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
  559. package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
  560. package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
  561. package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
  562. package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
  563. package/.agents/tests/fm-kimi-harness.test.sh +687 -0
  564. package/.agents/tests/fm-lint-workflows.test.sh +532 -0
  565. package/.agents/tests/fm-lint.test.sh +1023 -0
  566. package/.agents/tests/fm-muse-harness.test.sh +932 -0
  567. package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
  568. package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
  569. package/.agents/tests/fm-on.test.sh +507 -0
  570. package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
  571. package/.agents/tests/fm-operational-input.test.sh +160 -0
  572. package/.agents/tests/fm-peek-remote.test.sh +110 -0
  573. package/.agents/tests/fm-pending-reply.test.sh +1288 -0
  574. package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
  575. package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
  576. package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
  577. package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
  578. package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
  579. package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
  580. package/.agents/tests/fm-pr-merge.test.sh +2135 -0
  581. package/.agents/tests/fm-procevent-quota.test.sh +225 -0
  582. package/.agents/tests/fm-procevent-when.test.sh +406 -0
  583. package/.agents/tests/fm-procevent.test.sh +1745 -0
  584. package/.agents/tests/fm-project-origin.test.sh +110 -0
  585. package/.agents/tests/fm-public-followup.test.sh +2272 -0
  586. package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
  587. package/.agents/tests/fm-quota-choose.test.sh +626 -0
  588. package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
  589. package/.agents/tests/fm-remote-doctor.test.sh +625 -0
  590. package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
  591. package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
  592. package/.agents/tests/fm-remote-job.test.sh +768 -0
  593. package/.agents/tests/fm-remote-reply.test.sh +520 -0
  594. package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
  595. package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
  596. package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
  597. package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
  598. package/.agents/tests/fm-review-diff.test.sh +176 -0
  599. package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
  600. package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
  601. package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
  602. package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
  603. package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
  604. package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
  605. package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
  606. package/.agents/tests/fm-send-inbox.test.sh +352 -0
  607. package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
  608. package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
  609. package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
  610. package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
  611. package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
  612. package/.agents/tests/fm-send-settle.test.sh +147 -0
  613. package/.agents/tests/fm-send-strict.test.sh +241 -0
  614. package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
  615. package/.agents/tests/fm-session-start.test.sh +2515 -0
  616. package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
  617. package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
  618. package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
  619. package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
  620. package/.agents/tests/fm-spawn-batch.test.sh +148 -0
  621. package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
  622. package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
  623. package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
  624. package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
  625. package/.agents/tests/fm-startup-network.test.sh +712 -0
  626. package/.agents/tests/fm-stow-cascade.test.sh +370 -0
  627. package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
  628. package/.agents/tests/fm-supervision-events.test.sh +156 -0
  629. package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
  630. package/.agents/tests/fm-tangle-guard.test.sh +277 -0
  631. package/.agents/tests/fm-task-delivery.test.sh +420 -0
  632. package/.agents/tests/fm-task-inbox.test.sh +513 -0
  633. package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
  634. package/.agents/tests/fm-teardown.test.sh +2665 -0
  635. package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
  636. package/.agents/tests/fm-test-fixtures.test.sh +132 -0
  637. package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
  638. package/.agents/tests/fm-test-run.test.sh +1196 -0
  639. package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
  640. package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
  641. package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
  642. package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
  643. package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
  644. package/.agents/tests/fm-transition-lib.test.sh +54 -0
  645. package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
  646. package/.agents/tests/fm-update.test.sh +304 -0
  647. package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
  648. package/.agents/tests/fm-voice-relay.test.sh +4741 -0
  649. package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
  650. package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
  651. package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
  652. package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
  653. package/.agents/tests/fm-wake-queue.test.sh +1224 -0
  654. package/.agents/tests/fm-watch-arm.test.sh +821 -0
  655. package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
  656. package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
  657. package/.agents/tests/fm-watch-triage.test.sh +3889 -0
  658. package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
  659. package/.agents/tests/fm-x-mode.test.sh +3042 -0
  660. package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
  661. package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
  662. package/.agents/tests/herdr-test-safety.sh +42 -0
  663. package/.agents/tests/lib.sh +315 -0
  664. package/.agents/tests/remote-herdr-fixture.sh +125 -0
  665. package/.agents/tests/secondmate-helpers.sh +193 -0
  666. package/.agents/tests/smoke.test.sh +93 -0
  667. package/.agents/tests/wake-helpers.sh +338 -0
  668. package/.agents/tests/zellij-test-safety.sh +60 -0
  669. package/.agents/tools/README.md +10 -0
  670. package/.agents/tools/bin/tasks-cli +267 -0
  671. package/.agents/tools/tasks-cli.ts +216 -0
  672. package/AGENTS.md +498 -0
  673. package/CHANGELOG.md +1913 -0
  674. package/LICENSE +1 -1
  675. package/NOTICE +63 -0
  676. package/README.md +114 -60
  677. package/RULES/01-domains.md +51 -0
  678. package/RULES/02-agents.md +78 -0
  679. package/RULES/03-houses.md +61 -0
  680. package/RULES/04-hoard.md +97 -0
  681. package/RULES/05-platforms.md +67 -0
  682. package/RULES/06-append-only.md +108 -0
  683. package/RULES/07-config.md +101 -0
  684. package/RULES/08-delivery-gate.md +75 -0
  685. package/RULES/09-electron.md +58 -0
  686. package/RULES/README.md +22 -0
  687. package/bin/README.md +107 -0
  688. package/bin/a2a-mcp.sh +133 -0
  689. package/bin/a2a-serve.py +103 -0
  690. package/bin/a2a-serve.sh +8 -0
  691. package/bin/a2a-talk.sh +50 -0
  692. package/bin/a2abridge-ensure.sh +108 -0
  693. package/bin/agent-run.sh +43 -0
  694. package/bin/agents-config.sh +256 -0
  695. package/bin/apodex-smoke-test.sh +105 -0
  696. package/bin/bifrost-bridge.sh +131 -0
  697. package/bin/bifrost-ingress.sh +77 -0
  698. package/bin/bootstrap-macos.sh +112 -0
  699. package/bin/bootstrap-windows.ps1 +112 -0
  700. package/bin/branch-guard.sh +64 -0
  701. package/bin/brokk +73 -0
  702. package/bin/brokk-classify-lib.sh +1770 -0
  703. package/bin/brokk-lease-lib.sh +222 -0
  704. package/bin/brokk-lease.sh +193 -0
  705. package/bin/brokk-lint.sh +65 -0
  706. package/bin/brokk-send.sh +51 -0
  707. package/bin/brokk-update.sh +9 -0
  708. package/bin/brokk-wake-grant.sh +127 -0
  709. package/bin/brokk-wake-lib.sh +1849 -0
  710. package/bin/changelog-assemble.sh +154 -0
  711. package/bin/changelog-guard.sh +197 -0
  712. package/bin/crash-sense.sh +173 -0
  713. package/bin/design-check.sh +80 -0
  714. package/bin/design-icon.sh +153 -0
  715. package/bin/desktop-place.sh +224 -0
  716. package/bin/docs-guard.sh +39 -0
  717. package/bin/editor-place.sh +225 -0
  718. package/bin/eindri-acclaim.sh +47 -0
  719. package/bin/eindri-control.sh +28 -0
  720. package/bin/eindri-role.sh +83 -0
  721. package/bin/eindri-seat.sh +83 -0
  722. package/bin/eindri-seen.sh +54 -0
  723. package/bin/eindri-send.sh +15 -0
  724. package/bin/eindri-start.sh +98 -0
  725. package/bin/eindri-watch.sh +57 -0
  726. package/bin/einherjar-spawn.sh +576 -0
  727. package/bin/eir-doctor.sh +158 -0
  728. package/bin/erindi-brief.sh +271 -0
  729. package/bin/feature-inventory.sh +73 -0
  730. package/bin/fleet-apply.sh +81 -0
  731. package/bin/github-deploy.sh +94 -0
  732. package/bin/gjallarhorn-expose.sh +136 -0
  733. package/bin/gjallarhorn-notify.sh +71 -0
  734. package/bin/gjallarhorn-purge.sh +67 -0
  735. package/bin/gjallarhorn-tunnel.sh +85 -0
  736. package/bin/gleipnir-lock-lib.sh +284 -0
  737. package/bin/groa-update.sh +130 -0
  738. package/bin/hall-snapshot.sh +111 -0
  739. package/bin/hamr-harness.sh +246 -0
  740. package/bin/heimdall-ssh-keys.sh +374 -0
  741. package/bin/herdr-agents.py +44 -0
  742. package/bin/herdr-ensure.sh +104 -0
  743. package/bin/herdr-run.sh +452 -0
  744. package/bin/hermes-ensure.sh +88 -0
  745. package/bin/hlidskjalf-agents.sh +172 -0
  746. package/bin/hlidskjalf-usage.sh +196 -0
  747. package/bin/hoard-guard.sh +135 -0
  748. package/bin/hoard-lib.sh +105 -0
  749. package/bin/hodd.sh +102 -0
  750. package/bin/host-sense.sh +199 -0
  751. package/bin/huginn-research-worker.sh +121 -0
  752. package/bin/local-model-lock.sh +70 -0
  753. package/bin/mimir-bridge.py +259 -0
  754. package/bin/mimir-bridge.sh +100 -0
  755. package/bin/mimir-ingest.sh +131 -0
  756. package/bin/mimir-reflect.sh +27 -0
  757. package/bin/mimir.sh +113 -0
  758. package/bin/mjollnir-webhook.sh +72 -0
  759. package/bin/mjollnir.sh +104 -0
  760. package/bin/model-hardware.sh +96 -0
  761. package/bin/model-resolve.sh +160 -0
  762. package/bin/models-detect.sh +178 -0
  763. package/bin/nornir-cron-start.sh +162 -0
  764. package/bin/nornir-job-daily-briefing.sh +191 -0
  765. package/bin/nornir-job-git-sync.sh +152 -0
  766. package/bin/nornir-job-hall-snapshot.sh +28 -0
  767. package/bin/nornir-job-memory-housekeeping.sh +114 -0
  768. package/bin/nornir-job-nsr-compliance.sh +54 -0
  769. package/bin/nornir-job-observer.sh +166 -0
  770. package/bin/nornir-job-skillopt-sleep.sh +76 -0
  771. package/bin/npm-publish.sh +136 -0
  772. package/bin/omarchy-hook-install.sh +60 -0
  773. package/bin/omarchy-install.sh +161 -0
  774. package/bin/omarchy-plugins.sh +187 -0
  775. package/bin/omarchy-sense.sh +162 -0
  776. package/bin/perm-guard.sh +30 -0
  777. package/bin/pi-agent.sh +68 -0
  778. package/bin/pi-ensure.sh +77 -0
  779. package/bin/pi-local.sh +37 -0
  780. package/bin/pi-model-agnostic.sh +71 -0
  781. package/bin/pi-seat.sh +63 -0
  782. package/bin/prereq-ensure.sh +166 -0
  783. package/bin/private-guard.sh +58 -0
  784. package/bin/project-git.sh +69 -0
  785. package/bin/public-guard.sh +71 -0
  786. package/bin/ratatoskr.sh +49 -0
  787. package/bin/realm-lib.sh +32 -0
  788. package/bin/repo-scrub.sh +62 -0
  789. package/bin/rodd-operational-input.sh +250 -0
  790. package/bin/runes-append.sh +198 -0
  791. package/bin/saga-session-start.sh +172 -0
  792. package/bin/saga-sessionstart-run.sh +79 -0
  793. package/bin/saga-wake-drain.sh +41 -0
  794. package/bin/secret-guard.sh +63 -0
  795. package/bin/sessrumnir-ensure.sh +100 -0
  796. package/bin/sessrumnir-sync.sh +237 -0
  797. package/bin/sessrumnir.sh +104 -0
  798. package/bin/skillopt-setup.sh +76 -0
  799. package/bin/skuld-branch-outcome.sh +203 -0
  800. package/bin/skuld-branch-prompt.sh +101 -0
  801. package/bin/smidja-bootstrap.sh +94 -0
  802. package/bin/smidja-observe.sh +86 -0
  803. package/bin/syn-arm-pretool-check.sh +57 -0
  804. package/bin/syn-asset-pretool-check.sh +89 -0
  805. package/bin/syn-cd-pretool-check.sh +28 -0
  806. package/bin/syn-guard-pretool-check.sh +83 -0
  807. package/bin/syn-spawn-pretool-check.sh +58 -0
  808. package/bin/syn-turnend-guard.sh +36 -0
  809. package/bin/syn-watch-arm.sh +99 -0
  810. package/bin/tailscale-sync.sh +203 -0
  811. package/bin/telegram-bot.sh +82 -0
  812. package/bin/toolchain.sh +74 -0
  813. package/bin/ui-truth-check.sh +75 -0
  814. package/bin/utgard.sh +111 -0
  815. package/bin/valhalla.sh +115 -0
  816. package/bin/valknut-load.sh +298 -0
  817. package/bin/vor-crew-state.sh +124 -0
  818. package/bin/wedge-notify.sh +60 -0
  819. package/bin/workspace-provision.sh +80 -0
  820. package/bin/workspace-rag.sh +163 -0
  821. package/bin/wyrd-db.sh +47 -0
  822. package/bin/yggdrasil.sh +152 -0
  823. package/bin/ymir-edit.sh +145 -0
  824. package/bin/ymir-install.sh +806 -0
  825. package/bin/ymir-invite.sh +93 -0
  826. package/bin/ymir-isolation.sh +88 -0
  827. package/bin/ymir-migrate.sh +61 -0
  828. package/bin/ymir-plan.sh +356 -0
  829. package/bin/ymir-platform.sh +348 -0
  830. package/bin/ymir-say.sh +110 -0
  831. package/bin/ymir-setup-auth.sh +119 -0
  832. package/bin/ymir-validate.sh +193 -0
  833. package/bin/ymir.js +74 -16
  834. package/docs/Architecture.md +339 -0
  835. package/docs/design.md +351 -0
  836. package/docs/installations/README.md +63 -0
  837. package/docs/installations/ymir-home.md +93 -0
  838. package/docs/integration.md +105 -0
  839. package/docs/lore.md +1060 -0
  840. package/docs/research/agent-harnesses.md +290 -0
  841. package/docs/runbooks/README.md +19 -0
  842. package/docs/runbooks/a2a.md +91 -0
  843. package/docs/runbooks/agent-permissions.md +99 -0
  844. package/docs/runbooks/agents.md +57 -0
  845. package/docs/runbooks/models.md +69 -0
  846. package/docs/runbooks/realm-onboarding.md +60 -0
  847. package/docs/runbooks/secrets-and-hoard.md +60 -0
  848. package/docs/runbooks/updates-and-migrations.md +110 -0
  849. package/docs/runbooks/worktrees-isolation.md +72 -0
  850. package/docs/session-start.md +105 -0
  851. package/docs/skillopt-integration.md +321 -0
  852. package/docs/sync-system.md +66 -0
  853. package/docs/workspaces.md +104 -0
  854. package/docs/ymir-rut.md +260 -0
  855. package/package.json +54 -14
  856. package/packaging/build.sh +74 -0
  857. package/packaging/macos/Ymir Installer.command +43 -0
  858. package/packaging/windows/ymir-setup.nsi +74 -0
  859. package/scripts/README.md +22 -0
  860. package/scripts/electron.sh +269 -0
  861. package/scripts/lower.sh +34 -0
  862. package/scripts/raise.sh +42 -0
  863. package/scripts/start.sh +217 -0
  864. package/scripts/stop.sh +113 -0
  865. package/install.sh +0 -35
@@ -0,0 +1,981 @@
1
+ #!/usr/bin/env bash
2
+ # fm-backend.sh - runtime-backend selection, meta helpers, selector resolution,
3
+ # and dispatch for firstmate's session-provider abstraction.
4
+ #
5
+ # Design: data/fm-backend-design-d7/report.md ("Backend Interface") and
6
+ # data/fm-backend-design-d7/herdr-addendum.md ("Events as the core
7
+ # abstraction"). P1 extracted the tmux command sequences that fm-send.sh,
8
+ # fm-peek.sh, fm-watch.sh, fm-spawn.sh, and fm-teardown.sh already ran inline
9
+ # into bin/backends/tmux.sh, with those SAME command sequences, so the default
10
+ # (tmux) path stays byte-identical. P2 adds bin/backends/herdr.sh, an
11
+ # EXPERIMENTAL spawn-capable backend behind `--backend herdr`/`FM_BACKEND=herdr`/
12
+ # `config/backend`, and behind runtime auto-detection when firstmate itself is
13
+ # running inside herdr with no explicit backend setting; see herdr-addendum.md and
14
+ # data/fm-backend-design-d7/herdr-verification-p2.md for its empirical basis.
15
+ # P3 adds bin/backends/zellij.sh, also EXPERIMENTAL and spawn-capable, behind
16
+ # `--backend zellij`/`FM_BACKEND=zellij`/`config/backend` - NOT behind runtime
17
+ # auto-detection (report.md's Open Question #2: start with a dedicated
18
+ # background session for predictability, unlike tmux's/herdr's ambient-session
19
+ # reuse); see report.md's "Zellij Backend" section and docs/zellij-backend.md
20
+ # for its empirical basis. P4 makes Orca spawn-capable: Orca owns both the
21
+ # task worktree and the terminal endpoint. P5 adds bin/backends/cmux.sh, also
22
+ # EXPERIMENTAL and spawn-capable, behind `--backend cmux`/`FM_BACKEND=cmux`/
23
+ # `config/backend`, and behind runtime auto-detection when firstmate itself is
24
+ # running inside a cmux-spawned terminal (primary CMUX_WORKSPACE_ID marker, or
25
+ # the documented macOS fallback signals when cmux's claude wrapper strips that
26
+ # marker) with no explicit backend setting - unlike Orca, which stays
27
+ # never-auto-detected because it also owns the task worktree; see
28
+ # docs/cmux-backend.md for its empirical basis.
29
+ # Codex App is intentionally not in the known set yet.
30
+ # docs/codex-app-backend.md owns that blocked backend contract.
31
+ #
32
+ # Compatibility contract: a task's meta may omit `backend=`; every reader here
33
+ # treats that as `tmux` (fm_backend_of_meta), and fm-spawn.sh does not write
34
+ # `backend=tmux` for a default-backend task, so existing and newly spawned
35
+ # default-path metas stay byte-identical. Only a task spawned on a non-tmux
36
+ # spawn-capable backend, currently experimental herdr, zellij, orca, or cmux,
37
+ # carries an explicit `backend=` line.
38
+ #
39
+ # Event-source framing (herdr-addendum "Events as the core abstraction"): a
40
+ # backend's supervision surface is conceptually an EVENT SOURCE - it produces
41
+ # task events (status-changed, went-stale, exited) that map onto firstmate's
42
+ # existing signal/stale/check/heartbeat wake vocabulary. The tmux adapter has
43
+ # no native event push, so fm-watch.sh's poll loop over the pull primitives
44
+ # below (capture, list-live, busy-state via regex) IS the default event-source
45
+ # implementation that synthesizes those events; P1 only names that seam, it
46
+ # does not change the loop's behavior. The pull primitives also stay available
47
+ # on their own for on-demand reads (fm-peek.sh, fm-crew-state.sh).
48
+
49
+ FM_BACKEND_SCRIPT=${BASH_SOURCE[0]:-$0}
50
+ FM_BACKEND_LIB_DIR="$(cd "$(dirname "$FM_BACKEND_SCRIPT")" && pwd)"
51
+ unset FM_BACKEND_SCRIPT
52
+ FM_BACKEND_DEFAULT_ROOT="$(cd "$FM_BACKEND_LIB_DIR/.." && pwd)"
53
+ FM_ROOT="${FM_ROOT_OVERRIDE:-${FM_ROOT:-$FM_BACKEND_DEFAULT_ROOT}}"
54
+ FM_HOME="${FM_HOME:-${FM_ROOT_OVERRIDE:-$FM_ROOT}}"
55
+ FM_BACKEND_CONFIG_DIR="${FM_CONFIG_OVERRIDE:-$FM_HOME/config}"
56
+
57
+ # Verified backend adapters. Extend only after a backend gets its own
58
+ # bin/backends/<name>.sh and empirical verification, mirroring AGENTS.md
59
+ # section 4's harness-verification discipline. herdr is EXPERIMENTAL (P2;
60
+ # data/fm-backend-design-d7/herdr-addendum.md) - verified against the real
61
+ # v0.7.1/protocol-14 binary (data/fm-backend-design-d7/herdr-verification-p2.md)
62
+ # but newer than tmux's long-proven default path. zellij is EXPERIMENTAL (P3;
63
+ # data/fm-backend-design-d7/report.md "Zellij Backend") - verified against the
64
+ # real 0.44.0 binary (docs/zellij-backend.md). orca is EXPERIMENTAL and
65
+ # spawn-capable; unlike tmux/herdr/zellij it is also the worktree provider.
66
+ # cmux is EXPERIMENTAL and spawn-capable, session-provider-only like
67
+ # herdr/zellij - verified against the real 0.64.17 binary (docs/cmux-backend.md).
68
+ # codex-app remains deliberately absent; see docs/codex-app-backend.md.
69
+ FM_BACKEND_KNOWN="tmux herdr zellij orca cmux"
70
+ FM_BACKEND_SPAWN="tmux herdr zellij orca cmux"
71
+
72
+ # fm_backend_list_contains: whitespace-delimited membership without relying on
73
+ # shell word splitting. fm-backend.sh is normally sourced by bash scripts, but
74
+ # zsh diagnostics can source it too, so backend-name matching must stay portable.
75
+ fm_backend_list_contains() { # <list> <name>
76
+ local list=$1 name=$2
77
+ case "$name" in
78
+ *[[:space:]]*) return 1 ;;
79
+ esac
80
+ case " $list " in
81
+ *" $name "*) return 0 ;;
82
+ esac
83
+ return 1
84
+ }
85
+
86
+ fm_backend_is_known() { # <name>
87
+ fm_backend_list_contains "$FM_BACKEND_KNOWN" "$1"
88
+ }
89
+
90
+ # fm_backend_detect: detect the runtime firstmate itself is CURRENTLY executing
91
+ # inside, from verified environment markers (mirrors bin/fm-harness.sh's
92
+ # env-marker detection layer for harnesses). Prints the detected backend name
93
+ # and returns 0, or returns 1 when nothing is detected. Nesting resolves
94
+ # INNERMOST-first: tmux sets $TMUX in every process running inside it, even a
95
+ # tmux started inside a herdr pane, so $TMUX is checked first and wins over
96
+ # HERDR_ENV=1 in that nested case. herdr injects HERDR_ENV=1 (plus
97
+ # HERDR_SOCKET_PATH/HERDR_PANE_ID) into every process it manages a pane for;
98
+ # HERDR_ENV=1 alone (no $TMUX) selects herdr. cmux injects CMUX_WORKSPACE_ID
99
+ # (plus CMUX_SURFACE_ID/CMUX_SOCKET_PATH and the legacy CMUX_TAB_ID/
100
+ # CMUX_PANEL_ID aliases) into every terminal surface it spawns - verified from
101
+ # the shipped source (`TerminalSurface+StartupEnvironment.swift`'s
102
+ # `applyManagedCmuxContextEnvironment`, which marks all five keys
103
+ # `protectedKeys`, i.e. non-overridable) and corroborated by cmux's own CLI
104
+ # (`cmux_open.swift`) reading `CMUX_WORKSPACE_ID`/`CMUX_SURFACE_ID` as its own
105
+ # ambient-target fallback, exactly how `$TMUX` and `HERDR_ENV` work for their
106
+ # backends. CMUX_WORKSPACE_ID, not CMUX_SOCKET_PATH, is the chosen marker:
107
+ # CMUX_SOCKET_PATH is independently documented as a user-settable override for
108
+ # pointing the CLI at a non-default socket, so its mere presence would not
109
+ # reliably mean "running inside a cmux-spawned terminal" the way
110
+ # CMUX_WORKSPACE_ID does. cmux is checked LAST because it is a terminal
111
+ # application (the outermost layer, like iTerm2/Terminal.app), not a session
112
+ # multiplexer - both tmux and herdr can run nested inside a cmux-provided
113
+ # shell, but cmux cannot run nested inside either of them, so a tmux or herdr
114
+ # marker set alongside CMUX_WORKSPACE_ID always means that multiplexer is the
115
+ # innermost, currently-executing layer and must win.
116
+ #
117
+ # cmux FALLBACK signals (docs/cmux-backend.md "Runtime auto-detection" owns
118
+ # the empirical record): cmux's bundled `claude` PATH shim routes through
119
+ # cmux-claude-wrapper, whose passthrough path unsets every CMUX_* variable
120
+ # before exec'ing the real binary - so a claude-harness firstmate launched in
121
+ # a cmux tab can have NO CMUX_WORKSPACE_ID at all. When that primary marker is
122
+ # absent (and only then), two macOS-only fallback signals are consulted:
123
+ # 1. __CFBundleIdentifier == com.cmuxterm.app - LaunchServices' app-identity
124
+ # env var, inherited by every process a cmux tab spawns and NOT stripped
125
+ # by the wrapper (it only unsets CMUX_*, TERMINFO, and CLAUDECODE).
126
+ # Authoritative in the common wrapper-strip case, but also inherited into
127
+ # every pane of a tmux server started from a cmux tab - the $TMUX check
128
+ # winning FIRST is what keeps that false positive absorbed.
129
+ # 2. Process ancestry reaching the running cmux app (resolved by bundle id
130
+ # via lsappinfo, plus a bundle-shaped `ps` comm match so the install
131
+ # location is never hardcoded). Authoritative when the environment was
132
+ # scrubbed entirely (no bundle id to inherit); NOT usable from inside
133
+ # tmux, where the tmux server reparents to launchd and the chain never
134
+ # reaches cmux - which is fine, because $TMUX already won there.
135
+ # Callers needing the winning signal read FM_BACKEND_DETECT_SIGNAL (set to
136
+ # TMUX, HERDR_ENV, CMUX_WORKSPACE_ID, bundle-id, or ancestry) and
137
+ # FM_BACKEND_DETECTED after a direct (non-command-substitution) call.
138
+ FM_BACKEND_CMUX_BUNDLE_ID="com.cmuxterm.app"
139
+
140
+ fm_backend_detect() {
141
+ FM_BACKEND_DETECTED=""
142
+ FM_BACKEND_DETECT_SIGNAL=""
143
+ if [ -n "${TMUX:-}" ]; then
144
+ FM_BACKEND_DETECTED=tmux
145
+ FM_BACKEND_DETECT_SIGNAL=TMUX
146
+ printf 'tmux'
147
+ return 0
148
+ fi
149
+ if [ "${HERDR_ENV:-}" = "1" ]; then
150
+ FM_BACKEND_DETECTED=herdr
151
+ FM_BACKEND_DETECT_SIGNAL=HERDR_ENV
152
+ printf 'herdr'
153
+ return 0
154
+ fi
155
+ if [ -n "${CMUX_WORKSPACE_ID:-}" ]; then
156
+ FM_BACKEND_DETECTED=cmux
157
+ FM_BACKEND_DETECT_SIGNAL=CMUX_WORKSPACE_ID
158
+ printf 'cmux'
159
+ return 0
160
+ fi
161
+ if fm_backend_detect_cmux_fallback; then
162
+ FM_BACKEND_DETECTED=cmux
163
+ printf 'cmux'
164
+ return 0
165
+ fi
166
+ return 1
167
+ }
168
+
169
+ # fm_backend_detect_cmux_fallback: the two macOS-only cmux fallback signals
170
+ # (see fm_backend_detect's header comment). Sets FM_BACKEND_DETECT_SIGNAL to
171
+ # bundle-id or ancestry on success. Cheap-first: the bundle-id check is a pure
172
+ # env read; the ancestry walk (subprocess-per-hop) runs only when it misses.
173
+ fm_backend_detect_cmux_fallback() {
174
+ [ "$(uname 2>/dev/null)" = Darwin ] || return 1
175
+ if [ "${__CFBundleIdentifier:-}" = "$FM_BACKEND_CMUX_BUNDLE_ID" ]; then
176
+ FM_BACKEND_DETECT_SIGNAL=bundle-id
177
+ return 0
178
+ fi
179
+ if fm_backend_detect_cmux_app_is_ancestor; then
180
+ FM_BACKEND_DETECT_SIGNAL=ancestry
181
+ return 0
182
+ fi
183
+ return 1
184
+ }
185
+
186
+ # fm_backend_detect_cmux_app_pid: the running cmux app's pid, resolved by
187
+ # bundle id via lsappinfo (`"pid"=<n>`), or failure when lsappinfo is missing,
188
+ # errors, or the app is not running (lsappinfo prints nothing, exit 0).
189
+ fm_backend_detect_cmux_app_pid() {
190
+ command -v lsappinfo >/dev/null 2>&1 || return 1
191
+ local out pid
192
+ out=$(lsappinfo info -only pid -app "$FM_BACKEND_CMUX_BUNDLE_ID" 2>/dev/null) || return 1
193
+ pid=${out##*=}
194
+ pid=$(printf '%s' "$pid" | tr -d '[:space:]"')
195
+ case "$pid" in ''|*[!0-9]*) return 1 ;; esac
196
+ printf '%s' "$pid"
197
+ }
198
+
199
+ # fm_backend_detect_cmux_app_is_ancestor: walk this process's parent chain and
200
+ # report whether it reaches the cmux app - matching either the lsappinfo-
201
+ # resolved pid (bundle id, no path assumption) or a bundle-shaped comm path
202
+ # (`*/cmux.app/Contents/MacOS/cmux`, any install location) when lsappinfo
203
+ # could not resolve one. Stops at launchd (ppid 1), where a tmux server that
204
+ # was started from a cmux tab has already reparented - ancestry can never
205
+ # false-positive from inside tmux.
206
+ fm_backend_detect_cmux_app_is_ancestor() {
207
+ local cmux_pid pid ppid comm hops=0
208
+ cmux_pid=$(fm_backend_detect_cmux_app_pid) || cmux_pid=""
209
+ pid=$$
210
+ while [ "$hops" -lt 32 ]; do
211
+ if [ -n "$cmux_pid" ] && [ "$pid" = "$cmux_pid" ]; then
212
+ return 0
213
+ fi
214
+ comm=$(ps -o comm= -p "$pid" 2>/dev/null) || comm=""
215
+ comm="${comm#"${comm%%[![:space:]]*}"}"
216
+ comm="${comm%"${comm##*[![:space:]]}"}"
217
+ [ -n "$comm" ] || return 1
218
+ case "$comm" in
219
+ */cmux.app/Contents/MacOS/cmux) return 0 ;;
220
+ esac
221
+ ppid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d '[:space:]')
222
+ case "$ppid" in ''|*[!0-9]*) return 1 ;; esac
223
+ [ "$ppid" -gt 1 ] || return 1
224
+ pid=$ppid
225
+ hops=$((hops + 1))
226
+ done
227
+ return 1
228
+ }
229
+
230
+ # fm_backend_name: resolve the ACTIVE backend for a NEW spawn, absent an
231
+ # explicit per-task override. Precedence: FM_BACKEND env, then config/backend
232
+ # (a single word on its first non-empty line, mirroring config/crew-harness),
233
+ # then runtime auto-detection (fm_backend_detect), then default tmux. A
234
+ # per-task `--backend` flag is parsed by the caller (fm-spawn.sh) and takes
235
+ # precedence over this resolution entirely; it is not read here. Auto-detect
236
+ # fires only when nothing was explicitly configured, so an explicit setting
237
+ # always wins. Selecting herdr or cmux via auto-detect prints one loud stderr
238
+ # notice (both are experimental); auto-detecting tmux stays silent - it is
239
+ # today's default-path behavior and callers must see zero change. The cmux
240
+ # notice names the winning signal, so a fallback-detected cmux (bundle id or
241
+ # ancestry, after the claude wrapper stripped CMUX_WORKSPACE_ID) is visibly
242
+ # distinct from the primary-marker case.
243
+ fm_backend_name() {
244
+ local line v detected marker
245
+ if [ -n "${FM_BACKEND:-}" ]; then
246
+ printf '%s' "$FM_BACKEND"
247
+ return 0
248
+ fi
249
+ if [ -f "$FM_BACKEND_CONFIG_DIR/backend" ]; then
250
+ while IFS= read -r line || [ -n "$line" ]; do
251
+ v=$(printf '%s' "$line" | tr -d '[:space:]')
252
+ if [ -n "$v" ]; then
253
+ printf '%s' "$v"
254
+ return 0
255
+ fi
256
+ done < "$FM_BACKEND_CONFIG_DIR/backend"
257
+ fi
258
+ # Called directly (not in a command substitution) so the detect signal
259
+ # globals survive into the notice below.
260
+ if fm_backend_detect >/dev/null; then
261
+ detected=$FM_BACKEND_DETECTED
262
+ if [ "$detected" = herdr ]; then
263
+ echo "NOTICE: auto-detected herdr runtime (HERDR_ENV=1) - spawning into the EXPERIMENTAL herdr backend. Set config/backend or pass --backend tmux to opt out." >&2
264
+ fi
265
+ if [ "$detected" = cmux ]; then
266
+ case "$FM_BACKEND_DETECT_SIGNAL" in
267
+ bundle-id) marker="FALLBACK signal __CFBundleIdentifier=$FM_BACKEND_CMUX_BUNDLE_ID; CMUX_WORKSPACE_ID absent, stripped by cmux's bundled claude wrapper" ;;
268
+ ancestry) marker="FALLBACK signal process-ancestry reaching the running cmux app; CMUX_WORKSPACE_ID absent, stripped by cmux's bundled claude wrapper" ;;
269
+ *) marker="CMUX_WORKSPACE_ID" ;;
270
+ esac
271
+ echo "NOTICE: auto-detected cmux runtime ($marker) - spawning into the EXPERIMENTAL cmux backend. Set config/backend or pass --backend tmux to opt out." >&2
272
+ fi
273
+ printf '%s' "$detected"
274
+ return 0
275
+ fi
276
+ printf 'tmux'
277
+ }
278
+
279
+ # fm_backend_validate: refuse an unknown backend LOUDLY. Silent on success.
280
+ fm_backend_validate() { # <name>
281
+ local name=$1
282
+ if ! fm_backend_is_known "$name"; then
283
+ echo "error: unknown backend '$name' (known: $FM_BACKEND_KNOWN)" >&2
284
+ return 1
285
+ fi
286
+ return 0
287
+ }
288
+
289
+ fm_backend_validate_spawn() { # <name>
290
+ local name=$1
291
+ fm_backend_validate "$name" || return 1
292
+ fm_backend_list_contains "$FM_BACKEND_SPAWN" "$name" && return 0
293
+ echo "error: backend '$name' does not support task spawning yet (spawn-supported: $FM_BACKEND_SPAWN)" >&2
294
+ return 1
295
+ }
296
+
297
+ # fm_backend_required_tools: the backend-SPECIFIC CLI tools a firstmate home on
298
+ # <backend> genuinely requires, beyond firstmate's universal toolchain (owned by
299
+ # docs/configuration.md "Toolchain" and bootstrap's COMMON list). This is the
300
+ # single owner of the per-backend dependency delta, so bootstrap follows the
301
+ # RESOLVED backend instead of demanding an inactive backend's tools. Each set is:
302
+ # - the session-provider CLI itself (tmux/herdr/zellij/orca/cmux);
303
+ # - jq, for the JSON-emitting experimental adapters (herdr, zellij, cmux) whose
304
+ # spawn/liveness paths parse the backend's JSON output (see each adapter's
305
+ # tool check, e.g. fm_backend_herdr_tool_check);
306
+ # - the treehouse worktree provider for every session-provider-only backend
307
+ # (tmux, herdr, zellij, cmux); orca owns its own task worktree and terminal,
308
+ # so it drops both treehouse and any other backend's session CLI.
309
+ # Prints a single space-separated line and returns 0 for a known backend; returns
310
+ # 1 and prints nothing for an unknown backend.
311
+ fm_backend_required_tools() { # <backend>
312
+ case "$1" in
313
+ tmux) printf '%s' 'tmux treehouse' ;;
314
+ herdr) printf '%s' 'herdr jq treehouse' ;;
315
+ zellij) printf '%s' 'zellij jq treehouse' ;;
316
+ cmux) printf '%s' 'cmux jq treehouse' ;;
317
+ orca) printf '%s' 'orca' ;;
318
+ *) return 1 ;;
319
+ esac
320
+ }
321
+
322
+ fm_backend_required_tool_available() { # <backend> <tool>
323
+ local backend=$1 tool=$2 required
324
+ required=$(fm_backend_required_tools "$backend") || return 1
325
+ fm_backend_list_contains "$required" "$tool" || return 1
326
+ case "$backend:$tool" in
327
+ cmux:cmux)
328
+ fm_backend_source cmux >/dev/null 2>&1 || return 1
329
+ fm_backend_cmux_bin >/dev/null 2>&1
330
+ ;;
331
+ *) command -v "$tool" >/dev/null 2>&1 ;;
332
+ esac
333
+ }
334
+
335
+ # fm_meta_get: the LAST value of `key=` in <meta-file>, or empty (never
336
+ # errors) if the file or key is absent. Mirrors the ad hoc `grep '^key=' |
337
+ # tail -1 | cut -d= -f2-` snippet every fm-*.sh script used to repeat inline.
338
+ fm_meta_get() { # <meta-file> <key>
339
+ local meta=$1 key=$2
340
+ [ -f "$meta" ] || return 0
341
+ grep "^$key=" "$meta" 2>/dev/null | tail -1 | cut -d= -f2- || true
342
+ }
343
+
344
+ # fm_backend_of_meta: the backend recorded in <meta-file>, defaulting to
345
+ # `tmux` when the field is absent - the P1 compatibility contract.
346
+ fm_backend_of_meta() { # <meta-file>
347
+ local v
348
+ v=$(fm_meta_get "$1" backend)
349
+ printf '%s' "${v:-tmux}"
350
+ }
351
+
352
+ fm_backend_target_of_meta() { # <meta-file>
353
+ local meta=$1 backend terminal window
354
+ backend=$(fm_backend_of_meta "$meta")
355
+ if [ "$backend" = orca ]; then
356
+ terminal=$(fm_meta_get "$meta" terminal)
357
+ [ -n "$terminal" ] && { printf '%s' "$terminal"; return 0; }
358
+ fi
359
+ window=$(fm_meta_get "$meta" window)
360
+ [ -n "$window" ] && printf '%s' "$window"
361
+ }
362
+
363
+ # fm_backend_validate_task_endpoint: validate a task cleanup record entirely
364
+ # from its durable metadata before any runtime command or cleanup mutation.
365
+ # The validation binds the exact task id, selected backend, target, project,
366
+ # and worktree. New non-tmux records carry endpoint_task_id because their
367
+ # opaque runtime ids do not encode the task label. Legacy tmux records remain
368
+ # valid only when their window name itself is exactly fm-<task-id>.
369
+ # On success, sets FM_BACKEND_VALIDATED_BACKEND and
370
+ # FM_BACKEND_VALIDATED_TARGET. On failure, prints one refusal and returns 1.
371
+ fm_backend_meta_exact_value() { # <meta-file> <key>
372
+ local meta=$1 key=$2 count value
373
+ count=$(grep -c "^$key=" "$meta" 2>/dev/null || true)
374
+ [ "$count" -eq 1 ] || return 1
375
+ value=$(grep "^$key=" "$meta" | cut -d= -f2-)
376
+ [ -n "$value" ] || return 1
377
+ printf '%s' "$value"
378
+ }
379
+
380
+ fm_backend_endpoint_atom_valid() { # <value>
381
+ case "$1" in
382
+ ''|*[!A-Za-z0-9._@%+-]*) return 1 ;;
383
+ esac
384
+ }
385
+
386
+ fm_backend_validate_task_endpoint() { # <meta-file> <task-id>
387
+ local meta=$1 id=$2 backend_count backend window worktree project binding_count binding
388
+ local session pane recorded_session workspace tab terminal worktree_id surface
389
+ FM_BACKEND_VALIDATED_BACKEND=
390
+ FM_BACKEND_VALIDATED_TARGET=
391
+ [ -f "$meta" ] && [ ! -L "$meta" ] || {
392
+ echo "REFUSED: task $id has no regular endpoint metadata at $meta; preserving task state." >&2
393
+ return 1
394
+ }
395
+ case "$id" in ''|*[!A-Za-z0-9._-]*)
396
+ echo "REFUSED: task endpoint identity has an invalid task id; preserving task state." >&2
397
+ return 1
398
+ esac
399
+ window=$(fm_backend_meta_exact_value "$meta" window) || {
400
+ echo "REFUSED: task $id has a missing, empty, or ambiguous window endpoint; preserving task state." >&2
401
+ return 1
402
+ }
403
+ worktree=$(fm_backend_meta_exact_value "$meta" worktree) || {
404
+ echo "REFUSED: task $id has a missing, empty, or ambiguous worktree identity; preserving task state." >&2
405
+ return 1
406
+ }
407
+ project=$(fm_backend_meta_exact_value "$meta" project) || {
408
+ echo "REFUSED: task $id has a missing, empty, or ambiguous project identity; preserving task state." >&2
409
+ return 1
410
+ }
411
+ case "$worktree$project$window" in *$'\n'*|*$'\r'*|*$'\t'*)
412
+ echo "REFUSED: task $id has malformed endpoint metadata; preserving task state." >&2
413
+ return 1
414
+ esac
415
+ backend_count=$(grep -c '^backend=' "$meta" 2>/dev/null || true)
416
+ case "$backend_count" in
417
+ 0) backend=tmux ;;
418
+ 1) backend=$(fm_backend_meta_exact_value "$meta" backend) || backend= ;;
419
+ *) backend= ;;
420
+ esac
421
+ if [ -z "$backend" ] || ! fm_backend_is_known "$backend"; then
422
+ echo "REFUSED: task $id has a missing, ambiguous, or unknown backend identity; preserving task state." >&2
423
+ return 1
424
+ fi
425
+ binding_count=$(grep -c '^endpoint_task_id=' "$meta" 2>/dev/null || true)
426
+ case "$binding_count" in
427
+ 0) binding= ;;
428
+ 1)
429
+ binding=$(fm_backend_meta_exact_value "$meta" endpoint_task_id) || {
430
+ echo "REFUSED: task $id has an empty endpoint task binding; preserving task state." >&2
431
+ return 1
432
+ }
433
+ ;;
434
+ *)
435
+ echo "REFUSED: task $id has an ambiguous endpoint task binding; preserving task state." >&2
436
+ return 1
437
+ ;;
438
+ esac
439
+ if [ -n "$binding" ] && [ "$binding" != "$id" ]; then
440
+ echo "REFUSED: endpoint metadata belongs to task $binding, not $id; preserving task state." >&2
441
+ return 1
442
+ fi
443
+
444
+ case "$backend" in
445
+ tmux)
446
+ session=${window%%:*}
447
+ pane=${window#*:}
448
+ if [ "$pane" = "$window" ] || [ "$pane" != "fm-$id" ] \
449
+ || [ -z "$session" ]; then
450
+ echo "REFUSED: tmux endpoint '$window' is malformed or does not belong to task $id; preserving task state." >&2
451
+ return 1
452
+ fi
453
+ ;;
454
+ herdr)
455
+ [ "$binding" = "$id" ] || {
456
+ echo "REFUSED: legacy Herdr endpoint metadata for task $id lacks an exact task binding; preserving task state." >&2
457
+ return 1
458
+ }
459
+ recorded_session=$(fm_backend_meta_exact_value "$meta" herdr_session) || recorded_session=
460
+ workspace=$(fm_backend_meta_exact_value "$meta" herdr_workspace_id) || workspace=
461
+ tab=$(fm_backend_meta_exact_value "$meta" herdr_tab_id) || tab=
462
+ pane=$(fm_backend_meta_exact_value "$meta" herdr_pane_id) || pane=
463
+ if [ -z "$recorded_session" ] || [ -z "$workspace" ] || [ -z "$tab" ] || [ -z "$pane" ] \
464
+ || [ "$window" != "$recorded_session:$pane" ] \
465
+ || ! fm_backend_endpoint_atom_valid "$recorded_session" \
466
+ || ! fm_backend_endpoint_atom_valid "$workspace" \
467
+ || ! fm_backend_endpoint_atom_valid "${tab//:/_}" \
468
+ || ! fm_backend_endpoint_atom_valid "${pane//:/_}"; then
469
+ echo "REFUSED: Herdr endpoint metadata for task $id is malformed or inconsistent; preserving task state." >&2
470
+ return 1
471
+ fi
472
+ ;;
473
+ zellij)
474
+ [ "$binding" = "$id" ] || {
475
+ echo "REFUSED: legacy Zellij endpoint metadata for task $id lacks an exact task binding; preserving task state." >&2
476
+ return 1
477
+ }
478
+ recorded_session=$(fm_backend_meta_exact_value "$meta" zellij_session) || recorded_session=
479
+ tab=$(fm_backend_meta_exact_value "$meta" zellij_tab_id) || tab=
480
+ pane=$(fm_backend_meta_exact_value "$meta" zellij_pane_id) || pane=
481
+ case "$tab:$pane" in *[!0-9:]*) tab= ;; esac
482
+ if [ -z "$recorded_session" ] || [ -z "$tab" ] || [ -z "$pane" ] \
483
+ || [ "$window" != "$recorded_session:$pane" ] \
484
+ || ! fm_backend_endpoint_atom_valid "$recorded_session"; then
485
+ echo "REFUSED: Zellij endpoint metadata for task $id is malformed or inconsistent; preserving task state." >&2
486
+ return 1
487
+ fi
488
+ ;;
489
+ orca)
490
+ [ "$binding" = "$id" ] || {
491
+ echo "REFUSED: legacy Orca endpoint metadata for task $id lacks an exact task binding; preserving task state." >&2
492
+ return 1
493
+ }
494
+ terminal=$(fm_backend_meta_exact_value "$meta" terminal) || terminal=
495
+ worktree_id=$(fm_backend_meta_exact_value "$meta" orca_worktree_id) || worktree_id=
496
+ [ -n "$terminal" ] || {
497
+ echo "REFUSED: missing terminal in $meta; cannot close Orca endpoint; preserving task state." >&2
498
+ return 1
499
+ }
500
+ [ -n "$worktree_id" ] || {
501
+ echo "REFUSED: missing orca_worktree_id in $meta; cannot remove Orca worktree; preserving task state." >&2
502
+ return 1
503
+ }
504
+ if [ "$window" != "fm-$id" ] \
505
+ || ! fm_backend_endpoint_atom_valid "$terminal" \
506
+ || ! fm_backend_endpoint_atom_valid "$worktree_id"; then
507
+ echo "REFUSED: Orca endpoint metadata for task $id is malformed or inconsistent; preserving task state." >&2
508
+ return 1
509
+ fi
510
+ window=$terminal
511
+ ;;
512
+ cmux)
513
+ [ "$binding" = "$id" ] || {
514
+ echo "REFUSED: legacy cmux endpoint metadata for task $id lacks an exact task binding; preserving task state." >&2
515
+ return 1
516
+ }
517
+ workspace=$(fm_backend_meta_exact_value "$meta" cmux_workspace_id) || workspace=
518
+ surface=$(fm_backend_meta_exact_value "$meta" cmux_surface_id) || surface=
519
+ if [ -z "$workspace" ] || [ -z "$surface" ] || [ "$window" != "$workspace:$surface" ] \
520
+ || ! fm_backend_endpoint_atom_valid "$workspace" \
521
+ || ! fm_backend_endpoint_atom_valid "$surface"; then
522
+ echo "REFUSED: cmux endpoint metadata for task $id is malformed or inconsistent; preserving task state." >&2
523
+ return 1
524
+ fi
525
+ ;;
526
+ esac
527
+ # shellcheck disable=SC2034 # Output globals are consumed by sourcing callers.
528
+ FM_BACKEND_VALIDATED_BACKEND=$backend
529
+ # shellcheck disable=SC2034 # Output globals are consumed by sourcing callers.
530
+ FM_BACKEND_VALIDATED_TARGET=$window
531
+ return 0
532
+ }
533
+
534
+ fm_backend_meta_for_window() { # <target> <state-dir>
535
+ local target=$1 state=$2 meta window terminal
536
+ for meta in "$state"/*.meta; do
537
+ [ -e "$meta" ] || continue
538
+ window=$(fm_meta_get "$meta" window)
539
+ terminal=$(fm_meta_get "$meta" terminal)
540
+ { [ -n "$window" ] && [ "$window" = "$target" ]; } || { [ -n "$terminal" ] && [ "$terminal" = "$target" ]; } || continue
541
+ printf '%s' "$meta"
542
+ return 0
543
+ done
544
+ return 1
545
+ }
546
+
547
+ fm_backend_task_id_for_selector() { # <raw-target> <state-dir>
548
+ local raw=$1 state=$2 id
549
+ case "$raw" in
550
+ *:*) return 1 ;;
551
+ esac
552
+ if [ -f "$state/$raw.meta" ]; then
553
+ printf '%s' "$raw"
554
+ return 0
555
+ fi
556
+ case "$raw" in
557
+ fm-*)
558
+ id=${raw#fm-}
559
+ [ -f "$state/$id.meta" ] || return 1
560
+ printf '%s' "$id"
561
+ return 0
562
+ ;;
563
+ esac
564
+ return 1
565
+ }
566
+
567
+ fm_backend_meta_for_selector() { # <raw-target> <state-dir>
568
+ local raw=$1 state=$2 id
569
+ id=$(fm_backend_task_id_for_selector "$raw" "$state") || return 1
570
+ printf '%s/%s.meta' "$state" "$id"
571
+ }
572
+
573
+ fm_backend_of_selector() { # <raw-target> <resolved-target> <state-dir>
574
+ local raw=$1 resolved=$2 state=$3 meta
575
+ meta=$(fm_backend_meta_for_selector "$raw" "$state" 2>/dev/null || true)
576
+ [ -n "$meta" ] && { fm_backend_of_meta "$meta"; return 0; }
577
+ if [ -n "$resolved" ]; then
578
+ meta=$(fm_backend_meta_for_window "$resolved" "$state" 2>/dev/null || true)
579
+ [ -n "$meta" ] && { fm_backend_of_meta "$meta"; return 0; }
580
+ fi
581
+ printf 'tmux'
582
+ }
583
+
584
+ fm_backend_expected_label_of_selector() { # <raw-target> <state-dir>
585
+ local raw=$1 state=$2 id
586
+ id=$(fm_backend_task_id_for_selector "$raw" "$state" 2>/dev/null || true)
587
+ [ -n "$id" ] && printf 'fm-%s' "$id"
588
+ return 0
589
+ }
590
+
591
+ # fm_backend_source: source the named backend's adapter file, once per shell.
592
+ # Each adapter is an independently linted canonical root. The /dev/null source
593
+ # boundaries keep runtime dispatch from importing all five adapter ASTs into
594
+ # every dispatcher consumer while preserving the runtime source operations.
595
+ fm_backend_source() { # <name>
596
+ local name=$1
597
+ fm_backend_validate "$name" || return 1
598
+ case "$name" in
599
+ tmux)
600
+ if [ -z "${_FM_BACKEND_TMUX_SOURCED:-}" ]; then
601
+ # shellcheck source=/dev/null
602
+ . "$FM_BACKEND_LIB_DIR/backends/tmux.sh" || return 1
603
+ _FM_BACKEND_TMUX_SOURCED=1
604
+ fi
605
+ ;;
606
+ herdr)
607
+ if [ -z "${_FM_BACKEND_HERDR_SOURCED:-}" ]; then
608
+ # shellcheck source=/dev/null
609
+ . "$FM_BACKEND_LIB_DIR/backends/herdr.sh" || return 1
610
+ _FM_BACKEND_HERDR_SOURCED=1
611
+ fi
612
+ ;;
613
+ zellij)
614
+ if [ -z "${_FM_BACKEND_ZELLIJ_SOURCED:-}" ]; then
615
+ # shellcheck source=/dev/null
616
+ . "$FM_BACKEND_LIB_DIR/backends/zellij.sh" || return 1
617
+ _FM_BACKEND_ZELLIJ_SOURCED=1
618
+ fi
619
+ ;;
620
+ orca)
621
+ if [ -z "${_FM_BACKEND_ORCA_SOURCED:-}" ]; then
622
+ # shellcheck source=/dev/null
623
+ . "$FM_BACKEND_LIB_DIR/backends/orca.sh" || return 1
624
+ _FM_BACKEND_ORCA_SOURCED=1
625
+ fi
626
+ ;;
627
+ cmux)
628
+ if [ -z "${_FM_BACKEND_CMUX_SOURCED:-}" ]; then
629
+ # shellcheck source=/dev/null
630
+ . "$FM_BACKEND_LIB_DIR/backends/cmux.sh" || return 1
631
+ _FM_BACKEND_CMUX_SOURCED=1
632
+ fi
633
+ ;;
634
+ esac
635
+ }
636
+
637
+ # fm_backend_resolve_selector: resolve a raw fm-send.sh/fm-peek.sh style
638
+ # selector to a live session-provider target. Four forms, in order:
639
+ # target with ":" used as-is (the escape hatch for a window/pane outside
640
+ # this firstmate home) - backend-independent, a literal string.
641
+ # exact task id routed through <state-dir>/<id>.meta's backend target
642
+ # (`window=` normally, `terminal=` for Orca) -
643
+ # backend-independent, a stored value, NOT re-verified
644
+ # against a live backend inventory (matches today's
645
+ # behavior: tmux window names can be trusted from meta
646
+ # without a live re-check).
647
+ # "fm-<id>" legacy task window label fallback routed through
648
+ # <state-dir>/<id>.meta when no exact
649
+ # <state-dir>/fm-<id>.meta exists.
650
+ # anything else first matched against recorded `window=`/`terminal=`
651
+ # metadata, then treated as an ad hoc bare window name and
652
+ # resolved by searching the legacy tmux live inventory.
653
+ fm_backend_resolve_selector() { # <raw-target> <state-dir>
654
+ local raw=$1 state=$2 meta window
655
+ case "$raw" in
656
+ *:*)
657
+ printf '%s' "$raw"
658
+ return 0
659
+ ;;
660
+ esac
661
+ meta=$(fm_backend_meta_for_selector "$raw" "$state" 2>/dev/null || true)
662
+ if [ -n "$meta" ]; then
663
+ window=$(fm_backend_target_of_meta "$meta")
664
+ [ -n "$window" ] || { echo "error: no backend target recorded in $meta" >&2; return 1; }
665
+ printf '%s' "$window"
666
+ return 0
667
+ fi
668
+ case "$raw" in
669
+ fm-*)
670
+ echo "error: no metadata for $raw in $state; pass session:window to target a window outside this firstmate home" >&2
671
+ return 1
672
+ ;;
673
+ *)
674
+ meta=$(fm_backend_meta_for_window "$raw" "$state" 2>/dev/null || true)
675
+ if [ -n "$meta" ]; then
676
+ window=$(fm_backend_target_of_meta "$meta")
677
+ [ -n "$window" ] || { echo "error: no backend target recorded in $meta" >&2; return 1; }
678
+ printf '%s' "$window"
679
+ return 0
680
+ fi
681
+ fm_backend_source tmux || return 1
682
+ fm_backend_tmux_resolve_bare_selector "$raw"
683
+ ;;
684
+ esac
685
+ }
686
+
687
+ # --- generic per-op dispatch -------------------------------------------------
688
+ #
689
+ # Thin case-dispatch wrappers so a caller names an operation and a backend
690
+ # rather than hand-writing `case "$backend" in tmux) fm_backend_tmux_x ;; esac`
691
+ # at every call site. Each verified backend adds its own arm here, without
692
+ # changing call sites.
693
+
694
+ # fm_backend_capture: bounded plain-text session capture.
695
+ fm_backend_capture() { # <backend> <target> <lines> [expected-label]
696
+ local backend=$1
697
+ shift
698
+ fm_backend_source "$backend" || return 1
699
+ case "$backend" in
700
+ tmux) fm_backend_tmux_capture "$@" ;;
701
+ herdr) fm_backend_herdr_capture "$@" ;;
702
+ zellij) fm_backend_zellij_capture "$@" ;;
703
+ orca) fm_backend_orca_capture "$@" ;;
704
+ cmux) fm_backend_cmux_capture "$@" ;;
705
+ *) echo "error: no capture implementation for backend '$backend'" >&2; return 1 ;;
706
+ esac
707
+ }
708
+
709
+ # fm_backend_send_key: one backend-supported named special key.
710
+ fm_backend_send_key() { # <backend> <target> <key> [expected-label]
711
+ local backend=$1
712
+ shift
713
+ fm_backend_source "$backend" || return 1
714
+ case "$backend" in
715
+ tmux) fm_backend_tmux_send_key "$@" ;;
716
+ herdr) fm_backend_herdr_send_key "$@" ;;
717
+ zellij) fm_backend_zellij_send_key "$@" ;;
718
+ orca) fm_backend_orca_send_key "$@" ;;
719
+ cmux) fm_backend_cmux_send_key "$@" ;;
720
+ *) echo "error: no send-key implementation for backend '$backend'" >&2; return 1 ;;
721
+ esac
722
+ }
723
+
724
+ # fm_backend_send_text_submit: type text once, then submit and verify,
725
+ # retrying only the submission (never retyping). Echoes the backend's
726
+ # proof-carrying verdict; callers require exact empty for confirmed delivery.
727
+ fm_backend_send_text_submit() { # <backend> <target> <text> <retries> <enter-sleep> <settle> [expected-label]
728
+ local backend=$1
729
+ shift
730
+ fm_backend_source "$backend" || return 1
731
+ case "$backend" in
732
+ tmux) fm_backend_tmux_send_text_submit "$@" ;;
733
+ herdr) fm_backend_herdr_send_text_submit "$@" ;;
734
+ zellij) fm_backend_zellij_send_text_submit "$@" ;;
735
+ orca) fm_backend_orca_send_text_submit "$@" ;;
736
+ cmux) fm_backend_cmux_send_text_submit "$@" ;;
737
+ *) echo "error: no send-text implementation for backend '$backend'" >&2; return 1 ;;
738
+ esac
739
+ }
740
+
741
+ # fm_backend_kill: remove the task's session endpoint (best-effort; a
742
+ # nonexistent/already-gone target is not an error - callers already swallow
743
+ # failures here exactly as the inline `tmux kill-window ... || true` did).
744
+ fm_backend_kill() { # <backend> <target>
745
+ local backend=$1
746
+ shift
747
+ [ -n "${1:-}" ] || { echo "error: refusing empty backend kill target" >&2; return 1; }
748
+ fm_backend_source "$backend" || return 1
749
+ case "$backend" in
750
+ tmux) fm_backend_tmux_kill "$@" ;;
751
+ herdr) fm_backend_herdr_kill "$@" ;;
752
+ zellij) fm_backend_zellij_kill "$@" ;;
753
+ orca) fm_backend_orca_kill "$@" ;;
754
+ cmux) fm_backend_cmux_kill "$@" ;;
755
+ *) echo "error: no kill implementation for backend '$backend'" >&2; return 1 ;;
756
+ esac
757
+ }
758
+
759
+ fm_backend_remove_worktree() { # <backend> <worktree-id>
760
+ local backend=$1
761
+ shift
762
+ fm_backend_source "$backend" || return 1
763
+ case "$backend" in
764
+ orca) fm_backend_orca_remove_worktree "$@" ;;
765
+ *) echo "error: backend '$backend' does not own task worktrees" >&2; return 1 ;;
766
+ esac
767
+ }
768
+
769
+ fm_backend_worktree_path() { # <backend> <worktree-id>
770
+ local backend=$1
771
+ shift
772
+ fm_backend_source "$backend" || return 1
773
+ case "$backend" in
774
+ orca) fm_backend_orca_worktree_path "$@" ;;
775
+ *) echo "error: backend '$backend' does not own task worktrees" >&2; return 1 ;;
776
+ esac
777
+ }
778
+
779
+ # fm_backend_busy_state: semantic busy/idle/unknown for backends that expose
780
+ # native agent-state (herdr-addendum "busy state" row - the first backend
781
+ # where this gets real semantics beyond pane-regex). Backends with no such
782
+ # primitive (tmux) report unknown. Callers own the fallback policy: fm-watch.sh
783
+ # uses unknown as the cue for harness-scoped pane-tail detection, while
784
+ # fm-crew-state.sh also corroborates native idle verdicts with the recorded
785
+ # harness's signature before treating a no-run crew as not busy.
786
+ fm_backend_busy_state() { # <backend> <target>
787
+ local backend=$1
788
+ shift
789
+ fm_backend_source "$backend" || { printf 'unknown'; return 0; }
790
+ case "$backend" in
791
+ herdr) fm_backend_herdr_busy_state "$@" ;;
792
+ *) printf 'unknown' ;;
793
+ esac
794
+ }
795
+
796
+ # fm_backend_composer_state: classify the composer/input area of <target> as
797
+ # empty|pending|pending-unproven|unknown for callers that need a pre-submit
798
+ # input guard, a submit acknowledgement, or a launch-readiness check. It is
799
+ # exposed so a caller other than the send path (the away-mode daemon's
800
+ # supervisor-pane pending-input guard in bin/fm-supervise-daemon.sh, and
801
+ # fm-spawn.sh's kimi readiness/delivery checks) can ask the same question
802
+ # without duplicating per-backend composer reading. Every adapter's named
803
+ # classifier is a THIN wrapper - capture plus a capability descriptor fed to
804
+ # the one shared shape owner (bin/fm-composer-lib.sh,
805
+ # fm_composer_classify_screen) - so no backend can hold a private shape
806
+ # assumption; zellij's classifier reads `dump-screen --ansi`, which replaced
807
+ # its old no-classifier content-diff reporting.
808
+ fm_backend_composer_state() { # <backend> <target> [expected-label] -> empty|pending|pending-unproven|unknown
809
+ local backend=$1
810
+ shift
811
+ fm_backend_source "$backend" || { printf 'unknown'; return 0; }
812
+ case "$backend" in
813
+ tmux) fm_tmux_composer_state "$@" ;;
814
+ herdr) fm_backend_herdr_composer_state "$@" ;;
815
+ orca) fm_backend_orca_composer_state "$@" ;;
816
+ cmux) fm_backend_cmux_composer_state "$@" ;;
817
+ zellij) fm_backend_zellij_composer_state "$@" ;;
818
+ *) printf 'unknown' ;;
819
+ esac
820
+ }
821
+
822
+ # fm_backend_target_exists: cheap, READ-ONLY existence check - does the
823
+ # recorded TARGET endpoint still exist on BACKEND? Never starts a server or
824
+ # session: for herdr this deliberately queries the pane directly instead of
825
+ # going through fm_backend_herdr_target_ready (which auto-starts the herdr
826
+ # server as a side effect via fm_backend_herdr_server_ensure - fine for an
827
+ # operation that is about to use the pane, wrong for a passive liveness
828
+ # probe). A gone tmux window or an unqueryable herdr pane (server down, pane
829
+ # closed), missing zellij pane, or unreadable Orca terminal simply fails, which
830
+ # IS "does not exist" for this purpose.
831
+ # Mirrors fm-crew-state.sh's pane_readable check; exists here as one shared
832
+ # primitive so callers that only need a fast alive/dead read (recovery
833
+ # digests, the session-start fleet digest) do not re-derive it inline.
834
+ fm_backend_target_exists() { # <backend> <target> [expected-label]
835
+ local backend=$1 target=$2 expected_label=${3:-} session pane
836
+ case "$backend" in
837
+ tmux)
838
+ tmux display-message -p -t "$target" '#{pane_id}' >/dev/null 2>&1
839
+ ;;
840
+ herdr)
841
+ fm_backend_source herdr || return 1
842
+ session=${target%%:*}
843
+ pane=${target#*:}
844
+ [ -n "$session" ] && [ -n "$pane" ] && [ "$pane" != "$target" ] || return 1
845
+ # fm_backend_herdr_cli (not a raw HERDR_SESSION-only call): verified
846
+ # empirically (docs/herdr-backend.md "Session targeting") that the bare
847
+ # env var alone is NOT reliably honored once another herdr server is
848
+ # already bound on the machine - it silently queries whatever server IS
849
+ # running instead. fm_backend_herdr_cli appends the required --session
850
+ # flag on top, so this check is correctly scoped even when the caller's
851
+ # own ambient session (e.g. the primary firstmate's default session) is
852
+ # a DIFFERENT one than the target's.
853
+ fm_backend_herdr_cli "$session" pane get "$pane" >/dev/null 2>&1
854
+ ;;
855
+ zellij)
856
+ fm_backend_source zellij || return 1
857
+ fm_backend_zellij_target_ready "$target" "$expected_label"
858
+ ;;
859
+ orca)
860
+ fm_backend_source orca || return 1
861
+ fm_backend_orca_capture "$target" 1 >/dev/null 2>&1
862
+ ;;
863
+ cmux)
864
+ fm_backend_source cmux || return 1
865
+ fm_backend_cmux_target_ready "$target" "$expected_label"
866
+ ;;
867
+ *)
868
+ return 1
869
+ ;;
870
+ esac
871
+ }
872
+
873
+ # fm_backend_agent_state: the single recovery-grade agent/endpoint state
874
+ # contract. It is deliberately richer than fm_backend_target_exists's cheap
875
+ # pane-presence read and prints exactly one of:
876
+ # alive - a verified harness agent is running.
877
+ # dead - the endpoint exists but confidently has no agent.
878
+ # missing - the recorded endpoint is authoritatively absent.
879
+ # ambiguous - the endpoint exists but its process cannot be attributed.
880
+ # unreadable - a target or inventory read failed or contradicted itself.
881
+ # unverified - this backend has no recovery classifier.
882
+ # Only `dead` and `missing` license recovery. The tmux adapter requires a
883
+ # successful session inventory and returns `missing` only when it omits the
884
+ # exact window; the Herdr adapter reuses its husk
885
+ # classifier. Zellij remains unverified because its secondmate ghost-tab and
886
+ # agent-process recovery path has not been empirically validated. Orca and cmux
887
+ # do not support secondmate spawns.
888
+ fm_backend_agent_state() { # <backend> <target>
889
+ local backend=$1 target=$2
890
+ fm_backend_source "$backend" || { printf 'unverified'; return 0; }
891
+ case "$backend" in
892
+ tmux) fm_backend_tmux_agent_state "$target" ;;
893
+ herdr) fm_backend_herdr_agent_state "$target" ;;
894
+ *) printf 'unverified' ;;
895
+ esac
896
+ }
897
+
898
+ # Backward-compatible three-state view for existing callers. An
899
+ # authoritatively missing endpoint is confidently not a live agent, while every
900
+ # ambiguous, unreadable, or unverified result stays unknown.
901
+ fm_backend_agent_alive() { # <backend> <target>
902
+ case "$(fm_backend_agent_state "$1" "$2")" in
903
+ alive) printf 'alive' ;;
904
+ dead|missing) printf 'dead' ;;
905
+ *) printf 'unknown' ;;
906
+ esac
907
+ }
908
+
909
+ # --- native event push (backend-extensible) ---------------------------------
910
+ #
911
+ # The watcher's event-wait splice (bin/fm-watch.sh) is backend-agnostic: it asks
912
+ # fm_backend_has_push whether a window's backend can push semantic state changes,
913
+ # and for those backends replaces its blind `sleep POLL` with a bounded wait on
914
+ # fm_backend_wait_transition. Every push-capable backend reuses the shared
915
+ # normalized-transition shape and policy table (bin/fm-transition-lib.sh); today
916
+ # only herdr implements the surface (docs/herdr-backend.md "Native
917
+ # pane.agent_status_changed push escalation"). A backend with no native push
918
+ # reports has-push false and returns 2 from the dispatchers below, so the
919
+ # watcher falls back to its poll loop - the permanent fail-closed backstop.
920
+
921
+ # fm_backend_has_push: 0 if <backend> exposes a native transition push stream.
922
+ fm_backend_has_push() { # <backend>
923
+ case "$1" in
924
+ herdr) return 0 ;;
925
+ *) return 1 ;;
926
+ esac
927
+ }
928
+
929
+ # fm_backend_events_capable: 0 if <backend>'s push path is usable for <session>
930
+ # right now (version/schema/reader gate). Non-push backends are never capable.
931
+ # The watcher memoizes this per session so the potentially heavy capability
932
+ # probe is not repeated every poll.
933
+ fm_backend_events_capable() { # <backend> <session>
934
+ local backend=$1
935
+ shift
936
+ fm_backend_has_push "$backend" || return 1
937
+ fm_backend_source "$backend" || return 1
938
+ case "$backend" in
939
+ herdr) fm_backend_herdr_events_capable "$@" ;;
940
+ *) return 1 ;;
941
+ esac
942
+ }
943
+
944
+ # fm_backend_wait_transition: bounded wait for a fresh actionable (blocked)
945
+ # transition on one of <pane_window...> in <session>, up to <timeout_secs>.
946
+ # Prints the normalized transition record and returns 0 on a fresh actionable
947
+ # edge; returns 1 on a clean timeout (the caller has effectively already slept);
948
+ # returns 2 when the event path is unusable (the caller sleeps the budget
949
+ # itself). Non-push backends always return 2.
950
+ fm_backend_wait_transition() { # <backend> <session> <timeout_secs> <state_dir> <pane_window...>
951
+ local backend=$1
952
+ shift
953
+ fm_backend_has_push "$backend" || return 2
954
+ fm_backend_source "$backend" || return 2
955
+ case "$backend" in
956
+ herdr) fm_backend_herdr_wait_transition "$@" ;;
957
+ *) return 2 ;;
958
+ esac
959
+ }
960
+
961
+ fm_backend_commit_transition() { # <backend> <state_dir> <session> <record>
962
+ local backend=$1
963
+ shift
964
+ fm_backend_has_push "$backend" || return 1
965
+ fm_backend_source "$backend" || return 1
966
+ case "$backend" in
967
+ herdr) fm_backend_herdr_commit_transition "$@" ;;
968
+ *) return 1 ;;
969
+ esac
970
+ }
971
+
972
+ fm_backend_clear_transition() { # <backend> <state_dir> <window>
973
+ local backend=$1
974
+ shift
975
+ fm_backend_has_push "$backend" || return 0
976
+ fm_backend_source "$backend" || return 1
977
+ case "$backend" in
978
+ herdr) fm_backend_herdr_clear_transition "$@" ;;
979
+ *) return 0 ;;
980
+ esac
981
+ }