@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.
- package/.agents/README.md +291 -0
- package/.agents/agents/bragi-marketer.md +111 -0
- package/.agents/agents/brokk.md +352 -0
- package/.agents/agents/forseti-reviewer.md +93 -0
- package/.agents/agents/frigg-consent.md +77 -0
- package/.agents/agents/groa-update.md +77 -0
- package/.agents/agents/hnoss-designer.md +69 -0
- package/.agents/agents/huginn-researcher.md +99 -0
- package/.agents/agents/jord-projects.md +78 -0
- package/.agents/agents/kvasir-scout.md +91 -0
- package/.agents/agents/mimir-planner.md +92 -0
- package/.agents/agents/muninn-stow.md +77 -0
- package/.agents/agents/saga-bearings.md +78 -0
- package/.agents/agents/sindri-developer.md +114 -0
- package/.agents/agents/snotra-documenter.md +105 -0
- package/.agents/agents/syn-recovery.md +78 -0
- package/.agents/agents/tyr-check.md +78 -0
- package/.agents/agents/urdh-hold.md +77 -0
- package/.agents/agents/volundr-smith.md +80 -0
- package/.agents/agents/vor-diagnostics.md +77 -0
- package/.agents/assets/agents/naming.md +50 -0
- package/.agents/assets/agents/registry.md +93 -0
- package/.agents/assets/agents/runtime.md +36 -0
- package/.agents/assets/agents/toon-tasks-cli.md +42 -0
- package/.agents/assets/templates/company_entity.template.md +37 -0
- package/.agents/backend/README.md +74 -0
- package/.agents/backend/backends/cmux.sh +649 -0
- package/.agents/backend/backends/herdr-eventwait.py +157 -0
- package/.agents/backend/backends/herdr-workspace-move.py +114 -0
- package/.agents/backend/backends/herdr.sh +3386 -0
- package/.agents/backend/backends/orca.sh +290 -0
- package/.agents/backend/backends/tmux.sh +357 -0
- package/.agents/backend/backends/zellij.sh +700 -0
- package/.agents/backend/fm-afk-launch.sh +654 -0
- package/.agents/backend/fm-afk-return.sh +241 -0
- package/.agents/backend/fm-afk-start.sh +172 -0
- package/.agents/backend/fm-arm-command-policy.mjs +960 -0
- package/.agents/backend/fm-arm-pretool-check.sh +200 -0
- package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
- package/.agents/backend/fm-backend.sh +981 -0
- package/.agents/backend/fm-backlog-handoff.sh +909 -0
- package/.agents/backend/fm-backlog-receive.sh +187 -0
- package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
- package/.agents/backend/fm-bearings-board.sh +204 -0
- package/.agents/backend/fm-bearings-snapshot.sh +546 -0
- package/.agents/backend/fm-bootstrap.sh +1568 -0
- package/.agents/backend/fm-branch-outcome.sh +203 -0
- package/.agents/backend/fm-branch-prompt.sh +101 -0
- package/.agents/backend/fm-brief.sh +452 -0
- package/.agents/backend/fm-busy-event.sh +234 -0
- package/.agents/backend/fm-busy-lib.sh +986 -0
- package/.agents/backend/fm-captain-hold.sh +1001 -0
- package/.agents/backend/fm-cd-command-policy.mjs +153 -0
- package/.agents/backend/fm-cd-pretool-check.sh +190 -0
- package/.agents/backend/fm-check-lib.sh +74 -0
- package/.agents/backend/fm-check-register.sh +42 -0
- package/.agents/backend/fm-check-unregister.sh +52 -0
- package/.agents/backend/fm-classify-lib.sh +1770 -0
- package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
- package/.agents/backend/fm-composer-lib.sh +1416 -0
- package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
- package/.agents/backend/fm-config-push.sh +254 -0
- package/.agents/backend/fm-control-lib.sh +251 -0
- package/.agents/backend/fm-control.sh +878 -0
- package/.agents/backend/fm-crew-state.sh +625 -0
- package/.agents/backend/fm-cursor-lib.sh +243 -0
- package/.agents/backend/fm-decision-hold.sh +232 -0
- package/.agents/backend/fm-doc-audience-check.sh +269 -0
- package/.agents/backend/fm-dod-lib.sh +67 -0
- package/.agents/backend/fm-ensure-agents-md.sh +253 -0
- package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
- package/.agents/backend/fm-extension.mjs +2577 -0
- package/.agents/backend/fm-extension.sh +16 -0
- package/.agents/backend/fm-ff-lib.sh +420 -0
- package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
- package/.agents/backend/fm-fleet-sync.sh +458 -0
- package/.agents/backend/fm-fleet-view.sh +96 -0
- package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
- package/.agents/backend/fm-guard.sh +242 -0
- package/.agents/backend/fm-harness.sh +195 -0
- package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
- package/.agents/backend/fm-herdr-lab.sh +346 -0
- package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
- package/.agents/backend/fm-home-seed.sh +984 -0
- package/.agents/backend/fm-home-summary-refresh.sh +255 -0
- package/.agents/backend/fm-hook-host-lib.sh +36 -0
- package/.agents/backend/fm-inactive-reconcile.sh +531 -0
- package/.agents/backend/fm-inbox.sh +399 -0
- package/.agents/backend/fm-install-actionlint.sh +84 -0
- package/.agents/backend/fm-install-herdr.sh +96 -0
- package/.agents/backend/fm-install-shellcheck.sh +84 -0
- package/.agents/backend/fm-install-treehouse.sh +95 -0
- package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
- package/.agents/backend/fm-lease-lib.sh +218 -0
- package/.agents/backend/fm-lease.sh +189 -0
- package/.agents/backend/fm-line-cap-lib.sh +51 -0
- package/.agents/backend/fm-lint-workflows.sh +137 -0
- package/.agents/backend/fm-lint.sh +591 -0
- package/.agents/backend/fm-lock-lib.sh +104 -0
- package/.agents/backend/fm-lock.sh +107 -0
- package/.agents/backend/fm-marker-lib.sh +12 -0
- package/.agents/backend/fm-merge-local.sh +74 -0
- package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
- package/.agents/backend/fm-nm-run-lib.sh +124 -0
- package/.agents/backend/fm-on.sh +125 -0
- package/.agents/backend/fm-operational-input.sh +253 -0
- package/.agents/backend/fm-peek.sh +46 -0
- package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
- package/.agents/backend/fm-pr-check.sh +135 -0
- package/.agents/backend/fm-pr-lib.sh +1016 -0
- package/.agents/backend/fm-pr-merge.sh +707 -0
- package/.agents/backend/fm-pr-poll.sh +110 -0
- package/.agents/backend/fm-primary-scope-lib.sh +33 -0
- package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
- package/.agents/backend/fm-procevent-lavish.sh +628 -0
- package/.agents/backend/fm-procevent-lib.sh +892 -0
- package/.agents/backend/fm-procevent-quota.sh +290 -0
- package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
- package/.agents/backend/fm-procevent-when.sh +504 -0
- package/.agents/backend/fm-procevent.sh +1466 -0
- package/.agents/backend/fm-project-mode.sh +92 -0
- package/.agents/backend/fm-project-origin-lib.sh +180 -0
- package/.agents/backend/fm-promote.sh +282 -0
- package/.agents/backend/fm-public-followup-emit.sh +260 -0
- package/.agents/backend/fm-public-followup-lib.sh +398 -0
- package/.agents/backend/fm-public-followup.sh +1330 -0
- package/.agents/backend/fm-push-transition-lib.sh +168 -0
- package/.agents/backend/fm-quota-axi-lib.sh +93 -0
- package/.agents/backend/fm-quota-choose.sh +384 -0
- package/.agents/backend/fm-remote-delta-read.sh +189 -0
- package/.agents/backend/fm-remote-doctor.sh +800 -0
- package/.agents/backend/fm-remote-entrypoint.sh +193 -0
- package/.agents/backend/fm-remote-file.sh +234 -0
- package/.agents/backend/fm-remote-home-provision.sh +259 -0
- package/.agents/backend/fm-remote-home-seed.sh +259 -0
- package/.agents/backend/fm-remote-inherit-push.sh +90 -0
- package/.agents/backend/fm-remote-inherit.sh +179 -0
- package/.agents/backend/fm-remote-job-lib.sh +1241 -0
- package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
- package/.agents/backend/fm-remote-job-worker.sh +1124 -0
- package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
- package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
- package/.agents/backend/fm-review-diff.sh +158 -0
- package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
- package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
- package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
- package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
- package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
- package/.agents/backend/fm-secondmate-report.sh +88 -0
- package/.agents/backend/fm-send.sh +1059 -0
- package/.agents/backend/fm-session-lock-lib.sh +176 -0
- package/.agents/backend/fm-session-start.sh +970 -0
- package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
- package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
- package/.agents/backend/fm-sessionstart-run.sh +145 -0
- package/.agents/backend/fm-spawn.sh +3153 -0
- package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
- package/.agents/backend/fm-startup-memory-budget.sh +94 -0
- package/.agents/backend/fm-startup-network.sh +642 -0
- package/.agents/backend/fm-stow-cascade.sh +251 -0
- package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
- package/.agents/backend/fm-supervise-daemon.sh +1732 -0
- package/.agents/backend/fm-supervision-instructions.sh +215 -0
- package/.agents/backend/fm-supervision-lib.sh +90 -0
- package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
- package/.agents/backend/fm-tangle-lib.sh +53 -0
- package/.agents/backend/fm-task-inbox-lib.sh +402 -0
- package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
- package/.agents/backend/fm-teardown.sh +2922 -0
- package/.agents/backend/fm-test-isolation-proof.sh +588 -0
- package/.agents/backend/fm-test-run.sh +2191 -0
- package/.agents/backend/fm-timeout-lib.sh +141 -0
- package/.agents/backend/fm-timing-lib.sh +179 -0
- package/.agents/backend/fm-tmux-lib.sh +291 -0
- package/.agents/backend/fm-tool-update-check.sh +898 -0
- package/.agents/backend/fm-trace-context-lib.sh +227 -0
- package/.agents/backend/fm-transition-lib.sh +103 -0
- package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
- package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
- package/.agents/backend/fm-turnend-guard.sh +432 -0
- package/.agents/backend/fm-update.sh +110 -0
- package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
- package/.agents/backend/fm-voice-client.py +1373 -0
- package/.agents/backend/fm-voice-relay.py +1256 -0
- package/.agents/backend/fm-wake-drain.sh +592 -0
- package/.agents/backend/fm-wake-grant.sh +127 -0
- package/.agents/backend/fm-wake-lib.sh +1849 -0
- package/.agents/backend/fm-watch-arm.sh +602 -0
- package/.agents/backend/fm-watch-checkpoint.sh +109 -0
- package/.agents/backend/fm-watch.sh +1962 -0
- package/.agents/backend/fm-x-dismiss.sh +116 -0
- package/.agents/backend/fm-x-followup.sh +286 -0
- package/.agents/backend/fm-x-lib.sh +1000 -0
- package/.agents/backend/fm-x-link.sh +234 -0
- package/.agents/backend/fm-x-poll.sh +185 -0
- package/.agents/backend/fm-x-reply.sh +385 -0
- package/.agents/backend/fm_voice_frame.py +166 -0
- package/.agents/backend/fm_voice_records.py +574 -0
- package/.agents/backend/model-bridge.py +360 -0
- package/.agents/backend/opencode-go-bridge.py +30 -0
- package/.agents/bus/README.md +12 -0
- package/.agents/bus/messages.json +1 -0
- package/.agents/config/agents.machine.example.yaml +16 -0
- package/.agents/config/agents.yaml.example +72 -0
- package/.agents/config/crew-dispatch.json +22 -0
- package/.agents/config/cron.yaml +11 -0
- package/.agents/config/eindri-dispatch.json +67 -0
- package/.agents/config/eindri-harness +1 -0
- package/.agents/config/startup-memory-budget +1 -0
- package/.agents/filebrowser/.filebrowser.json +6 -0
- package/.agents/filebrowser/README.md +11 -0
- package/.agents/gateway/README.md +12 -0
- package/.agents/github/README.md +15 -0
- package/.agents/github/webhooks/.gitkeep +0 -0
- package/.agents/github/workflows/.gitkeep +0 -0
- package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
- package/.agents/harness/opencode/plugins/package.json +4 -0
- package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
- package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
- package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
- package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
- package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
- package/.agents/memory/README.md +46 -0
- package/.agents/memory/runes_audit.md +5 -0
- package/.agents/migrations/0001-hodd-layout.sh +38 -0
- package/.agents/migrations/0002-a2a-mcp.sh +27 -0
- package/.agents/migrations/0003-private-data-separation.sh +121 -0
- package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
- package/.agents/migrations/0005-roots-out-of-tree.sh +158 -0
- package/.agents/sandbox/Dockerfile.utgard +19 -0
- package/.agents/sandbox/README.md +14 -0
- package/.agents/sandbox/utgard.config.json +14 -0
- package/.agents/skills/README.md +59 -0
- package/.agents/skills/bragi-marketing/README.md +3 -0
- package/.agents/skills/bragi-marketing/SKILL.md +82 -0
- package/.agents/skills/eindri-homes/README.md +3 -0
- package/.agents/skills/eindri-homes/SKILL.md +291 -0
- package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
- package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
- package/.agents/skills/frigg-consent/README.md +3 -0
- package/.agents/skills/frigg-consent/SKILL.md +56 -0
- package/.agents/skills/galdr-ymirsystem/README.md +3 -0
- package/.agents/skills/galdr-ymirsystem/SKILL.md +159 -0
- package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
- package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +736 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
- package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +539 -0
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +763 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +415 -0
- package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +133 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +101 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
- package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
- package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
- package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +180 -0
- package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
- package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
- package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +503 -0
- package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
- package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
- package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
- package/.agents/skills/gjallarhorn-relay/README.md +3 -0
- package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
- package/.agents/skills/groa-update/SKILL.md +90 -0
- package/.agents/skills/hamr-adapters/README.md +3 -0
- package/.agents/skills/hamr-adapters/SKILL.md +123 -0
- package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
- package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
- package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
- package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
- package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
- package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
- package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
- package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
- package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
- package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
- package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
- package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
- package/.agents/skills/herdr-panes/README.md +3 -0
- package/.agents/skills/herdr-panes/SKILL.md +92 -0
- package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
- package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
- package/.agents/skills/hnoss-design/DESIGN.md +19 -0
- package/.agents/skills/hnoss-design/README.md +3 -0
- package/.agents/skills/hnoss-design/SKILL.md +83 -0
- package/.agents/skills/hvild-afk/README.md +3 -0
- package/.agents/skills/hvild-afk/SKILL.md +241 -0
- package/.agents/skills/jord-projects/README.md +3 -0
- package/.agents/skills/jord-projects/SKILL.md +92 -0
- package/.agents/skills/lifecycle/SKILL.md +56 -0
- package/.agents/skills/lifecycle/smoke_test.sh +57 -0
- package/.agents/skills/lifecycle/start.sh +21 -0
- package/.agents/skills/lifecycle/status.sh +35 -0
- package/.agents/skills/lifecycle/stop.sh +16 -0
- package/.agents/skills/muninn-stow/README.md +3 -0
- package/.agents/skills/muninn-stow/SKILL.md +309 -0
- package/.agents/skills/no-mistakes/SKILL.md +379 -0
- package/.agents/skills/nornir-schedule/README.md +3 -0
- package/.agents/skills/nornir-schedule/SKILL.md +25 -0
- package/.agents/skills/nornir-schedule/assets/events.md +137 -0
- package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
- package/.agents/skills/nsr-compliance/README.md +3 -0
- package/.agents/skills/nsr-compliance/SKILL.md +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/pr-ops/README.md +3 -0
- package/.agents/skills/pr-ops/SKILL.md +109 -0
- package/.agents/skills/ratatoskr-a2a/README.md +3 -0
- package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
- package/.agents/skills/rules-check-drift/SKILL.md +81 -0
- package/.agents/skills/saga-bearings/README.md +3 -0
- package/.agents/skills/saga-bearings/SKILL.md +26 -0
- package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
- package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
- package/.agents/skills/saga-bearings/assets/recap.md +56 -0
- package/.agents/skills/syn-recovery/README.md +3 -0
- package/.agents/skills/syn-recovery/SKILL.md +54 -0
- package/.agents/skills/tyr-check/README.md +3 -0
- package/.agents/skills/tyr-check/SKILL.md +218 -0
- package/.agents/skills/urdh-hold/README.md +3 -0
- package/.agents/skills/urdh-hold/SKILL.md +25 -0
- package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
- package/.agents/skills/urdh-hold/assets/hold.md +54 -0
- package/.agents/skills/vor-diagnostics/README.md +3 -0
- package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
- package/.agents/skills/ymir-host/README.md +3 -0
- package/.agents/skills/ymir-host/SKILL.md +35 -0
- package/.agents/skills/ymir-host/assets/desktop.md +36 -0
- package/.agents/skills/ymir-host/assets/install.md +69 -0
- package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
- package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
- package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
- package/.agents/skills/ymir-host/assets/update.md +64 -0
- package/.agents/tests/assets/board-render-harness.mjs +117 -0
- package/.agents/tests/cmux-test-safety.sh +51 -0
- package/.agents/tests/fixtures.sh +291 -0
- package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
- package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
- package/.agents/tests/fm-afk-launch.test.sh +961 -0
- package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
- package/.agents/tests/fm-afk-return.test.sh +283 -0
- package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
- package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
- package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
- package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
- package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
- package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
- package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
- package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
- package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
- package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
- package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
- package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
- package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
- package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
- package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
- package/.agents/tests/fm-backend-orca.test.sh +1357 -0
- package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
- package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
- package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
- package/.agents/tests/fm-backend.test.sh +1142 -0
- package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
- package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
- package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
- package/.agents/tests/fm-bearings-board.test.sh +414 -0
- package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
- package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
- package/.agents/tests/fm-bootstrap.test.sh +1178 -0
- package/.agents/tests/fm-branch-supervision.test.sh +552 -0
- package/.agents/tests/fm-brief.test.sh +785 -0
- package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
- package/.agents/tests/fm-busy-state.test.sh +465 -0
- package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
- package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
- package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
- package/.agents/tests/fm-check-unregister.test.sh +198 -0
- package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
- package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
- package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
- package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
- package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
- package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
- package/.agents/tests/fm-composer-ghost.test.sh +716 -0
- package/.agents/tests/fm-composer-lib.test.sh +666 -0
- package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
- package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
- package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
- package/.agents/tests/fm-control.test.sh +910 -0
- package/.agents/tests/fm-crew-state.test.sh +1610 -0
- package/.agents/tests/fm-cursor-harness.test.sh +404 -0
- package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
- package/.agents/tests/fm-cursor-primary.test.sh +708 -0
- package/.agents/tests/fm-daemon.test.sh +2740 -0
- package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
- package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
- package/.agents/tests/fm-extension-binding.test.sh +2187 -0
- package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
- package/.agents/tests/fm-fleet-sync.test.sh +721 -0
- package/.agents/tests/fm-gate-refuse.test.sh +356 -0
- package/.agents/tests/fm-gitignore-config.test.sh +89 -0
- package/.agents/tests/fm-gotmp.test.sh +230 -0
- package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
- package/.agents/tests/fm-grok-harness.test.sh +117 -0
- package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
- package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
- package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
- package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
- package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
- package/.agents/tests/fm-herdr-lab.test.sh +243 -0
- package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
- package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
- package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
- package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
- package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
- package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
- package/.agents/tests/fm-kimi-harness.test.sh +687 -0
- package/.agents/tests/fm-lint-workflows.test.sh +532 -0
- package/.agents/tests/fm-lint.test.sh +1023 -0
- package/.agents/tests/fm-muse-harness.test.sh +932 -0
- package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
- package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
- package/.agents/tests/fm-on.test.sh +507 -0
- package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
- package/.agents/tests/fm-operational-input.test.sh +160 -0
- package/.agents/tests/fm-peek-remote.test.sh +110 -0
- package/.agents/tests/fm-pending-reply.test.sh +1288 -0
- package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
- package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
- package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
- package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
- package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
- package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
- package/.agents/tests/fm-pr-merge.test.sh +2135 -0
- package/.agents/tests/fm-procevent-quota.test.sh +225 -0
- package/.agents/tests/fm-procevent-when.test.sh +406 -0
- package/.agents/tests/fm-procevent.test.sh +1745 -0
- package/.agents/tests/fm-project-origin.test.sh +110 -0
- package/.agents/tests/fm-public-followup.test.sh +2272 -0
- package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
- package/.agents/tests/fm-quota-choose.test.sh +626 -0
- package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
- package/.agents/tests/fm-remote-doctor.test.sh +625 -0
- package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
- package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
- package/.agents/tests/fm-remote-job.test.sh +768 -0
- package/.agents/tests/fm-remote-reply.test.sh +520 -0
- package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
- package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
- package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
- package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
- package/.agents/tests/fm-review-diff.test.sh +176 -0
- package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
- package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
- package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
- package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
- package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
- package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
- package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
- package/.agents/tests/fm-send-inbox.test.sh +352 -0
- package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
- package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
- package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
- package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
- package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
- package/.agents/tests/fm-send-settle.test.sh +147 -0
- package/.agents/tests/fm-send-strict.test.sh +241 -0
- package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
- package/.agents/tests/fm-session-start.test.sh +2515 -0
- package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
- package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
- package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
- package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
- package/.agents/tests/fm-spawn-batch.test.sh +148 -0
- package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
- package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
- package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
- package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
- package/.agents/tests/fm-startup-network.test.sh +712 -0
- package/.agents/tests/fm-stow-cascade.test.sh +370 -0
- package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
- package/.agents/tests/fm-supervision-events.test.sh +156 -0
- package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
- package/.agents/tests/fm-tangle-guard.test.sh +277 -0
- package/.agents/tests/fm-task-delivery.test.sh +420 -0
- package/.agents/tests/fm-task-inbox.test.sh +513 -0
- package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
- package/.agents/tests/fm-teardown.test.sh +2665 -0
- package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
- package/.agents/tests/fm-test-fixtures.test.sh +132 -0
- package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
- package/.agents/tests/fm-test-run.test.sh +1196 -0
- package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
- package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
- package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
- package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
- package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
- package/.agents/tests/fm-transition-lib.test.sh +54 -0
- package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
- package/.agents/tests/fm-update.test.sh +304 -0
- package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
- package/.agents/tests/fm-voice-relay.test.sh +4741 -0
- package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
- package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
- package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
- package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
- package/.agents/tests/fm-wake-queue.test.sh +1224 -0
- package/.agents/tests/fm-watch-arm.test.sh +821 -0
- package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
- package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
- package/.agents/tests/fm-watch-triage.test.sh +3889 -0
- package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
- package/.agents/tests/fm-x-mode.test.sh +3042 -0
- package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
- package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
- package/.agents/tests/herdr-test-safety.sh +42 -0
- package/.agents/tests/lib.sh +315 -0
- package/.agents/tests/remote-herdr-fixture.sh +125 -0
- package/.agents/tests/secondmate-helpers.sh +193 -0
- package/.agents/tests/smoke.test.sh +93 -0
- package/.agents/tests/wake-helpers.sh +338 -0
- package/.agents/tests/zellij-test-safety.sh +60 -0
- package/.agents/tools/README.md +10 -0
- package/.agents/tools/bin/tasks-cli +267 -0
- package/.agents/tools/tasks-cli.ts +216 -0
- package/AGENTS.md +498 -0
- package/CHANGELOG.md +1913 -0
- package/LICENSE +1 -1
- package/NOTICE +63 -0
- package/README.md +114 -60
- package/RULES/01-domains.md +51 -0
- package/RULES/02-agents.md +78 -0
- package/RULES/03-houses.md +61 -0
- package/RULES/04-hoard.md +97 -0
- package/RULES/05-platforms.md +67 -0
- package/RULES/06-append-only.md +108 -0
- package/RULES/07-config.md +101 -0
- package/RULES/08-delivery-gate.md +75 -0
- package/RULES/09-electron.md +58 -0
- package/RULES/README.md +22 -0
- package/bin/README.md +107 -0
- package/bin/a2a-mcp.sh +133 -0
- package/bin/a2a-serve.py +103 -0
- package/bin/a2a-serve.sh +8 -0
- package/bin/a2a-talk.sh +50 -0
- package/bin/a2abridge-ensure.sh +108 -0
- package/bin/agent-run.sh +43 -0
- package/bin/agents-config.sh +256 -0
- package/bin/apodex-smoke-test.sh +105 -0
- package/bin/bifrost-bridge.sh +131 -0
- package/bin/bifrost-ingress.sh +77 -0
- package/bin/bootstrap-macos.sh +112 -0
- package/bin/bootstrap-windows.ps1 +112 -0
- package/bin/branch-guard.sh +64 -0
- package/bin/brokk +73 -0
- package/bin/brokk-classify-lib.sh +1770 -0
- package/bin/brokk-lease-lib.sh +222 -0
- package/bin/brokk-lease.sh +193 -0
- package/bin/brokk-lint.sh +65 -0
- package/bin/brokk-send.sh +51 -0
- package/bin/brokk-update.sh +9 -0
- package/bin/brokk-wake-grant.sh +127 -0
- package/bin/brokk-wake-lib.sh +1849 -0
- package/bin/changelog-assemble.sh +154 -0
- package/bin/changelog-guard.sh +197 -0
- package/bin/crash-sense.sh +173 -0
- package/bin/design-check.sh +80 -0
- package/bin/design-icon.sh +153 -0
- package/bin/desktop-place.sh +224 -0
- package/bin/docs-guard.sh +39 -0
- package/bin/editor-place.sh +225 -0
- package/bin/eindri-acclaim.sh +47 -0
- package/bin/eindri-control.sh +28 -0
- package/bin/eindri-role.sh +83 -0
- package/bin/eindri-seat.sh +83 -0
- package/bin/eindri-seen.sh +54 -0
- package/bin/eindri-send.sh +15 -0
- package/bin/eindri-start.sh +98 -0
- package/bin/eindri-watch.sh +57 -0
- package/bin/einherjar-spawn.sh +576 -0
- package/bin/eir-doctor.sh +158 -0
- package/bin/erindi-brief.sh +271 -0
- package/bin/feature-inventory.sh +73 -0
- package/bin/fleet-apply.sh +81 -0
- package/bin/github-deploy.sh +94 -0
- package/bin/gjallarhorn-expose.sh +136 -0
- package/bin/gjallarhorn-notify.sh +71 -0
- package/bin/gjallarhorn-purge.sh +67 -0
- package/bin/gjallarhorn-tunnel.sh +85 -0
- package/bin/gleipnir-lock-lib.sh +284 -0
- package/bin/groa-update.sh +130 -0
- package/bin/hall-snapshot.sh +111 -0
- package/bin/hamr-harness.sh +246 -0
- package/bin/heimdall-ssh-keys.sh +374 -0
- package/bin/herdr-agents.py +44 -0
- package/bin/herdr-ensure.sh +104 -0
- package/bin/herdr-run.sh +452 -0
- package/bin/hermes-ensure.sh +88 -0
- package/bin/hlidskjalf-agents.sh +172 -0
- package/bin/hlidskjalf-usage.sh +196 -0
- package/bin/hoard-guard.sh +135 -0
- package/bin/hoard-lib.sh +105 -0
- package/bin/hodd.sh +102 -0
- package/bin/host-sense.sh +199 -0
- package/bin/huginn-research-worker.sh +121 -0
- package/bin/local-model-lock.sh +70 -0
- package/bin/mimir-bridge.py +259 -0
- package/bin/mimir-bridge.sh +100 -0
- package/bin/mimir-ingest.sh +131 -0
- package/bin/mimir-reflect.sh +27 -0
- package/bin/mimir.sh +113 -0
- package/bin/mjollnir-webhook.sh +72 -0
- package/bin/mjollnir.sh +104 -0
- package/bin/model-hardware.sh +96 -0
- package/bin/model-resolve.sh +160 -0
- package/bin/models-detect.sh +178 -0
- package/bin/nornir-cron-start.sh +162 -0
- package/bin/nornir-job-daily-briefing.sh +191 -0
- package/bin/nornir-job-git-sync.sh +152 -0
- package/bin/nornir-job-hall-snapshot.sh +28 -0
- package/bin/nornir-job-memory-housekeeping.sh +114 -0
- package/bin/nornir-job-nsr-compliance.sh +54 -0
- package/bin/nornir-job-observer.sh +166 -0
- package/bin/nornir-job-skillopt-sleep.sh +76 -0
- package/bin/npm-publish.sh +136 -0
- package/bin/omarchy-hook-install.sh +60 -0
- package/bin/omarchy-install.sh +161 -0
- package/bin/omarchy-plugins.sh +187 -0
- package/bin/omarchy-sense.sh +162 -0
- package/bin/perm-guard.sh +30 -0
- package/bin/pi-agent.sh +68 -0
- package/bin/pi-ensure.sh +77 -0
- package/bin/pi-local.sh +37 -0
- package/bin/pi-model-agnostic.sh +71 -0
- package/bin/pi-seat.sh +63 -0
- package/bin/prereq-ensure.sh +166 -0
- package/bin/private-guard.sh +58 -0
- package/bin/project-git.sh +69 -0
- package/bin/public-guard.sh +71 -0
- package/bin/ratatoskr.sh +49 -0
- package/bin/realm-lib.sh +32 -0
- package/bin/repo-scrub.sh +62 -0
- package/bin/rodd-operational-input.sh +250 -0
- package/bin/runes-append.sh +198 -0
- package/bin/saga-session-start.sh +172 -0
- package/bin/saga-sessionstart-run.sh +79 -0
- package/bin/saga-wake-drain.sh +41 -0
- package/bin/secret-guard.sh +63 -0
- package/bin/sessrumnir-ensure.sh +100 -0
- package/bin/sessrumnir-sync.sh +237 -0
- package/bin/sessrumnir.sh +104 -0
- package/bin/skillopt-setup.sh +76 -0
- package/bin/skuld-branch-outcome.sh +203 -0
- package/bin/skuld-branch-prompt.sh +101 -0
- package/bin/smidja-bootstrap.sh +94 -0
- package/bin/smidja-observe.sh +86 -0
- package/bin/syn-arm-pretool-check.sh +57 -0
- package/bin/syn-asset-pretool-check.sh +89 -0
- package/bin/syn-cd-pretool-check.sh +28 -0
- package/bin/syn-guard-pretool-check.sh +83 -0
- package/bin/syn-spawn-pretool-check.sh +58 -0
- package/bin/syn-turnend-guard.sh +36 -0
- package/bin/syn-watch-arm.sh +99 -0
- package/bin/tailscale-sync.sh +203 -0
- package/bin/telegram-bot.sh +82 -0
- package/bin/toolchain.sh +74 -0
- package/bin/ui-truth-check.sh +75 -0
- package/bin/utgard.sh +111 -0
- package/bin/valhalla.sh +115 -0
- package/bin/valknut-load.sh +298 -0
- package/bin/vor-crew-state.sh +124 -0
- package/bin/wedge-notify.sh +60 -0
- package/bin/workspace-provision.sh +80 -0
- package/bin/workspace-rag.sh +163 -0
- package/bin/wyrd-db.sh +47 -0
- package/bin/yggdrasil.sh +152 -0
- package/bin/ymir-edit.sh +145 -0
- package/bin/ymir-install.sh +806 -0
- package/bin/ymir-invite.sh +93 -0
- package/bin/ymir-isolation.sh +88 -0
- package/bin/ymir-migrate.sh +61 -0
- package/bin/ymir-plan.sh +356 -0
- package/bin/ymir-platform.sh +348 -0
- package/bin/ymir-say.sh +110 -0
- package/bin/ymir-setup-auth.sh +119 -0
- package/bin/ymir-validate.sh +193 -0
- package/bin/ymir.js +74 -16
- package/docs/Architecture.md +339 -0
- package/docs/design.md +351 -0
- package/docs/installations/README.md +63 -0
- package/docs/installations/ymir-home.md +93 -0
- package/docs/integration.md +105 -0
- package/docs/lore.md +1060 -0
- package/docs/research/agent-harnesses.md +290 -0
- package/docs/runbooks/README.md +19 -0
- package/docs/runbooks/a2a.md +91 -0
- package/docs/runbooks/agent-permissions.md +99 -0
- package/docs/runbooks/agents.md +57 -0
- package/docs/runbooks/models.md +69 -0
- package/docs/runbooks/realm-onboarding.md +60 -0
- package/docs/runbooks/secrets-and-hoard.md +60 -0
- package/docs/runbooks/updates-and-migrations.md +110 -0
- package/docs/runbooks/worktrees-isolation.md +72 -0
- package/docs/session-start.md +105 -0
- package/docs/skillopt-integration.md +321 -0
- package/docs/sync-system.md +66 -0
- package/docs/workspaces.md +104 -0
- package/docs/ymir-rut.md +260 -0
- package/package.json +54 -14
- package/packaging/build.sh +74 -0
- package/packaging/macos/Ymir Installer.command +43 -0
- package/packaging/windows/ymir-setup.nsi +74 -0
- package/scripts/README.md +22 -0
- package/scripts/electron.sh +269 -0
- package/scripts/lower.sh +34 -0
- package/scripts/raise.sh +42 -0
- package/scripts/start.sh +217 -0
- package/scripts/stop.sh +113 -0
- package/install.sh +0 -35
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# bin/backends/zellij.sh - the zellij session-provider adapter (EXPERIMENTAL).
|
|
3
|
+
#
|
|
4
|
+
# Design: data/fm-backend-design-d7/report.md ("Zellij Backend" section - the
|
|
5
|
+
# interface mapping, implementation choices, and "Zellij gaps to verify" list)
|
|
6
|
+
# and herdr-addendum.md D2/D3 (zellij is P3, after herdr; treehouse stays the
|
|
7
|
+
# worktree provider). Zellij is a session provider ONLY: the worktree provider
|
|
8
|
+
# stays treehouse, exactly like tmux and herdr. Sourced only through
|
|
9
|
+
# bin/fm-backend.sh's fm_backend_source in normal operation; the unit tests
|
|
10
|
+
# source it directly.
|
|
11
|
+
#
|
|
12
|
+
# Session shape (report "Zellij implementation choices" #1, unchanged by
|
|
13
|
+
# empirical verification): ONE zellij session (default name "firstmate",
|
|
14
|
+
# overridable via FM_ZELLIJ_SESSION for test isolation - mirrors herdr's
|
|
15
|
+
# HERDR_SESSION), ONE tab per task, with caller-facing label "fm-<id>" and a
|
|
16
|
+
# home-scoped actual title. No per-home workspace split (unlike herdr's later
|
|
17
|
+
# P3 refinement): zellij has no workspace concept, only sessions/tabs/panes,
|
|
18
|
+
# so this stays exactly the report's original choice. Target string shape:
|
|
19
|
+
# "<zellij-session>:<pane-id>" (pane id is a bare non-negative integer with no
|
|
20
|
+
# embedded colon, so splitting on the FIRST colon is trivially correct and
|
|
21
|
+
# mirrors herdr's target-string convention).
|
|
22
|
+
#
|
|
23
|
+
# Home-scoped tab titles (closes a cross-home collision gap): because every
|
|
24
|
+
# task in every firstmate home - primary or secondmate - shares this ONE
|
|
25
|
+
# session's tab bar with no per-home split, and zellij enforces no tab-name
|
|
26
|
+
# uniqueness at all, two firstmate homes whose task ids happen to collide
|
|
27
|
+
# could send/peek/close each other's tabs. This is the exact gap a
|
|
28
|
+
# captain-directed no-mistakes review gate caught for the cmux backend
|
|
29
|
+
# (docs/cmux-backend.md) and this same tag mechanism (bin/backends/cmux.sh's
|
|
30
|
+
# fm_backend_cmux_scoped_title, now shared via bin/fm-backend-hometag-lib.sh)
|
|
31
|
+
# is ported here for the identical reason. Every NEW tab is created with a
|
|
32
|
+
# title tagged with this installation's home label (fm_backend_zellij_scoped_title,
|
|
33
|
+
# "fm-<hometag>-<id>"); every list/find/recover/kill path is scoped to this
|
|
34
|
+
# home's own tag. A tab created before this change carries the old untagged
|
|
35
|
+
# "fm-<id>" title; target_ready, kill, and ad hoc selector fallback still
|
|
36
|
+
# match it, but ONLY when that bare title is unambiguous (exactly one live tab
|
|
37
|
+
# in the session carries it) - see fm_backend_zellij_tab_matches_label and
|
|
38
|
+
# docs/zellij-backend.md "Home-scoped tab titles" for the full migration
|
|
39
|
+
# posture. Moving/relocating a firstmate installation changes its tag
|
|
40
|
+
# (acceptable - recorded worktree paths do not survive a move either).
|
|
41
|
+
#
|
|
42
|
+
# Empirical verification (real zellij 0.44.0, macOS aarch64, 2026-07-02;
|
|
43
|
+
# docs/zellij-backend.md has the full evidence log) resolved every "gaps to
|
|
44
|
+
# verify" item in the design report, plus additional real findings not
|
|
45
|
+
# anticipated by the report:
|
|
46
|
+
#
|
|
47
|
+
# 1. dump-screen on a background session with NO attached client: WORKS.
|
|
48
|
+
# 2. Key names: Enter -> "Enter", Escape -> "Esc" (NOT "Escape"), Ctrl-C ->
|
|
49
|
+
# "Ctrl c" as ONE shell argument with an embedded space (NOT two argv
|
|
50
|
+
# words, NOT "C-c" or "Ctrl+c" - all verified to fail).
|
|
51
|
+
# 3. `new-tab --cwd --name` DOES return the created tab's bare integer id on
|
|
52
|
+
# stdout, exactly as documented.
|
|
53
|
+
# 4. `list-panes --json`'s `pane_cwd` reflects a `cd` run DIRECTLY in the
|
|
54
|
+
# pane's own top-level shell within one poll (<0.3s) - but does NOT
|
|
55
|
+
# reflect a `cd` performed by a NESTED SUBSHELL the pane's shell
|
|
56
|
+
# launched as a foreground command (verified: `treehouse get` opens
|
|
57
|
+
# exactly such a subshell). `pane_cwd` stays frozen at wherever the
|
|
58
|
+
# pane's shell was when it invoked that foreground command - worse than
|
|
59
|
+
# herdr's frozen-cwd trap (herdr at least exposes a `foreground_cwd`
|
|
60
|
+
# that tracks this; zellij's CLI exposes no live-process cwd field and
|
|
61
|
+
# no per-pane pid to read it from `/proc`/`lsof` either). This directly
|
|
62
|
+
# contradicts the design report's assumption ("acceptable for tmux and
|
|
63
|
+
# zellij") and required a different implementation strategy - see
|
|
64
|
+
# fm_backend_zellij_current_path below and docs/zellij-backend.md
|
|
65
|
+
# "Worktree-path discovery: pane_cwd does not track a subshell".
|
|
66
|
+
# 5. `new-tab` DOES steal focus from an attached client with NO flag to
|
|
67
|
+
# suppress it (unlike herdr's --no-focus and tmux's new-window -d).
|
|
68
|
+
# Mitigated (fm_backend_zellij_create_task): capture the previously
|
|
69
|
+
# active tab id before creating, restore it with go-to-tab-by-id
|
|
70
|
+
# afterward - verified to correctly restore an attached client's view
|
|
71
|
+
# and to be a safe no-op with no client attached.
|
|
72
|
+
#
|
|
73
|
+
# Additional un-anticipated findings, load-bearing for this adapter:
|
|
74
|
+
# - Every pane-targeting action (write-chars, send-keys, dump-screen, ...)
|
|
75
|
+
# MUST pass an explicit --pane-id. The "focused pane" default is
|
|
76
|
+
# unreliable: a fresh session auto-opens a floating "About Zellij"/release
|
|
77
|
+
# notes PLUGIN pane that starts FOCUSED and shadows the real terminal pane
|
|
78
|
+
# - a pane-id-less send silently goes nowhere.
|
|
79
|
+
# - `zellij action <subcommand>` ALWAYS exits 0, even against a nonexistent
|
|
80
|
+
# session (prints the live session list to stdout, an error to stderr) or
|
|
81
|
+
# a nonexistent pane id in a live session (prints nothing at all, to
|
|
82
|
+
# neither stream). The exit code can NEVER be trusted to detect a bad
|
|
83
|
+
# target. Mitigated: send/capture/cwd ops verify session liveness first
|
|
84
|
+
# (fm_backend_zellij_session_exists, a passive list-sessions query, never
|
|
85
|
+
# auto-creating), verify the specific pane still appears in list-panes JSON,
|
|
86
|
+
# and, for metadata-routed task selector operations, verify the pane's tab
|
|
87
|
+
# still matches the expected caller-facing task label through the home-scoped or
|
|
88
|
+
# unambiguous legacy title before use. Kill verifies the session and, when
|
|
89
|
+
# teardown supplies an expected tab label, verifies a tab id still matches
|
|
90
|
+
# that label before closing it. Output-SHAPE validation (a bare integer tab
|
|
91
|
+
# id, JSON that parses) rejects the "session not found" text fallback. A
|
|
92
|
+
# pane can still die between the preflight check and the operation call;
|
|
93
|
+
# docs/zellij-backend.md records that residual race.
|
|
94
|
+
# - `zellij list-tabs`/`new-tab` does NOT enforce unique tab names (same as
|
|
95
|
+
# herdr's tabs, unlike tmux's own window-name uniqueness), so the
|
|
96
|
+
# duplicate check below is ours, mirroring both prior adapters.
|
|
97
|
+
# - Closing a tab's only pane (`close-pane`) does NOT close the now-empty
|
|
98
|
+
# tab (unlike herdr, where the analogous close DOES remove the tab) - an
|
|
99
|
+
# empty "ghost" tab survives in `list-tabs` until explicitly closed. Kill
|
|
100
|
+
# therefore always resolves the owning tab id and calls
|
|
101
|
+
# `close-tab-by-id`, which verified cleanly removes a live tab (pane and
|
|
102
|
+
# all) in one call - never a separate close-pane first.
|
|
103
|
+
#
|
|
104
|
+
# Requires: zellij (CLI), jq (JSON parsing). Bootstrap detects these through
|
|
105
|
+
# fm_backend_required_tools only when zellij is the resolved backend; this
|
|
106
|
+
# adapter also gates them again before spawning.
|
|
107
|
+
|
|
108
|
+
# FM_HOME fallback: every real caller already sets FM_HOME as a global before
|
|
109
|
+
# sourcing fm-backend.sh (which sources this file); this exists only so this
|
|
110
|
+
# file's own unit tests, which source it directly, resolve sanely. Mirrors
|
|
111
|
+
# bin/backends/herdr.sh's identical fallback; unlike herdr this adapter still
|
|
112
|
+
# has no per-home CONTAINER split (one shared session for every home), but
|
|
113
|
+
# FM_HOME/FM_ROOT now also feed fm_backend_zellij_home_label's tab-title tag
|
|
114
|
+
# below.
|
|
115
|
+
FM_BACKEND_ZELLIJ_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
116
|
+
FM_ROOT="${FM_ROOT_OVERRIDE:-${FM_ROOT:-$FM_BACKEND_ZELLIJ_ROOT}}"
|
|
117
|
+
FM_HOME="${FM_HOME:-${FM_ROOT_OVERRIDE:-$FM_ROOT}}"
|
|
118
|
+
|
|
119
|
+
# shellcheck source=bin/fm-backend-hometag-lib.sh
|
|
120
|
+
. "$FM_BACKEND_ZELLIJ_ROOT/bin/fm-backend-hometag-lib.sh"
|
|
121
|
+
|
|
122
|
+
# Shared composer classification (the fleet-wide shape catalogue and verdict
|
|
123
|
+
# owner; this adapter contributes only capture and capability facts).
|
|
124
|
+
# shellcheck source=bin/fm-composer-lib.sh
|
|
125
|
+
. "$FM_BACKEND_ZELLIJ_ROOT/bin/fm-composer-lib.sh"
|
|
126
|
+
|
|
127
|
+
# Verified minimum: report.md recommends "likely Zellij 0.44 or newer" for
|
|
128
|
+
# returned pane/tab IDs and dump-screen --pane-id; empirically verified
|
|
129
|
+
# against the installed 0.44.0 (docs/zellij-backend.md).
|
|
130
|
+
FM_BACKEND_ZELLIJ_MIN_MAJOR=0
|
|
131
|
+
FM_BACKEND_ZELLIJ_MIN_MINOR=44
|
|
132
|
+
|
|
133
|
+
# fm_backend_zellij_session: the session name this spawn/op uses.
|
|
134
|
+
# FM_ZELLIJ_SESSION mirrors herdr's HERDR_SESSION ambient-selection knob: an
|
|
135
|
+
# operator (or firstmate's own isolated test harness) sets it explicitly;
|
|
136
|
+
# absent means the shared "firstmate" session. Do not use this alone for
|
|
137
|
+
# destructive test cleanup; tests/zellij-test-safety.sh documents and guards
|
|
138
|
+
# that path (mirrors tests/herdr-test-safety.sh).
|
|
139
|
+
fm_backend_zellij_session() {
|
|
140
|
+
printf '%s' "${FM_ZELLIJ_SESSION:-firstmate}"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# fm_backend_zellij_home_label: readable home prefix plus a short hash of the
|
|
144
|
+
# resolved FM_ROOT path (bin/fm-backend-hometag-lib.sh). Zellij has one
|
|
145
|
+
# session-global tab namespace shared by every firstmate home, so the path
|
|
146
|
+
# hash distinguishes every installation, including multiple primary homes.
|
|
147
|
+
# Moving an installation changes this tag and old zellij tab titles stop
|
|
148
|
+
# matching; task meta already records absolute worktree paths, so repo
|
|
149
|
+
# relocation is already outside the supported recovery contract.
|
|
150
|
+
fm_backend_zellij_home_label() {
|
|
151
|
+
fm_backend_hometag
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# fm_backend_zellij_scoped_title: the actual tab title a NEW task's tab is
|
|
155
|
+
# created with - the caller-facing "fm-<id>" label, home-tagged as
|
|
156
|
+
# "fm-<hometag>-<id>" (mirrors bin/backends/cmux.sh's identical
|
|
157
|
+
# fm_backend_cmux_scoped_title). Every list/find/recover/kill path below
|
|
158
|
+
# scopes its own-home matches through this.
|
|
159
|
+
fm_backend_zellij_scoped_title() { # <fm-task-label>
|
|
160
|
+
local label=$1 rest home
|
|
161
|
+
home=$(fm_backend_zellij_home_label)
|
|
162
|
+
case "$label" in
|
|
163
|
+
fm-*) rest=${label#fm-} ;;
|
|
164
|
+
*) rest=$label ;;
|
|
165
|
+
esac
|
|
166
|
+
printf 'fm-%s-%s' "$home" "$rest"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# fm_backend_zellij_tool_check: refuse loudly if zellij or jq is missing.
|
|
170
|
+
fm_backend_zellij_tool_check() {
|
|
171
|
+
command -v zellij >/dev/null 2>&1 || { echo "error: backend=zellij selected but the 'zellij' CLI is not installed (https://zellij.dev)" >&2; return 1; }
|
|
172
|
+
command -v jq >/dev/null 2>&1 || { echo "error: backend=zellij selected but 'jq' is not installed (required to parse zellij's JSON output)" >&2; return 1; }
|
|
173
|
+
return 0
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# fm_backend_zellij_version_check: refuse loudly on a missing/incompatible
|
|
177
|
+
# zellij client. Verified locally: 0.44.0 (`zellij --version` -> "zellij
|
|
178
|
+
# 0.44.0", session-independent - no server/session needs to exist yet).
|
|
179
|
+
fm_backend_zellij_version_check() {
|
|
180
|
+
fm_backend_zellij_tool_check || return 1
|
|
181
|
+
local raw ver major rest minor
|
|
182
|
+
raw=$(zellij --version 2>/dev/null) || { echo "error: 'zellij --version' failed; is zellij installed correctly?" >&2; return 1; }
|
|
183
|
+
ver=$(printf '%s' "$raw" | awk '{print $2}')
|
|
184
|
+
case "$ver" in
|
|
185
|
+
''|*[!0-9.]*)
|
|
186
|
+
echo "error: could not parse a zellij version from '$raw'; refusing to use an unverified zellij build" >&2
|
|
187
|
+
return 1
|
|
188
|
+
;;
|
|
189
|
+
esac
|
|
190
|
+
major=${ver%%.*}
|
|
191
|
+
rest=${ver#*.}
|
|
192
|
+
minor=${rest%%.*}
|
|
193
|
+
case "$major" in ''|*[!0-9]*) major=0 ;; esac
|
|
194
|
+
case "$minor" in ''|*[!0-9]*) minor=0 ;; esac
|
|
195
|
+
if [ "$major" -lt "$FM_BACKEND_ZELLIJ_MIN_MAJOR" ] || { [ "$major" -eq "$FM_BACKEND_ZELLIJ_MIN_MAJOR" ] && [ "$minor" -lt "$FM_BACKEND_ZELLIJ_MIN_MINOR" ]; }; then
|
|
196
|
+
echo "error: zellij $ver is older than the verified minimum $FM_BACKEND_ZELLIJ_MIN_MAJOR.$FM_BACKEND_ZELLIJ_MIN_MINOR; update zellij before using backend=zellij" >&2
|
|
197
|
+
return 1
|
|
198
|
+
fi
|
|
199
|
+
return 0
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# fm_backend_zellij_cli: run `zellij --session <session> action <args...>`,
|
|
203
|
+
# setting BOTH the ZELLIJ_SESSION_NAME env var AND the leading global
|
|
204
|
+
# `--session <name>` flag (zellij's session-target flag is GLOBAL, before the
|
|
205
|
+
# subcommand - unlike herdr's trailing --session - verified both forms
|
|
206
|
+
# independently route correctly on the installed 0.44.0 client; kept together
|
|
207
|
+
# for defense in depth, mirroring bin/backends/herdr.sh's fm_backend_herdr_cli
|
|
208
|
+
# rationale even though no equivalent env-var-unreliable incident has been
|
|
209
|
+
# observed for zellij).
|
|
210
|
+
fm_backend_zellij_cli() { # <session> <action-subcommand-and-args...>
|
|
211
|
+
local session=$1
|
|
212
|
+
shift
|
|
213
|
+
ZELLIJ_SESSION_NAME="$session" zellij --session "$session" "$@"
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
# fm_backend_zellij_session_exists: passive, READ-ONLY liveness check - never
|
|
217
|
+
# starts or creates a session (unlike herdr's target_ready, which DOES
|
|
218
|
+
# auto-start its server: a herdr server restart is non-destructive and
|
|
219
|
+
# recovers persisted state, but zellij's `kill-session` is destructive and
|
|
220
|
+
# recreating an unrelated target session under the same name would silently
|
|
221
|
+
# orphan whatever the caller actually meant to reach). Every op below calls
|
|
222
|
+
# this first and fails rather than guessing.
|
|
223
|
+
fm_backend_zellij_session_exists() { # <session>
|
|
224
|
+
zellij list-sessions --short --no-formatting 2>/dev/null | grep -qxF "$1"
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# fm_backend_zellij_server_ensure: create the named session in the background,
|
|
228
|
+
# headless (no attached client), if it does not already exist - mirrors
|
|
229
|
+
# tmux's `tmux has-session || tmux new-session -d` and herdr's server_ensure.
|
|
230
|
+
# Verified: `zellij attach -b <name>` with stdin redirected from /dev/null and
|
|
231
|
+
# no controlling TTY creates the session and returns promptly (it cannot
|
|
232
|
+
# actually attach without a TTY, so it exits after creating); running it again
|
|
233
|
+
# against an EXISTING session prints "Session already exists" and exits 1 -
|
|
234
|
+
# harmless here because existence is checked first and the launch is
|
|
235
|
+
# backgrounded, its exit status never inspected.
|
|
236
|
+
fm_backend_zellij_server_ensure() { # <session>
|
|
237
|
+
local session=$1 i
|
|
238
|
+
fm_backend_zellij_session_exists "$session" && return 0
|
|
239
|
+
( nohup zellij attach -b "$session" </dev/null >/dev/null 2>&1 & ) || return 1
|
|
240
|
+
for i in $(seq 1 20); do
|
|
241
|
+
fm_backend_zellij_session_exists "$session" && return 0
|
|
242
|
+
sleep 0.5
|
|
243
|
+
done
|
|
244
|
+
echo "error: zellij session '$session' did not come up within 10s" >&2
|
|
245
|
+
return 1
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# fm_backend_zellij_container_ensure: the full spawn-time container-ensure
|
|
249
|
+
# sequence (version gate, session). Echoes the session name (no second
|
|
250
|
+
# "workspace" component - zellij has no such concept, unlike herdr).
|
|
251
|
+
fm_backend_zellij_container_ensure() {
|
|
252
|
+
local session
|
|
253
|
+
fm_backend_zellij_version_check || return 1
|
|
254
|
+
session=$(fm_backend_zellij_session)
|
|
255
|
+
fm_backend_zellij_server_ensure "$session" || return 1
|
|
256
|
+
printf '%s' "$session"
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
# fm_backend_zellij_pane_for_tab: the terminal (non-plugin) pane id for
|
|
260
|
+
# <tab_id> in <session>, via one list-panes call filtered by tab_id and
|
|
261
|
+
# is_plugin==false. Terminal pane ids are globally unique across a session's
|
|
262
|
+
# whole tab set (verified: sequential across tabs, a SEPARATE numbering
|
|
263
|
+
# namespace from plugin panes, which is why a plugin pane and a terminal pane
|
|
264
|
+
# can share the same bare "id" - the CLI's own --pane-id contract, "3
|
|
265
|
+
# (equivalent to terminal_3)", already documents this split. Never assumes a
|
|
266
|
+
# tab-position/pane-number correspondence.
|
|
267
|
+
fm_backend_zellij_pane_for_tab() { # <session> <tab_id>
|
|
268
|
+
local session=$1 tab_id=$2
|
|
269
|
+
fm_backend_zellij_cli "$session" action list-panes --json 2>/dev/null \
|
|
270
|
+
| jq -r --argjson t "$tab_id" '.[]? | select(.tab_id == $t and .is_plugin == false) | .id' 2>/dev/null | head -1
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# fm_backend_zellij_tab_for_pane: the owning tab id for <pane_id> in
|
|
274
|
+
# <session>, the reverse lookup kill needs (meta stores only the pane in the
|
|
275
|
+
# target string; the tab id is looked up fresh rather than trusted stale,
|
|
276
|
+
# mirroring herdr's label-based, never-trust-a-stored-id recovery posture).
|
|
277
|
+
fm_backend_zellij_tab_for_pane() { # <session> <pane_id>
|
|
278
|
+
local session=$1 pane_id=$2
|
|
279
|
+
fm_backend_zellij_cli "$session" action list-panes --json 2>/dev/null \
|
|
280
|
+
| jq -r --argjson p "$pane_id" '.[]? | select(.id == $p and .is_plugin == false) | .tab_id' 2>/dev/null | head -1
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
fm_backend_zellij_pane_exists() { # <session> <pane_id>
|
|
284
|
+
local session=$1 pane_id=$2
|
|
285
|
+
fm_backend_zellij_cli "$session" action list-panes --json 2>/dev/null \
|
|
286
|
+
| jq -e --argjson p "$pane_id" '[.[]? | select(.id == $p and .is_plugin == false)] | length > 0' >/dev/null 2>&1
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
# fm_backend_zellij_tab_matches_label: does <tab_id> in <session> carry the
|
|
290
|
+
# tab name firstmate expects for the caller-facing task label <label>?
|
|
291
|
+
# Checks the home-scoped, tagged title first (fm_backend_zellij_scoped_title
|
|
292
|
+
# - what every NEW tab is created with), then falls back to the legacy
|
|
293
|
+
# untagged bare title (the plain <label>, e.g. "fm-<id>") for a tab created
|
|
294
|
+
# before this home-scoping change shipped - but ONLY when that bare name is
|
|
295
|
+
# not ambiguous: exactly one live tab in the whole session carries it. A bare
|
|
296
|
+
# name shared by 2+ live tabs (this home's own pre-migration tab plus, say, a
|
|
297
|
+
# same-named tab from a different firstmate home sharing this one zellij
|
|
298
|
+
# session) refuses rather than silently trusting whichever one happened to
|
|
299
|
+
# match - the migration posture documented in docs/zellij-backend.md
|
|
300
|
+
# "Home-scoped tab titles". One list-tabs call serves every check here (the
|
|
301
|
+
# scoped check, the bare check, and the ambiguity count all read the SAME
|
|
302
|
+
# already-fetched JSON), so a caller whose fake-CLI fixture supplies exactly
|
|
303
|
+
# one list-tabs response keeps working unchanged.
|
|
304
|
+
fm_backend_zellij_tab_matches_label() { # <session> <tab_id> <label>
|
|
305
|
+
local session=$1 tab_id=$2 label=$3 scoped tabs count
|
|
306
|
+
scoped=$(fm_backend_zellij_scoped_title "$label")
|
|
307
|
+
tabs=$(fm_backend_zellij_cli "$session" action list-tabs --json 2>/dev/null)
|
|
308
|
+
printf '%s' "$tabs" | jq -e --argjson t "$tab_id" --arg want "$scoped" \
|
|
309
|
+
'[.[]? | select(.tab_id == $t and .name == $want)] | length > 0' >/dev/null 2>&1 && return 0
|
|
310
|
+
printf '%s' "$tabs" | jq -e --argjson t "$tab_id" --arg want "$label" \
|
|
311
|
+
'[.[]? | select(.tab_id == $t and .name == $want)] | length > 0' >/dev/null 2>&1 || return 1
|
|
312
|
+
count=$(printf '%s' "$tabs" | jq -r --arg want "$label" '[.[]? | select(.name == $want)] | length' 2>/dev/null)
|
|
313
|
+
[ "$count" = "1" ]
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
# fm_backend_zellij_create_task: create the task's tab (one terminal pane) in
|
|
317
|
+
# <session>, refusing an existing <label>. Zellij does NOT enforce tab-name
|
|
318
|
+
# uniqueness itself (verified: two tabs can share a name), so the duplicate
|
|
319
|
+
# check is ours, mirroring both tmux's and herdr's adapters. The tab is
|
|
320
|
+
# always created with the home-scoped, tagged title
|
|
321
|
+
# (fm_backend_zellij_scoped_title), never the bare caller-facing label - see
|
|
322
|
+
# the file header's "Home-scoped tab titles" note.
|
|
323
|
+
#
|
|
324
|
+
# Focus-steal mitigation (verified real finding, no upstream suppression
|
|
325
|
+
# flag exists): `new-tab` unconditionally focuses the created tab for every
|
|
326
|
+
# attached client. Capture the previously-active tab id (if any) before
|
|
327
|
+
# creating, and restore it with `go-to-tab-by-id` afterward - verified to
|
|
328
|
+
# correctly move an attached client's view back and to be a safe, silent
|
|
329
|
+
# no-op when no client is attached (the common case: an unattended firstmate
|
|
330
|
+
# spawn). Best-effort: a failure to restore never fails the spawn.
|
|
331
|
+
#
|
|
332
|
+
# Echoes "<tab_id> <pane_id>" on success.
|
|
333
|
+
fm_backend_zellij_create_task() { # <session> <label> <cwd>
|
|
334
|
+
local session=$1 label=$2 cwd=$3 title tabs dup prev_active tab_id pane_id
|
|
335
|
+
fm_backend_zellij_session_exists "$session" || { echo "error: zellij session '$session' does not exist; run container_ensure first" >&2; return 1; }
|
|
336
|
+
title=$(fm_backend_zellij_scoped_title "$label")
|
|
337
|
+
tabs=$(fm_backend_zellij_cli "$session" action list-tabs --json 2>/dev/null)
|
|
338
|
+
dup=$(printf '%s' "$tabs" | jq -r --arg want "$title" '.[]? | select(.name == $want) | .tab_id' 2>/dev/null | head -1)
|
|
339
|
+
if [ -n "$dup" ]; then
|
|
340
|
+
echo "error: zellij tab '$title' already exists in session '$session'" >&2
|
|
341
|
+
return 1
|
|
342
|
+
fi
|
|
343
|
+
prev_active=$(printf '%s' "$tabs" | jq -r '.[]? | select(.active == true) | .tab_id' 2>/dev/null | head -1)
|
|
344
|
+
tab_id=$(fm_backend_zellij_cli "$session" action new-tab --cwd "$cwd" --name "$title" 2>/dev/null | tr -d '[:space:]')
|
|
345
|
+
case "$tab_id" in
|
|
346
|
+
''|*[!0-9]*)
|
|
347
|
+
echo "error: zellij new-tab did not return a numeric tab id for '$title' (got '$tab_id'; session '$session' may not exist)" >&2
|
|
348
|
+
return 1
|
|
349
|
+
;;
|
|
350
|
+
esac
|
|
351
|
+
pane_id=$(fm_backend_zellij_pane_for_tab "$session" "$tab_id")
|
|
352
|
+
if [ -z "$pane_id" ]; then
|
|
353
|
+
echo "error: could not find a terminal pane for zellij tab $tab_id (session '$session')" >&2
|
|
354
|
+
return 1
|
|
355
|
+
fi
|
|
356
|
+
if [ -n "$prev_active" ] && [ "$prev_active" != "$tab_id" ]; then
|
|
357
|
+
fm_backend_zellij_cli "$session" action go-to-tab-by-id "$prev_active" >/dev/null 2>&1 || true
|
|
358
|
+
fi
|
|
359
|
+
printf '%s %s' "$tab_id" "$pane_id"
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
# fm_backend_zellij_parse_target: split "<session>:<pane_id>" on the FIRST
|
|
363
|
+
# colon (the pane id is a bare integer with no embedded colon, so this is
|
|
364
|
+
# simpler than herdr's equivalent but kept structurally parallel). Sets
|
|
365
|
+
# FM_BACKEND_ZELLIJ_SESSION and FM_BACKEND_ZELLIJ_PANE for the caller.
|
|
366
|
+
fm_backend_zellij_parse_target() { # <target>
|
|
367
|
+
local target=$1
|
|
368
|
+
FM_BACKEND_ZELLIJ_SESSION=${target%%:*}
|
|
369
|
+
FM_BACKEND_ZELLIJ_PANE=${target#*:}
|
|
370
|
+
[ -n "$FM_BACKEND_ZELLIJ_SESSION" ] && [ -n "$FM_BACKEND_ZELLIJ_PANE" ] && [ "$FM_BACKEND_ZELLIJ_PANE" != "$target" ]
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
# fm_backend_zellij_target_ready: parse the target and verify its session and
|
|
374
|
+
# pane are alive. When the caller knows the owning firstmate task label, verify
|
|
375
|
+
# the pane belongs to that named tab before trusting the numeric pane id.
|
|
376
|
+
fm_backend_zellij_target_ready() { # <target> [expected-label]
|
|
377
|
+
local expected_label=${2:-} tab_id
|
|
378
|
+
fm_backend_zellij_parse_target "$1" || return 1
|
|
379
|
+
fm_backend_zellij_session_exists "$FM_BACKEND_ZELLIJ_SESSION" || return 1
|
|
380
|
+
if [ -n "$expected_label" ]; then
|
|
381
|
+
tab_id=$(fm_backend_zellij_tab_for_pane "$FM_BACKEND_ZELLIJ_SESSION" "$FM_BACKEND_ZELLIJ_PANE" 2>/dev/null)
|
|
382
|
+
[ -n "$tab_id" ] || return 1
|
|
383
|
+
fm_backend_zellij_tab_matches_label "$FM_BACKEND_ZELLIJ_SESSION" "$tab_id" "$expected_label"
|
|
384
|
+
return $?
|
|
385
|
+
fi
|
|
386
|
+
fm_backend_zellij_pane_exists "$FM_BACKEND_ZELLIJ_SESSION" "$FM_BACKEND_ZELLIJ_PANE"
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
# fm_backend_zellij_current_path: the live pane's cwd, or empty on any error.
|
|
390
|
+
# Mirrors tmux's pane_current_path poll used for worktree-path discovery after
|
|
391
|
+
# `treehouse get`.
|
|
392
|
+
#
|
|
393
|
+
# Verified pitfall (docs/zellij-backend.md "Worktree-path discovery: pane_cwd
|
|
394
|
+
# does not track a subshell"): `list-panes --json`'s `pane_cwd` DOES reflect a
|
|
395
|
+
# `cd` run directly in the pane's own top-level shell, but stays FROZEN at
|
|
396
|
+
# whatever directory the pane's shell was in when it launched `treehouse get`
|
|
397
|
+
# as a foreground command - it never follows that command's own internal `cd`
|
|
398
|
+
# into the acquired worktree, even after the subshell is fully interactive and
|
|
399
|
+
# a `pwd` typed into it prints the correct live path on screen. Zellij's CLI
|
|
400
|
+
# exposes no per-pane pid and no live-process cwd field to read instead
|
|
401
|
+
# (unlike herdr's `foreground_cwd`), so passive JSON polling cannot solve
|
|
402
|
+
# this. Active probe instead: print the pane's `$PWD` with a unique marker
|
|
403
|
+
# (atomically submitted, mirroring send_text_line), briefly settle, then capture
|
|
404
|
+
# and read only that marker line. Scoped to fm-spawn.sh's own worktree-discovery
|
|
405
|
+
# poll loop (the only caller of this op), where injecting a harmless extra
|
|
406
|
+
# command before the harness ever launches is an acceptable trade for a reliable
|
|
407
|
+
# answer.
|
|
408
|
+
fm_backend_zellij_current_path() { # <target> [expected-label]
|
|
409
|
+
local target=$1 expected_label=${2:-} out line marker_begin="__FM_ZELLIJ_CWD_BEGIN__" marker_end="__FM_ZELLIJ_CWD_END__" in_block=0 chunk="" last=""
|
|
410
|
+
fm_backend_zellij_target_ready "$target" "$expected_label" || return 0
|
|
411
|
+
fm_backend_zellij_send_text_line "$target" "printf '%s\n' '$marker_begin'; pwd; printf '%s\n' '$marker_end'" "$expected_label" || return 0
|
|
412
|
+
sleep 0.3
|
|
413
|
+
out=$(fm_backend_zellij_capture "$target" 200 "$expected_label") || return 0
|
|
414
|
+
while IFS= read -r line; do
|
|
415
|
+
if [ "$line" = "$marker_begin" ]; then
|
|
416
|
+
in_block=1
|
|
417
|
+
chunk=""
|
|
418
|
+
continue
|
|
419
|
+
fi
|
|
420
|
+
if [ "$line" = "$marker_end" ]; then
|
|
421
|
+
case "$chunk" in /*) last=$chunk ;; esac
|
|
422
|
+
in_block=0
|
|
423
|
+
continue
|
|
424
|
+
fi
|
|
425
|
+
[ "$in_block" -eq 1 ] && chunk="$chunk$line"
|
|
426
|
+
done <<EOF
|
|
427
|
+
$out
|
|
428
|
+
EOF
|
|
429
|
+
printf '%s' "$last"
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
# fm_backend_zellij_send_literal: send TEXT as literal, UNSUBMITTED input via
|
|
433
|
+
# bracketed paste - the caller sends Enter separately. Mirrors tmux's
|
|
434
|
+
# `send-keys -t T -l text` / herdr's `pane send-text`. Verified: `action
|
|
435
|
+
# paste` does NOT auto-submit and uses bracketed paste mode (the report's
|
|
436
|
+
# recommendation over write-chars, for popup-safety parity with tmux/herdr).
|
|
437
|
+
fm_backend_zellij_send_literal() { # <target> <text> [expected-label]
|
|
438
|
+
fm_backend_zellij_target_ready "$1" "${3:-}" || return 1
|
|
439
|
+
fm_backend_zellij_cli "$FM_BACKEND_ZELLIJ_SESSION" action paste --pane-id "$FM_BACKEND_ZELLIJ_PANE" -- "$2" >/dev/null 2>&1
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
# fm_backend_zellij_normalize_key: map firstmate's key vocabulary (Enter,
|
|
443
|
+
# Escape, C-c, as used by fm-send.sh --key and stuck-crewmate-recovery) onto
|
|
444
|
+
# zellij's verified `action send-keys` names. Verified empirically: "Enter"
|
|
445
|
+
# and "Esc" work; "Escape" and "escape" are REJECTED ("Invalid key"); Ctrl-C
|
|
446
|
+
# must be the single argument "Ctrl c" (a space-separated two-word key
|
|
447
|
+
# expression passed as ONE shell arg) - "C-c", "Ctrl+c", and two separate argv
|
|
448
|
+
# words all fail.
|
|
449
|
+
fm_backend_zellij_normalize_key() { # <key>
|
|
450
|
+
case "$1" in
|
|
451
|
+
Enter|enter) printf 'Enter' ;;
|
|
452
|
+
Escape|escape|Esc|esc) printf 'Esc' ;;
|
|
453
|
+
C-c|c-c|ctrl+c|Ctrl+c|Ctrl+C|'Ctrl c'|'ctrl c') printf 'Ctrl c' ;;
|
|
454
|
+
# C-u clears a composer line. fm-send.sh's muse interrupt path needs it to
|
|
455
|
+
# drop the prompt muse restores into the composer after Escape.
|
|
456
|
+
C-u|c-u|ctrl+u|Ctrl+u|Ctrl+U|'Ctrl u'|'ctrl u') printf 'Ctrl u' ;;
|
|
457
|
+
*) printf '%s' "$1" ;;
|
|
458
|
+
esac
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
# fm_backend_zellij_send_key: one named special key, targeted at the pane by
|
|
462
|
+
# its EXPLICIT --pane-id (never the ambient "focused pane" default - verified
|
|
463
|
+
# unreliable, see file header). Mirrors fm-send.sh's --key path.
|
|
464
|
+
fm_backend_zellij_send_key() { # <target> <key> [expected-label]
|
|
465
|
+
fm_backend_zellij_target_ready "$1" "${3:-}" || return 1
|
|
466
|
+
local key
|
|
467
|
+
key=$(fm_backend_zellij_normalize_key "$2")
|
|
468
|
+
fm_backend_zellij_cli "$FM_BACKEND_ZELLIJ_SESSION" action send-keys --pane-id "$FM_BACKEND_ZELLIJ_PANE" "$key" >/dev/null 2>&1
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
# fm_backend_zellij_send_text_line: send one line of TEXT then submit.
|
|
472
|
+
fm_backend_zellij_send_text_line() { # <target> <text> [expected-label]
|
|
473
|
+
fm_backend_zellij_send_literal "$1" "$2" "${3:-}" || return 1
|
|
474
|
+
fm_backend_zellij_send_key "$1" Enter "${3:-}" && return 0
|
|
475
|
+
fm_backend_zellij_send_key "$1" C-c "${3:-}" >/dev/null 2>&1 && return 1
|
|
476
|
+
return 2
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
# fm_backend_zellij_capture: bounded plain-text pane capture. Mirrors
|
|
480
|
+
# fm-peek.sh's/fm-watch.sh's `tmux capture-pane -p -t T -S -N`. `dump-screen`
|
|
481
|
+
# has no --lines bound, so routine 40-line-or-smaller reads use the current
|
|
482
|
+
# viewport and larger explicit reads use --full scrollback, then trim locally.
|
|
483
|
+
# On a very short viewport, a small read can see fewer than the requested lines.
|
|
484
|
+
fm_backend_zellij_capture() { # <target> <lines> [expected-label]
|
|
485
|
+
fm_backend_zellij_target_ready "$1" "${3:-}" || return 1
|
|
486
|
+
local lines=${2:-40} out
|
|
487
|
+
case "$lines" in ''|*[!0-9]*) lines=40 ;; esac
|
|
488
|
+
if [ "$lines" -le 40 ]; then
|
|
489
|
+
out=$(fm_backend_zellij_cli "$FM_BACKEND_ZELLIJ_SESSION" action dump-screen --pane-id "$FM_BACKEND_ZELLIJ_PANE" 2>/dev/null) || return 1
|
|
490
|
+
else
|
|
491
|
+
out=$(fm_backend_zellij_cli "$FM_BACKEND_ZELLIJ_SESSION" action dump-screen --pane-id "$FM_BACKEND_ZELLIJ_PANE" --full 2>/dev/null) || return 1
|
|
492
|
+
fi
|
|
493
|
+
printf '%s' "$out" | tail -n "$lines"
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
# --- zellij composer capture and capability primitives ----------------------
|
|
497
|
+
#
|
|
498
|
+
# `zellij action dump-screen --ansi` ("Preserve ANSI styling in the dump
|
|
499
|
+
# output", verified live at zellij 0.44.0 against real Claude Code) gives
|
|
500
|
+
# zellij a styled capture, so the shared classifier reads its composer with
|
|
501
|
+
# the same ghost-stripping confidence as tmux and herdr. Every shape lives in
|
|
502
|
+
# the shared owner (bin/fm-composer-lib.sh, fm_composer_classify_screen);
|
|
503
|
+
# this adapter contributes only the capture and its capability facts.
|
|
504
|
+
|
|
505
|
+
# fm_backend_zellij_composer_capture: bounded styled tail of the pane. When
|
|
506
|
+
# --ansi is unsupported (an older zellij), the caller falls back to the plain
|
|
507
|
+
# dump and a styled=0 descriptor - see fm_backend_zellij_composer_state.
|
|
508
|
+
fm_backend_zellij_composer_capture() { # <target> [expected-label]
|
|
509
|
+
fm_backend_zellij_target_ready "$1" "${2:-}" || return 1
|
|
510
|
+
local out
|
|
511
|
+
out=$(fm_backend_zellij_cli "$FM_BACKEND_ZELLIJ_SESSION" action dump-screen --pane-id "$FM_BACKEND_ZELLIJ_PANE" --ansi 2>/dev/null) || return 1
|
|
512
|
+
[ -n "$out" ] || return 1
|
|
513
|
+
printf '%s' "$out" | tail -n "$FM_COMPOSER_CAPTURE_LINES"
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
# fm_backend_zellij_composer_state: thin adapter - capture plus capabilities
|
|
517
|
+
# in, shared verdict out. This replaced the content-diff submit heuristic
|
|
518
|
+
# that was the fleet's only FALSE-POSITIVE delivery confirmation: a pane
|
|
519
|
+
# whose content changed for any reason (a spinner, streaming output, a
|
|
520
|
+
# clock) read as "submitted", which could close a --resolve-key decision for
|
|
521
|
+
# a message the crew never received. A dead pane still fails safe here: the
|
|
522
|
+
# unconditional-exit-0 CLI quirk (file header) yields an empty dump, which
|
|
523
|
+
# classifies unknown - never a confirmation.
|
|
524
|
+
fm_backend_zellij_composer_state() { # <target> [expected-label] -> empty|pending|pending-unproven|unknown
|
|
525
|
+
local target=$1 expected_label=${2:-} cap caps verdict
|
|
526
|
+
if cap=$(fm_backend_zellij_composer_capture "$target" "$expected_label"); then
|
|
527
|
+
caps=$(printf 'styled=1\ncursor=0\nidentity=0\nrows=%s' "$FM_COMPOSER_CAPTURE_LINES")
|
|
528
|
+
elif cap=$(fm_backend_zellij_capture "$target" "$FM_COMPOSER_CAPTURE_LINES" "$expected_label") && [ -n "$cap" ]; then
|
|
529
|
+
caps=$(printf 'styled=0\ncursor=0\nidentity=0\nrows=%s' "$FM_COMPOSER_CAPTURE_LINES")
|
|
530
|
+
else
|
|
531
|
+
printf 'unknown'
|
|
532
|
+
return 0
|
|
533
|
+
fi
|
|
534
|
+
verdict=$(fm_composer_classify_screen "$caps" "$cap")
|
|
535
|
+
[ "$verdict" != need-identity ] || verdict=unknown
|
|
536
|
+
printf '%s' "$verdict"
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
fm_backend_zellij_composer_content() { # <target> [expected-label]
|
|
540
|
+
local target=$1 expected_label=${2:-} cap caps
|
|
541
|
+
cap=$(fm_backend_zellij_composer_capture "$target" "$expected_label") || return 1
|
|
542
|
+
caps=$(printf 'styled=1\ncursor=0\nidentity=0\nrows=%s' "$FM_COMPOSER_CAPTURE_LINES")
|
|
543
|
+
fm_composer_extract_selected_content "$caps" "$cap"
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
fm_backend_zellij_composer_observed_append() { # <target> <before> <text> [expected-label]
|
|
547
|
+
local target=$1 before=$2 text=$3 expected_label=${4:-} cap caps after expected
|
|
548
|
+
[ -n "$text" ] || return 1
|
|
549
|
+
cap=$(fm_backend_zellij_composer_capture "$target" "$expected_label") || return 1
|
|
550
|
+
caps=$(printf 'styled=1\ncursor=0\nidentity=0\nrows=%s' "$FM_COMPOSER_CAPTURE_LINES")
|
|
551
|
+
after=$(fm_composer_extract_selected_content "$caps" "$cap") || return 1
|
|
552
|
+
fm_composer_normalize_spaces_var before
|
|
553
|
+
fm_composer_normalize_spaces_var text
|
|
554
|
+
fm_composer_normalize_spaces_var after
|
|
555
|
+
before=${before//[$' \t\r\n\v\f']/}
|
|
556
|
+
text=${text//[$' \t\r\n\v\f']/}
|
|
557
|
+
after=${after//[$' \t\r\n\v\f']/}
|
|
558
|
+
[ -n "$text" ] || return 1
|
|
559
|
+
expected=$before$text
|
|
560
|
+
[ "$after" = "$expected" ]
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
# fm_backend_zellij_send_text_submit: type <text> into <target> once (raw,
|
|
564
|
+
# unsubmitted, via send_literal), then drive the shared verify-and-retry-Enter
|
|
565
|
+
# loop (bin/fm-composer-lib.sh: fm_composer_submit_retry_core) against the
|
|
566
|
+
# real composer verdict above. Echoes empty|pending|unknown|send-failed, a
|
|
567
|
+
# subset of the proof-carrying submit vocabulary. Only a positively classified
|
|
568
|
+
# empty composer confirms delivery - a pane that merely CHANGED does not, so
|
|
569
|
+
# the old heuristic's false "delivery confirmed" cannot recur.
|
|
570
|
+
fm_backend_zellij_send_text_submit() { # <target> <text> <retries> <enter-sleep> <settle> [expected-label]
|
|
571
|
+
local target=$1 text=$2 retries=$3 sleep_s=$4 settle=$5 expected_label=${6:-} before
|
|
572
|
+
before=$(fm_backend_zellij_composer_content "$target" "$expected_label") \
|
|
573
|
+
|| { printf 'send-failed'; return 0; }
|
|
574
|
+
fm_backend_zellij_send_literal "$target" "$text" "$expected_label" || { printf 'send-failed'; return 0; }
|
|
575
|
+
sleep "$settle"
|
|
576
|
+
fm_backend_zellij_composer_observed_append "$target" "$before" "$text" "$expected_label" \
|
|
577
|
+
|| { printf 'send-failed'; return 0; }
|
|
578
|
+
fm_composer_submit_retry_core fm_backend_zellij_send_key fm_backend_zellij_composer_state \
|
|
579
|
+
"$target" "$retries" "$sleep_s" "$expected_label"
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
# fm_backend_zellij_kill: remove the task's tab, best-effort (mirrors
|
|
583
|
+
# tmux-kill-window's/herdr-pane-close's `|| true` contract). Verified: unlike
|
|
584
|
+
# herdr, closing a zellij tab's only PANE does NOT close the tab itself (an
|
|
585
|
+
# empty tab survives in list-tabs); `close-tab-by-id` on a live tab DOES
|
|
586
|
+
# cleanly remove both the pane and the tab in one call, verified to need no
|
|
587
|
+
# separate pane-close first. The owning tab id is looked up fresh from the
|
|
588
|
+
# pane id when possible via fm_backend_zellij_tab_for_pane; teardown also
|
|
589
|
+
# passes the recorded tab id and expected tab label for already-empty ghost
|
|
590
|
+
# tabs. Any tab id is verified against the expected label when one is provided.
|
|
591
|
+
fm_backend_zellij_kill() { # <target> [tab_id] [expected_label]
|
|
592
|
+
fm_backend_zellij_parse_target "$1" || return 0
|
|
593
|
+
fm_backend_zellij_session_exists "$FM_BACKEND_ZELLIJ_SESSION" || return 0
|
|
594
|
+
local tab_id fallback_tab_id=${2:-} expected_label=${3:-}
|
|
595
|
+
tab_id=$(fm_backend_zellij_tab_for_pane "$FM_BACKEND_ZELLIJ_SESSION" "$FM_BACKEND_ZELLIJ_PANE" 2>/dev/null)
|
|
596
|
+
if [ -n "$tab_id" ] && [ -n "$expected_label" ] && ! fm_backend_zellij_tab_matches_label "$FM_BACKEND_ZELLIJ_SESSION" "$tab_id" "$expected_label"; then
|
|
597
|
+
tab_id=
|
|
598
|
+
fi
|
|
599
|
+
case "$fallback_tab_id" in
|
|
600
|
+
''|*[!0-9]*) ;;
|
|
601
|
+
*)
|
|
602
|
+
if [ -z "$tab_id" ]; then
|
|
603
|
+
if [ -z "$expected_label" ] || fm_backend_zellij_tab_matches_label "$FM_BACKEND_ZELLIJ_SESSION" "$fallback_tab_id" "$expected_label"; then
|
|
604
|
+
tab_id=$fallback_tab_id
|
|
605
|
+
fi
|
|
606
|
+
fi
|
|
607
|
+
;;
|
|
608
|
+
esac
|
|
609
|
+
if [ -n "$tab_id" ]; then
|
|
610
|
+
fm_backend_zellij_cli "$FM_BACKEND_ZELLIJ_SESSION" action close-tab-by-id "$tab_id" >/dev/null 2>&1 || true
|
|
611
|
+
elif [ -z "$expected_label" ]; then
|
|
612
|
+
fm_backend_zellij_cli "$FM_BACKEND_ZELLIJ_SESSION" action close-pane --pane-id "$FM_BACKEND_ZELLIJ_PANE" >/dev/null 2>&1 || true
|
|
613
|
+
fi
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
# fm_backend_zellij_list_live: recovery/orphan discovery. Lists every tab in
|
|
617
|
+
# <session> whose title carries THIS firstmate home's own tag
|
|
618
|
+
# (fm-<hometag>-, fm_backend_zellij_home_label) - never any other home's
|
|
619
|
+
# tagged tabs, and never a bare untagged "fm-<id>" tab either (this sweep
|
|
620
|
+
# deliberately does NOT attempt the legacy-bare-title fallback
|
|
621
|
+
# fm_backend_zellij_tab_matches_label uses for a single already-known tab:
|
|
622
|
+
# telling apart "our own pre-migration tab" from "another home's same-shaped
|
|
623
|
+
# bare title" in a bulk, no-numeric-id-in-hand sweep is not something this
|
|
624
|
+
# adapter can do safely - see docs/zellij-backend.md "Home-scoped tab
|
|
625
|
+
# titles"). A pre-migration task is still reachable through its recorded
|
|
626
|
+
# window= meta, which target_ready/kill DO accept via that bare-title
|
|
627
|
+
# fallback. One "<session>:<pane_id>\t<plain fm-<id> label>" line per live,
|
|
628
|
+
# in-home task tab (the home tag is stripped back off before printing, so
|
|
629
|
+
# callers see the same plain label they always have). Read-only: a session
|
|
630
|
+
# that does not exist yet simply lists nothing.
|
|
631
|
+
fm_backend_zellij_list_live() { # <session>
|
|
632
|
+
local session=$1 home prefix tabs tab_id name pane_id plain
|
|
633
|
+
fm_backend_zellij_session_exists "$session" || return 0
|
|
634
|
+
home=$(fm_backend_zellij_home_label)
|
|
635
|
+
prefix="fm-$home-"
|
|
636
|
+
tabs=$(fm_backend_zellij_cli "$session" action list-tabs --json 2>/dev/null) || return 0
|
|
637
|
+
while IFS=$'\t' read -r tab_id name; do
|
|
638
|
+
[ -n "$tab_id" ] || continue
|
|
639
|
+
plain=${name#"$prefix"}
|
|
640
|
+
[ -n "$plain" ] || continue
|
|
641
|
+
pane_id=$(fm_backend_zellij_pane_for_tab "$session" "$tab_id") || continue
|
|
642
|
+
[ -n "$pane_id" ] || continue
|
|
643
|
+
printf '%s:%s\tfm-%s\n' "$session" "$pane_id" "$plain"
|
|
644
|
+
done < <(printf '%s' "$tabs" | jq -r --arg prefix "$prefix" '.[]? | select(.name | startswith($prefix)) | "\(.tab_id)\t\(.name)"' 2>/dev/null)
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
# fm_backend_zellij_resolve_bare_selector: the live-tab-listing fallback for
|
|
648
|
+
# an ad hoc selector with no meta (mirrors tmux's list-windows grep and
|
|
649
|
+
# herdr's equivalent). Searches every active zellij session for a tab
|
|
650
|
+
# matching <name>: the home-scoped, tagged title first
|
|
651
|
+
# (fm_backend_zellij_scoped_title), then falls back to an exact bare <name>
|
|
652
|
+
# match ONLY when unambiguous - exactly one tab across every active session
|
|
653
|
+
# carries it - mirroring fm_backend_zellij_tab_matches_label's migration
|
|
654
|
+
# posture. Rare path in practice (zellij tasks normally carry meta);
|
|
655
|
+
# best-effort. Not wired into fm_backend_resolve_selector's dispatcher
|
|
656
|
+
# (bin/fm-backend.sh), mirroring herdr: that bare-selector fallback stays
|
|
657
|
+
# tmux-only by design, and zellij/herdr tasks are targeted via task-selector
|
|
658
|
+
# meta or an explicit recorded target.
|
|
659
|
+
fm_backend_zellij_resolve_bare_selector() { # <name>
|
|
660
|
+
local name=$1 scoped sessions session tabs tab_id count=0 pane_id bare_session='' bare_tab_id=''
|
|
661
|
+
scoped=$(fm_backend_zellij_scoped_title "$name")
|
|
662
|
+
sessions=$(zellij list-sessions --short --no-formatting 2>/dev/null)
|
|
663
|
+
while IFS= read -r session; do
|
|
664
|
+
[ -n "$session" ] || continue
|
|
665
|
+
tabs=$(fm_backend_zellij_cli "$session" action list-tabs --json 2>/dev/null)
|
|
666
|
+
tab_id=$(printf '%s' "$tabs" | jq -r --arg want "$scoped" '.[]? | select(.name == $want) | .tab_id' 2>/dev/null | head -1)
|
|
667
|
+
[ -n "$tab_id" ] || continue
|
|
668
|
+
pane_id=$(fm_backend_zellij_pane_for_tab "$session" "$tab_id") || continue
|
|
669
|
+
[ -n "$pane_id" ] || continue
|
|
670
|
+
printf '%s:%s' "$session" "$pane_id"
|
|
671
|
+
return 0
|
|
672
|
+
done <<EOF
|
|
673
|
+
$sessions
|
|
674
|
+
EOF
|
|
675
|
+
while IFS= read -r session; do
|
|
676
|
+
[ -n "$session" ] || continue
|
|
677
|
+
tabs=$(fm_backend_zellij_cli "$session" action list-tabs --json 2>/dev/null)
|
|
678
|
+
while IFS= read -r tab_id; do
|
|
679
|
+
[ -n "$tab_id" ] || continue
|
|
680
|
+
count=$((count + 1))
|
|
681
|
+
if [ "$count" -eq 1 ]; then
|
|
682
|
+
bare_session=$session
|
|
683
|
+
bare_tab_id=$tab_id
|
|
684
|
+
fi
|
|
685
|
+
done <<EOF_MATCHES
|
|
686
|
+
$(printf '%s' "$tabs" | jq -r --arg want "$name" '.[]? | select(.name == $want) | .tab_id' 2>/dev/null)
|
|
687
|
+
EOF_MATCHES
|
|
688
|
+
done <<EOF
|
|
689
|
+
$sessions
|
|
690
|
+
EOF
|
|
691
|
+
if [ "$count" -eq 1 ]; then
|
|
692
|
+
pane_id=$(fm_backend_zellij_pane_for_tab "$bare_session" "$bare_tab_id") || pane_id=
|
|
693
|
+
if [ -n "$pane_id" ]; then
|
|
694
|
+
printf '%s:%s' "$bare_session" "$pane_id"
|
|
695
|
+
return 0
|
|
696
|
+
fi
|
|
697
|
+
fi
|
|
698
|
+
echo "error: no zellij tab named $name in any active session" >&2
|
|
699
|
+
return 1
|
|
700
|
+
}
|