@zerwiz/ymir 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 +290 -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 +50 -0
- package/.agents/config/agents.yaml.example +72 -0
- package/.agents/config/crew-dispatch.json +22 -0
- package/.agents/config/cron.yaml +9 -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/config/tailscale-sync.yaml +27 -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/kaia.engram +0 -0
- package/.agents/memory/kaia.engram-shm +0 -0
- package/.agents/memory/kaia.engram-wal +0 -0
- package/.agents/memory/runes_audit.md +5 -0
- package/.agents/memory/well/episodes.jsonl +367 -0
- package/.agents/memory/well/workspace.jsonl +27 -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 +111 -0
- package/.agents/migrations/0004-hoard-and-realms.sh +104 -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 +158 -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 +673 -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 +496 -0
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +571 -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 +161 -0
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +377 -0
- package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +129 -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 +96 -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 +175 -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 +360 -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 +70 -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 +52 -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 +34 -0
- package/.agents/skills/ymir-host/assets/desktop.md +36 -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 +454 -0
- package/CHANGELOG.md +881 -0
- package/LICENSE +1 -1
- package/NOTICE +63 -0
- package/README.md +113 -39
- 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 +65 -0
- package/RULES/05-platforms.md +67 -0
- package/RULES/06-append-only.md +58 -0
- package/RULES/07-config.md +45 -0
- package/RULES/08-delivery-gate.md +75 -0
- package/RULES/README.md +20 -0
- package/bin/README.md +107 -0
- package/bin/a2a-mcp.sh +98 -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 +233 -0
- package/bin/apodex-smoke-test.sh +92 -0
- package/bin/bifrost-bridge.sh +112 -0
- package/bin/bifrost-ingress.sh +77 -0
- package/bin/bootstrap-macos.sh +107 -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 +38 -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-guard.sh +165 -0
- package/bin/crash-sense.sh +160 -0
- package/bin/desktop-place.sh +224 -0
- package/bin/docs-guard.sh +39 -0
- package/bin/editor-place.sh +207 -0
- package/bin/eindri-acclaim.sh +34 -0
- package/bin/eindri-control.sh +28 -0
- package/bin/eindri-role.sh +83 -0
- package/bin/eindri-seen.sh +41 -0
- package/bin/eindri-send.sh +15 -0
- package/bin/eindri-start.sh +85 -0
- package/bin/eindri-watch.sh +57 -0
- package/bin/einherjar-spawn.sh +576 -0
- package/bin/eir-doctor.sh +112 -0
- package/bin/erindi-brief.sh +271 -0
- package/bin/fleet-apply.sh +68 -0
- package/bin/github-deploy.sh +81 -0
- package/bin/gjallarhorn-expose.sh +102 -0
- package/bin/gjallarhorn-notify.sh +58 -0
- package/bin/gjallarhorn-purge.sh +54 -0
- package/bin/gjallarhorn-tunnel.sh +72 -0
- package/bin/gleipnir-lock-lib.sh +284 -0
- package/bin/groa-update.sh +122 -0
- package/bin/hall-snapshot.sh +92 -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 +431 -0
- package/bin/hermes-ensure.sh +88 -0
- package/bin/hoard-lib.sh +25 -0
- package/bin/hodd.sh +78 -0
- package/bin/host-sense.sh +199 -0
- package/bin/huginn-research-worker.sh +108 -0
- package/bin/local-model-lock.sh +51 -0
- package/bin/mimir-bridge.py +226 -0
- package/bin/mimir-bridge.sh +87 -0
- package/bin/mimir-ingest.sh +131 -0
- package/bin/mimir.sh +113 -0
- package/bin/mjollnir-webhook.sh +59 -0
- package/bin/mjollnir.sh +85 -0
- package/bin/model-hardware.sh +82 -0
- package/bin/model-resolve.sh +147 -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-memory-housekeeping.sh +114 -0
- package/bin/nornir-job-observer.sh +166 -0
- package/bin/nornir-job-skillopt-sleep.sh +76 -0
- package/bin/omarchy-hook-install.sh +60 -0
- package/bin/omarchy-install.sh +148 -0
- package/bin/omarchy-plugins.sh +174 -0
- package/bin/omarchy-sense.sh +149 -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-seat.sh +63 -0
- package/bin/prereq-ensure.sh +166 -0
- package/bin/private-guard.sh +38 -0
- package/bin/project-git.sh +55 -0
- package/bin/public-guard.sh +70 -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 +91 -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 +90 -0
- package/bin/smidja-observe.sh +86 -0
- package/bin/syn-arm-pretool-check.sh +57 -0
- package/bin/syn-asset-pretool-check.sh +76 -0
- package/bin/syn-cd-pretool-check.sh +28 -0
- package/bin/syn-guard-pretool-check.sh +83 -0
- package/bin/syn-turnend-guard.sh +36 -0
- package/bin/syn-watch-arm.sh +99 -0
- package/bin/tailscale-sync.sh +190 -0
- package/bin/telegram-bot.sh +69 -0
- package/bin/toolchain.sh +74 -0
- package/bin/utgard.sh +111 -0
- package/bin/valhalla.sh +115 -0
- package/bin/valknut-load.sh +260 -0
- package/bin/vor-crew-state.sh +124 -0
- package/bin/wedge-notify.sh +47 -0
- package/bin/workspace-provision.sh +80 -0
- package/bin/workspace-rag.sh +150 -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 +641 -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-platform.sh +348 -0
- package/bin/ymir-say.sh +97 -0
- package/bin/ymir-setup-auth.sh +108 -0
- package/bin/ymir-validate.sh +173 -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 +1056 -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 +44 -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 +210 -0
- package/scripts/start.sh +167 -0
- package/scripts/stop.sh +113 -0
- package/install.sh +0 -35
|
@@ -0,0 +1,1360 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tests/fm-backend-zellij.test.sh - fake-zellij-CLI unit tests for the zellij
|
|
3
|
+
# session-provider adapter (bin/backends/zellij.sh), P3 of
|
|
4
|
+
# data/fm-backend-design-d7 (report.md "Zellij Backend"). Mirrors
|
|
5
|
+
# tests/fm-backend-herdr.test.sh's fakebin/command-log convention: a small,
|
|
6
|
+
# LOG-based, canned-response fake `zellij` + real `jq` (jq is a real required
|
|
7
|
+
# tool for this backend, not faked). The real-binary smoke test lives in
|
|
8
|
+
# tests/fm-backend-zellij-smoke.test.sh, gated on the zellij binary actually
|
|
9
|
+
# being installed.
|
|
10
|
+
set -u
|
|
11
|
+
|
|
12
|
+
# shellcheck source=tests/lib.sh
|
|
13
|
+
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
|
14
|
+
|
|
15
|
+
command -v jq >/dev/null 2>&1 || { echo "skip: jq not found (required by the zellij adapter)"; exit 0; }
|
|
16
|
+
|
|
17
|
+
TMP_ROOT=$(fm_test_tmproot fm-backend-zellij-tests)
|
|
18
|
+
|
|
19
|
+
# make_zellij_fakebin: a `zellij` stub that logs every invocation (one line,
|
|
20
|
+
# unit-separated args, to $FM_ZELLIJ_LOG) and returns the canned response for
|
|
21
|
+
# that call read from $FM_ZELLIJ_RESPONSES/<n>.out, consumed IN ORDER (call 1
|
|
22
|
+
# reads 1.out, call 2 reads 2.out, ...), mirroring
|
|
23
|
+
# tests/fm-backend-herdr.test.sh's make_herdr_fakebin. A missing response file
|
|
24
|
+
# means "succeed with empty stdout" (paste/send-keys/close-* are silent on
|
|
25
|
+
# success on the real CLI). `--version` and `list-sessions` are handled
|
|
26
|
+
# specially (not call-counted) since fm_backend_zellij_session_exists calls
|
|
27
|
+
# list-sessions on every op as a passive liveness probe and must not consume
|
|
28
|
+
# the ordered response queue - its canned membership is controlled by
|
|
29
|
+
# FM_ZELLIJ_SESSION_LIST instead.
|
|
30
|
+
make_zellij_fakebin() { # <dir> -> echoes fakebin dir
|
|
31
|
+
local dir=$1 fb="$1/fakebin"
|
|
32
|
+
mkdir -p "$fb"
|
|
33
|
+
cat > "$fb/zellij" <<'SH'
|
|
34
|
+
#!/usr/bin/env bash
|
|
35
|
+
set -u
|
|
36
|
+
LOG="${FM_ZELLIJ_LOG:?}"
|
|
37
|
+
RESP="${FM_ZELLIJ_RESPONSES:?}"
|
|
38
|
+
COUNT_FILE="$RESP/.count"
|
|
39
|
+
{
|
|
40
|
+
printf 'ZELLIJ_SESSION_NAME=%s' "${ZELLIJ_SESSION_NAME:-}"
|
|
41
|
+
for a in "$@"; do printf '\x1f%s' "$a"; done
|
|
42
|
+
printf '\n'
|
|
43
|
+
} >> "$LOG"
|
|
44
|
+
|
|
45
|
+
if [ "${1:-}" = --version ]; then
|
|
46
|
+
printf 'zellij %s\n' "${FM_ZELLIJ_FAKE_VERSION:-0.44.0}"
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
if [ "${1:-}" = list-sessions ]; then
|
|
50
|
+
printf '%s\n' "${FM_ZELLIJ_SESSION_LIST:-}"
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
if [ "${1:-}" = attach ]; then
|
|
54
|
+
exit "${FM_ZELLIJ_ATTACH_EXIT:-0}"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
next=$(( $(cat "$COUNT_FILE" 2>/dev/null || echo 0) + 1 ))
|
|
58
|
+
n=$next
|
|
59
|
+
echo "$n" > "$COUNT_FILE"
|
|
60
|
+
if [ -f "$RESP/$n.exit" ]; then
|
|
61
|
+
exit "$(cat "$RESP/$n.exit")"
|
|
62
|
+
fi
|
|
63
|
+
[ -f "$RESP/$n.out" ] && cat "$RESP/$n.out"
|
|
64
|
+
exit 0
|
|
65
|
+
SH
|
|
66
|
+
chmod +x "$fb/zellij"
|
|
67
|
+
printf '%s\n' "$fb"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
zellij_pane_response() {
|
|
71
|
+
local dir=$1 n=$2 pane=${3:-7} tab=${4:-3}
|
|
72
|
+
printf '[{"id":%s,"tab_id":%s,"is_plugin":false}]\n' "$pane" "$tab" > "$dir/responses/$n.out"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
zellij_tab_response() {
|
|
76
|
+
local dir=$1 n=$2 tab=${3:-3} name=${4:-fm-task}
|
|
77
|
+
printf '[{"tab_id":%s,"name":"%s"}]\n' "$tab" "$name" > "$dir/responses/$n.out"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
zellij_assert_call_order() {
|
|
81
|
+
local log=$1 before=$2 after=$3 msg=$4 before_line after_line
|
|
82
|
+
before_line=$(grep -anF -- "$before" "$log" | head -1 | cut -d: -f1)
|
|
83
|
+
after_line=$(grep -anF -- "$after" "$log" | head -1 | cut -d: -f1)
|
|
84
|
+
[ -n "$before_line" ] || fail "$msg (missing before call: '$before')"
|
|
85
|
+
[ -n "$after_line" ] || fail "$msg (missing after call: '$after')"
|
|
86
|
+
[ "$before_line" -lt "$after_line" ] || fail "$msg"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# zellij_multi_tab_response: a list-tabs --json canned response holding
|
|
90
|
+
# several tabs at once (tab_id/name pairs), for ambiguity/foreign-tag tests.
|
|
91
|
+
zellij_multi_tab_response() { # <dir> <n> <tab1> <name1> [<tab2> <name2> ...]
|
|
92
|
+
local dir=$1 n=$2 json first=1
|
|
93
|
+
shift 2
|
|
94
|
+
json='['
|
|
95
|
+
while [ $# -ge 2 ]; do
|
|
96
|
+
[ "$first" -eq 1 ] || json="$json,"
|
|
97
|
+
json="$json{\"tab_id\":$1,\"name\":\"$2\"}"
|
|
98
|
+
first=0
|
|
99
|
+
shift 2
|
|
100
|
+
done
|
|
101
|
+
json="$json]"
|
|
102
|
+
printf '%s' "$json" > "$dir/responses/$n.out"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# zellij_expected_home_label / zellij_expected_scoped_title: bash-only
|
|
106
|
+
# reimplementations of fm_backend_zellij_home_label/scoped_title (mirroring
|
|
107
|
+
# tests/fm-backend-cmux.test.sh's identical cmux_expected_* helpers), used to
|
|
108
|
+
# build canned fixtures for the home-scoped tab titles this adapter now
|
|
109
|
+
# creates and matches.
|
|
110
|
+
zellij_expected_root_hash() { # <root>
|
|
111
|
+
local root real
|
|
112
|
+
root=$1
|
|
113
|
+
real=$(cd "$root" && pwd -P) || return 1
|
|
114
|
+
if command -v shasum >/dev/null 2>&1; then
|
|
115
|
+
printf '%s' "$real" | shasum -a 256 | awk '{print substr($1,1,8)}'
|
|
116
|
+
elif command -v sha256sum >/dev/null 2>&1; then
|
|
117
|
+
printf '%s' "$real" | sha256sum | awk '{print substr($1,1,8)}'
|
|
118
|
+
else
|
|
119
|
+
printf '%s' "$real" | cksum | awk '{printf "%08x", $1}'
|
|
120
|
+
fi
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
zellij_expected_home_label() { # [home] [root]
|
|
124
|
+
local home=${1:-$ROOT} root=${2:-$ROOT} marker id prefix
|
|
125
|
+
marker="$home/.fm-secondmate-home"
|
|
126
|
+
if [ -f "$marker" ]; then
|
|
127
|
+
id=$(tr -d '[:space:]' < "$marker" 2>/dev/null)
|
|
128
|
+
if [ -n "$id" ]; then
|
|
129
|
+
prefix="2ndmate-$id"
|
|
130
|
+
else
|
|
131
|
+
prefix="firstmate"
|
|
132
|
+
fi
|
|
133
|
+
else
|
|
134
|
+
prefix="firstmate"
|
|
135
|
+
fi
|
|
136
|
+
printf '%s-%s' "$prefix" "$(zellij_expected_root_hash "$root")"
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
zellij_expected_scoped_title() { # <fm-task-label> [home] [root]
|
|
140
|
+
local label=$1 home=${2:-$ROOT} root=${3:-$ROOT} rest
|
|
141
|
+
case "$label" in
|
|
142
|
+
fm-*) rest=${label#fm-} ;;
|
|
143
|
+
*) rest=$label ;;
|
|
144
|
+
esac
|
|
145
|
+
printf 'fm-%s-%s' "$(zellij_expected_home_label "$home" "$root")" "$rest"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# --- version_check / tool_check ----------------------------------------------
|
|
149
|
+
|
|
150
|
+
test_version_check_accepts_current_version() {
|
|
151
|
+
local dir fb status
|
|
152
|
+
dir="$TMP_ROOT/version-ok"; mkdir -p "$dir/responses"
|
|
153
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
154
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_FAKE_VERSION=0.44.0 \
|
|
155
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_version_check' "$ROOT"
|
|
156
|
+
status=$?
|
|
157
|
+
expect_code 0 "$status" "version_check should accept 0.44.0 (the verified minimum)"
|
|
158
|
+
pass "fm_backend_zellij_version_check: accepts the verified minimum (0.44.0)"
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
test_version_check_accepts_newer_version() {
|
|
162
|
+
local dir fb status
|
|
163
|
+
dir="$TMP_ROOT/version-newer"; mkdir -p "$dir/responses"
|
|
164
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
165
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_FAKE_VERSION=0.45.2 \
|
|
166
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_version_check' "$ROOT"
|
|
167
|
+
status=$?
|
|
168
|
+
expect_code 0 "$status" "version_check should accept a newer minor (0.45.2)"
|
|
169
|
+
pass "fm_backend_zellij_version_check: accepts a newer version (0.45.2)"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
test_version_check_refuses_old_version() {
|
|
173
|
+
local dir fb out status
|
|
174
|
+
dir="$TMP_ROOT/version-old"; mkdir -p "$dir/responses"
|
|
175
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
176
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_FAKE_VERSION=0.38.1 \
|
|
177
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_version_check' "$ROOT" 2>&1 )
|
|
178
|
+
status=$?
|
|
179
|
+
[ "$status" -ne 0 ] || fail "version_check should refuse 0.38.1 (below the 0.44 minimum)"
|
|
180
|
+
assert_contains "$out" "0.38.1" "version_check error did not name the rejected version"
|
|
181
|
+
pass "fm_backend_zellij_version_check: refuses an old version loudly"
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
test_version_check_refuses_missing_zellij() {
|
|
185
|
+
local dir out status
|
|
186
|
+
dir="$TMP_ROOT/version-missing"; mkdir -p "$dir/empty-fakebin"
|
|
187
|
+
out=$( PATH="$dir/empty-fakebin:/usr/bin:/bin" \
|
|
188
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_version_check' "$ROOT" 2>&1 )
|
|
189
|
+
status=$?
|
|
190
|
+
[ "$status" -ne 0 ] || fail "version_check should refuse when zellij is not installed"
|
|
191
|
+
assert_contains "$out" "not installed" "version_check did not report zellij as missing"
|
|
192
|
+
pass "fm_backend_zellij_version_check: refuses loudly when zellij is not installed"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# --- session name resolution --------------------------------------------------
|
|
196
|
+
|
|
197
|
+
test_session_defaults_to_firstmate() {
|
|
198
|
+
local out
|
|
199
|
+
out=$( bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_session' "$ROOT" )
|
|
200
|
+
[ "$out" = firstmate ] || fail "default session should be 'firstmate', got '$out'"
|
|
201
|
+
pass "fm_backend_zellij_session: defaults to 'firstmate' when FM_ZELLIJ_SESSION is unset"
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
test_session_honors_override() {
|
|
205
|
+
local out
|
|
206
|
+
out=$( FM_ZELLIJ_SESSION=fm-test-iso bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_session' "$ROOT" )
|
|
207
|
+
[ "$out" = fm-test-iso ] || fail "FM_ZELLIJ_SESSION override was not honored, got '$out'"
|
|
208
|
+
pass "fm_backend_zellij_session: honors the FM_ZELLIJ_SESSION test-isolation override"
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# --- target parsing, key normalization ---------------------------------------
|
|
212
|
+
|
|
213
|
+
test_parse_target() {
|
|
214
|
+
( . "$ROOT/bin/backends/zellij.sh"
|
|
215
|
+
fm_backend_zellij_parse_target "firstmate:5" || exit 1
|
|
216
|
+
[ "$FM_BACKEND_ZELLIJ_SESSION" = firstmate ] || { echo "session mismatch: $FM_BACKEND_ZELLIJ_SESSION" >&2; exit 1; }
|
|
217
|
+
[ "$FM_BACKEND_ZELLIJ_PANE" = "5" ] || { echo "pane mismatch: $FM_BACKEND_ZELLIJ_PANE" >&2; exit 1; }
|
|
218
|
+
) || fail "fm_backend_zellij_parse_target did not split session:pane correctly"
|
|
219
|
+
pass "fm_backend_zellij_parse_target: splits '<session>:<pane_id>' on the first colon"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
test_normalize_key() {
|
|
223
|
+
( . "$ROOT/bin/backends/zellij.sh"
|
|
224
|
+
[ "$(fm_backend_zellij_normalize_key Enter)" = Enter ] || { echo "Enter failed" >&2; exit 1; }
|
|
225
|
+
[ "$(fm_backend_zellij_normalize_key Escape)" = Esc ] || { echo "Escape failed" >&2; exit 1; }
|
|
226
|
+
[ "$(fm_backend_zellij_normalize_key Esc)" = Esc ] || { echo "Esc failed" >&2; exit 1; }
|
|
227
|
+
[ "$(fm_backend_zellij_normalize_key C-c)" = "Ctrl c" ] || { echo "C-c failed" >&2; exit 1; }
|
|
228
|
+
[ "$(fm_backend_zellij_normalize_key ctrl+c)" = "Ctrl c" ] || { echo "ctrl+c failed" >&2; exit 1; }
|
|
229
|
+
) || fail "fm_backend_zellij_normalize_key did not map firstmate's key vocabulary to zellij's verified names"
|
|
230
|
+
pass "fm_backend_zellij_normalize_key: Enter/Escape/C-c map to zellij's verified Enter/Esc/'Ctrl c'"
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
# --- home-scoped tab titles (cross-home collision fix) ------------------------
|
|
234
|
+
|
|
235
|
+
test_scoped_title_uses_primary_home_label() {
|
|
236
|
+
local dir out expected
|
|
237
|
+
dir="$TMP_ROOT/scoped-title-primary"; mkdir -p "$dir"
|
|
238
|
+
expected=$(zellij_expected_scoped_title fm-task1 "$dir")
|
|
239
|
+
out=$( FM_HOME="$dir" bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_scoped_title fm-task1' "$ROOT" )
|
|
240
|
+
[ "$out" = "$expected" ] || fail "primary scoped title should be $expected, got '$out'"
|
|
241
|
+
pass "fm_backend_zellij_scoped_title: scopes a primary task title with firstmate plus root hash"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
test_scoped_title_uses_secondmate_home_label() {
|
|
245
|
+
local dir out expected
|
|
246
|
+
dir="$TMP_ROOT/scoped-title-secondmate"; mkdir -p "$dir"
|
|
247
|
+
printf 'sm-one\n' > "$dir/.fm-secondmate-home"
|
|
248
|
+
expected=$(zellij_expected_scoped_title fm-task1 "$dir")
|
|
249
|
+
out=$( FM_HOME="$dir" bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_scoped_title fm-task1' "$ROOT" )
|
|
250
|
+
[ "$out" = "$expected" ] || fail "secondmate scoped title should be $expected, got '$out'"
|
|
251
|
+
pass "fm_backend_zellij_scoped_title: scopes a secondmate task title with the home marker plus root hash"
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
test_scoped_title_changes_with_root_path() {
|
|
255
|
+
local dir home root_one root_two out_one out_two expected_one expected_two
|
|
256
|
+
dir="$TMP_ROOT/scoped-title-root-hash"; home="$dir/home"; root_one="$dir/root-one"; root_two="$dir/root-two"
|
|
257
|
+
mkdir -p "$home" "$root_one" "$root_two"
|
|
258
|
+
expected_one=$(zellij_expected_scoped_title fm-task1 "$home" "$root_one")
|
|
259
|
+
expected_two=$(zellij_expected_scoped_title fm-task1 "$home" "$root_two")
|
|
260
|
+
out_one=$( FM_HOME="$home" FM_ROOT_OVERRIDE="$root_one" bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_scoped_title fm-task1' "$ROOT" )
|
|
261
|
+
out_two=$( FM_HOME="$home" FM_ROOT_OVERRIDE="$root_two" bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_scoped_title fm-task1' "$ROOT" )
|
|
262
|
+
[ "$out_one" = "$expected_one" ] || fail "scoped title should include root-one hash as $expected_one, got '$out_one'"
|
|
263
|
+
[ "$out_two" = "$expected_two" ] || fail "scoped title should include root-two hash as $expected_two, got '$out_two'"
|
|
264
|
+
[ "$out_one" != "$out_two" ] || fail "scoped titles should differ for distinct FM_ROOT paths"
|
|
265
|
+
pass "fm_backend_zellij_scoped_title: includes the resolved FM_ROOT hash in the home label"
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
test_expected_label_accepts_unambiguous_untagged_legacy_tab() {
|
|
269
|
+
local dir fb
|
|
270
|
+
dir="$TMP_ROOT/label-legacy-unambiguous"; mkdir -p "$dir/responses"
|
|
271
|
+
zellij_pane_response "$dir" 1 7 3
|
|
272
|
+
# 2: list-tabs --json -> exactly ONE live tab, still carrying its
|
|
273
|
+
# pre-migration untagged bare title (never re-tagged) - unambiguous, so
|
|
274
|
+
# the legacy fallback in fm_backend_zellij_tab_matches_label trusts it.
|
|
275
|
+
zellij_tab_response "$dir" 2 3 fm-legacy
|
|
276
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
277
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
278
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
279
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_key firstmate:7 Escape fm-legacy' "$ROOT"
|
|
280
|
+
expect_code 0 $? "send_key should still reach a task tab spawned before home-scoping shipped, when its untagged title is unambiguous"
|
|
281
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-keys'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''Esc' \
|
|
282
|
+
"send_key did not send after accepting the unambiguous legacy label"
|
|
283
|
+
pass "fm_backend_zellij_tab_matches_label: accepts an untagged legacy fm-<id> title when it is the only live tab carrying it"
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
test_expected_label_refuses_ambiguous_untagged_tab() {
|
|
287
|
+
local dir fb status
|
|
288
|
+
dir="$TMP_ROOT/label-ambiguous-untagged"; mkdir -p "$dir/responses"
|
|
289
|
+
zellij_pane_response "$dir" 1 7 3
|
|
290
|
+
# 2: list-tabs --json -> TWO different tabs sharing the exact same bare,
|
|
291
|
+
# untagged legacy title "fm-shared" (our pane's owning tab_id=3 is one of
|
|
292
|
+
# them, but so is an unrelated tab_id=9) - the migration posture requires
|
|
293
|
+
# an untagged bare match to be UNIQUE across the whole session before it
|
|
294
|
+
# is trusted; ambiguity here must refuse loudly rather than assume ours.
|
|
295
|
+
zellij_multi_tab_response "$dir" 2 3 fm-shared 9 fm-shared
|
|
296
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
297
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
298
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
299
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_key firstmate:7 Escape fm-shared' "$ROOT"
|
|
300
|
+
status=$?
|
|
301
|
+
[ "$status" -ne 0 ] || fail "send_key should refuse an ambiguous untagged legacy label shared by 2+ live tabs"
|
|
302
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
303
|
+
"send_key should not send after an ambiguous untagged-label match refuses"
|
|
304
|
+
pass "fm_backend_zellij_tab_matches_label: refuses an untagged legacy label match when 2+ live tabs share it (migration ambiguity guard)"
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
test_list_live_scopes_to_own_home_tag() {
|
|
308
|
+
local dir fb out own_title foreign_title other_root
|
|
309
|
+
dir="$TMP_ROOT/list-live-scope"; mkdir -p "$dir/responses"
|
|
310
|
+
other_root="$dir/other-root"; mkdir -p "$other_root"
|
|
311
|
+
own_title=$(zellij_expected_scoped_title fm-task1)
|
|
312
|
+
foreign_title=$(zellij_expected_scoped_title fm-task2 "$ROOT" "$other_root")
|
|
313
|
+
# 1: list-tabs --json -> our own home-scoped tab, a DIFFERENT installation's
|
|
314
|
+
# home-scoped tab (same prefix shape, different FM_ROOT hash), and an
|
|
315
|
+
# unrelated non-firstmate tab.
|
|
316
|
+
zellij_multi_tab_response "$dir" 1 \
|
|
317
|
+
3 "$own_title" \
|
|
318
|
+
4 "$foreign_title" \
|
|
319
|
+
5 "zsh"
|
|
320
|
+
# 2: list-panes for our own home's task1 tab only
|
|
321
|
+
zellij_pane_response "$dir" 2 7 3
|
|
322
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
323
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
324
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
325
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_list_live firstmate' "$ROOT" )
|
|
326
|
+
[ "$out" = $'firstmate:7\tfm-task1' ] \
|
|
327
|
+
|| fail "list_live should list only this home's own tagged tab with its plain fm-<id> label, got '$out'"
|
|
328
|
+
pass "fm_backend_zellij_list_live: scopes to this home's own tag - excludes a different installation's tagged tab and unrelated tabs"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
test_resolve_bare_selector_prefers_scoped_title() {
|
|
332
|
+
local dir fb out title
|
|
333
|
+
dir="$TMP_ROOT/resolve-scoped"; mkdir -p "$dir/responses"
|
|
334
|
+
title=$(zellij_expected_scoped_title fm-resolve1)
|
|
335
|
+
# 1: list-tabs --json -> the home-scoped tagged tab
|
|
336
|
+
printf '[{"tab_id":6,"name":"%s"}]\n' "$title" > "$dir/responses/1.out"
|
|
337
|
+
# 2: list-panes for tab 6
|
|
338
|
+
zellij_pane_response "$dir" 2 12 6
|
|
339
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
340
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
341
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
342
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_resolve_bare_selector fm-resolve1' "$ROOT" )
|
|
343
|
+
[ "$out" = "firstmate:12" ] || fail "resolve_bare_selector should resolve the home-scoped tagged tab, got '$out'"
|
|
344
|
+
pass "fm_backend_zellij_resolve_bare_selector: matches the home-scoped tagged title first"
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
test_resolve_bare_selector_refuses_ambiguous_untagged() {
|
|
348
|
+
local dir fb out status
|
|
349
|
+
dir="$TMP_ROOT/resolve-ambiguous"; mkdir -p "$dir/responses"
|
|
350
|
+
# 1: list-tabs --json -> two DIFFERENT tabs sharing the same untagged bare name
|
|
351
|
+
zellij_multi_tab_response "$dir" 1 3 fm-resolve2 9 fm-resolve2
|
|
352
|
+
zellij_multi_tab_response "$dir" 2 3 fm-resolve2 9 fm-resolve2
|
|
353
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
354
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
355
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
356
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_resolve_bare_selector fm-resolve2' "$ROOT" 2>&1 )
|
|
357
|
+
status=$?
|
|
358
|
+
[ "$status" -ne 0 ] || fail "resolve_bare_selector should refuse an ambiguous untagged label shared by 2+ live tabs"
|
|
359
|
+
assert_contains "$out" "no zellij tab named" "resolve_bare_selector's refusal did not report the expected not-found error"
|
|
360
|
+
pass "fm_backend_zellij_resolve_bare_selector: refuses an ambiguous untagged legacy label shared by 2+ live tabs"
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
test_resolve_bare_selector_prefers_later_session_scoped_title_over_legacy() {
|
|
364
|
+
local dir fb out title
|
|
365
|
+
dir="$TMP_ROOT/resolve-later-scoped"; mkdir -p "$dir/responses"
|
|
366
|
+
title=$(zellij_expected_scoped_title fm-resolve3)
|
|
367
|
+
zellij_tab_response "$dir" 1 3 fm-resolve3
|
|
368
|
+
zellij_tab_response "$dir" 2 6 "$title"
|
|
369
|
+
zellij_pane_response "$dir" 3 12 6
|
|
370
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
371
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
372
|
+
FM_ZELLIJ_SESSION_LIST=$'legacy-session\nscoped-session' \
|
|
373
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_resolve_bare_selector fm-resolve3' "$ROOT" )
|
|
374
|
+
[ "$out" = "scoped-session:12" ] || fail "resolve_bare_selector should prefer a later session's scoped title over an earlier legacy title, got '$out'"
|
|
375
|
+
pass "fm_backend_zellij_resolve_bare_selector: checks every session for the scoped title before legacy fallback"
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
test_resolve_bare_selector_refuses_cross_session_ambiguous_untagged() {
|
|
379
|
+
local dir fb out status
|
|
380
|
+
dir="$TMP_ROOT/resolve-cross-session-ambiguous"; mkdir -p "$dir/responses"
|
|
381
|
+
zellij_tab_response "$dir" 1 3 fm-resolve4
|
|
382
|
+
zellij_tab_response "$dir" 2 9 fm-resolve4
|
|
383
|
+
zellij_tab_response "$dir" 3 3 fm-resolve4
|
|
384
|
+
zellij_tab_response "$dir" 4 9 fm-resolve4
|
|
385
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
386
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
387
|
+
FM_ZELLIJ_SESSION_LIST=$'one-session\ntwo-session' \
|
|
388
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_resolve_bare_selector fm-resolve4' "$ROOT" 2>&1 )
|
|
389
|
+
status=$?
|
|
390
|
+
[ "$status" -ne 0 ] || fail "resolve_bare_selector should refuse an untagged legacy label that appears once in each of two sessions"
|
|
391
|
+
assert_contains "$out" "no zellij tab named" "resolve_bare_selector's cross-session ambiguity refusal did not report the expected not-found error"
|
|
392
|
+
pass "fm_backend_zellij_resolve_bare_selector: requires untagged legacy labels to be globally unique across sessions"
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# --- session_exists / server_ensure -------------------------------------------
|
|
396
|
+
|
|
397
|
+
test_session_exists_true_when_listed() {
|
|
398
|
+
local dir fb out
|
|
399
|
+
dir="$TMP_ROOT/exists-true"; mkdir -p "$dir/responses"
|
|
400
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
401
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
402
|
+
FM_ZELLIJ_SESSION_LIST=$'firstmate\nother-session' \
|
|
403
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_session_exists firstmate' "$ROOT"
|
|
404
|
+
expect_code 0 $? "session_exists should report true when the session is in the list"
|
|
405
|
+
pass "fm_backend_zellij_session_exists: true when the session name is listed"
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
test_session_exists_false_when_absent() {
|
|
409
|
+
local dir fb
|
|
410
|
+
dir="$TMP_ROOT/exists-false"; mkdir -p "$dir/responses"
|
|
411
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
412
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
413
|
+
FM_ZELLIJ_SESSION_LIST=$'other-session' \
|
|
414
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_session_exists firstmate' "$ROOT" 2>&1 )
|
|
415
|
+
status=$?
|
|
416
|
+
[ "$status" -ne 0 ] || fail "session_exists should report false when the session is absent"
|
|
417
|
+
pass "fm_backend_zellij_session_exists: false when the session name is not listed"
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
test_server_ensure_skips_attach_when_already_exists() {
|
|
421
|
+
local dir fb
|
|
422
|
+
dir="$TMP_ROOT/server-reuse"; mkdir -p "$dir/responses"
|
|
423
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
424
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
425
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
426
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_server_ensure firstmate' "$ROOT"
|
|
427
|
+
expect_code 0 $? "server_ensure should succeed immediately when the session already exists"
|
|
428
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''attach' "server_ensure should not call attach when the session already exists"
|
|
429
|
+
pass "fm_backend_zellij_server_ensure: reuses an existing session without calling attach"
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
# --- dispatch wiring (fm-backend.sh) ------------------------------------------
|
|
433
|
+
|
|
434
|
+
test_dispatch_routes_zellij_backend() {
|
|
435
|
+
fm_backend_validate zellij 2>/dev/null || fail "fm_backend_validate should accept zellij (P3 adds it to FM_BACKEND_KNOWN)"
|
|
436
|
+
pass "fm_backend_validate: zellij is a known backend (P3)"
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
test_dispatch_busy_state_unknown_for_zellij() {
|
|
440
|
+
# shellcheck source=/dev/null
|
|
441
|
+
. "$ROOT/bin/fm-backend.sh"
|
|
442
|
+
[ "$(fm_backend_busy_state zellij 'firstmate:5')" = unknown ] \
|
|
443
|
+
|| fail "fm_backend_busy_state should report unknown for zellij (no native agent-state primitive; D5: watcher falls back to regex, same as tmux)"
|
|
444
|
+
pass "fm_backend_busy_state: zellij (no native primitive) always reports unknown, same as tmux"
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
# --- create_task: duplicate refusal, id parsing, focus-restore mitigation ----
|
|
448
|
+
|
|
449
|
+
test_create_task_refuses_duplicate_label() {
|
|
450
|
+
local dir fb out status title
|
|
451
|
+
dir="$TMP_ROOT/dup-task"; mkdir -p "$dir/responses"
|
|
452
|
+
title=$(zellij_expected_scoped_title fm-dup1)
|
|
453
|
+
# 1: list-tabs --json -> existing tab already carrying the home-scoped title
|
|
454
|
+
printf '[{"tab_id":2,"name":"%s","active":false}]\n' "$title" > "$dir/responses/1.out"
|
|
455
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
456
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
457
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
458
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_create_task firstmate fm-dup1 /tmp/proj' "$ROOT" 2>&1 )
|
|
459
|
+
status=$?
|
|
460
|
+
[ "$status" -ne 0 ] || fail "create_task should refuse an existing tab name (zellij itself does not enforce uniqueness)"
|
|
461
|
+
assert_contains "$out" "already exists" "create_task did not report the duplicate name"
|
|
462
|
+
assert_contains "$out" "$title" "create_task's duplicate error did not name the home-scoped title"
|
|
463
|
+
pass "fm_backend_zellij_create_task: refuses a duplicate home-scoped tab title (zellij's own new-tab has no uniqueness check)"
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
test_create_task_creates_and_parses_ids() {
|
|
467
|
+
local dir fb out title
|
|
468
|
+
dir="$TMP_ROOT/create-task"; mkdir -p "$dir/responses"
|
|
469
|
+
title=$(zellij_expected_scoped_title fm-newtask)
|
|
470
|
+
# 1: list-tabs --json -> no existing tabs, none active
|
|
471
|
+
printf '[]\n' > "$dir/responses/1.out"
|
|
472
|
+
# 2: new-tab --cwd --name -> bare tab id on stdout
|
|
473
|
+
printf '3\n' > "$dir/responses/2.out"
|
|
474
|
+
# 3: list-panes --json -> the new tab's terminal pane
|
|
475
|
+
printf '[{"id":7,"tab_id":3,"is_plugin":false}]\n' > "$dir/responses/3.out"
|
|
476
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
477
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
478
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
479
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_create_task firstmate fm-newtask /tmp/proj' "$ROOT" )
|
|
480
|
+
[ "$out" = "3 7" ] || fail "create_task should echo '<tab_id> <pane_id>', got '$out'"
|
|
481
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''new-tab'$'\x1f''--cwd'$'\x1f''/tmp/proj'$'\x1f''--name'$'\x1f'"$title" \
|
|
482
|
+
"create_task did not call new-tab with the right cwd/home-scoped name"
|
|
483
|
+
pass "fm_backend_zellij_create_task: creates a home-scoped tab and parses tab_id/pane_id from the response"
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
test_create_task_restores_previously_active_tab() {
|
|
487
|
+
local dir fb out
|
|
488
|
+
dir="$TMP_ROOT/focus-restore"; mkdir -p "$dir/responses"
|
|
489
|
+
# 1: list-tabs --json -> tab 0 is currently active
|
|
490
|
+
printf '[{"tab_id":0,"name":"Tab #1","active":true}]\n' > "$dir/responses/1.out"
|
|
491
|
+
# 2: new-tab -> id 4 (this steals focus on the real CLI)
|
|
492
|
+
printf '4\n' > "$dir/responses/2.out"
|
|
493
|
+
# 3: list-panes --json -> tab 4's terminal pane
|
|
494
|
+
printf '[{"id":9,"tab_id":4,"is_plugin":false}]\n' > "$dir/responses/3.out"
|
|
495
|
+
# 4: go-to-tab-by-id 0 (the restore call) - silent on success
|
|
496
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
497
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
498
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
499
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_create_task firstmate fm-focustest /tmp/proj' "$ROOT" )
|
|
500
|
+
[ "$out" = "4 9" ] || fail "create_task should still echo '<tab_id> <pane_id>', got '$out'"
|
|
501
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''go-to-tab-by-id'$'\x1f''0' \
|
|
502
|
+
"create_task did not restore focus to the previously-active tab (verified real-zellij focus-steal mitigation)"
|
|
503
|
+
pass "fm_backend_zellij_create_task: restores focus to the previously-active tab after the steal-focus new-tab call"
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
test_create_task_no_restore_when_new_tab_was_already_active() {
|
|
507
|
+
local dir fb out
|
|
508
|
+
dir="$TMP_ROOT/focus-noop"; mkdir -p "$dir/responses"
|
|
509
|
+
# No active tab at all (no client attached - the common unattended case)
|
|
510
|
+
printf '[]\n' > "$dir/responses/1.out"
|
|
511
|
+
printf '5\n' > "$dir/responses/2.out"
|
|
512
|
+
printf '[{"id":11,"tab_id":5,"is_plugin":false}]\n' > "$dir/responses/3.out"
|
|
513
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
514
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
515
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
516
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_create_task firstmate fm-noclient /tmp/proj' "$ROOT" )
|
|
517
|
+
[ "$out" = "5 11" ] || fail "create_task should still echo '<tab_id> <pane_id>', got '$out'"
|
|
518
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''go-to-tab-by-id' \
|
|
519
|
+
"create_task should not call go-to-tab-by-id when there was no previously-active tab (no attached client)"
|
|
520
|
+
pass "fm_backend_zellij_create_task: skips the restore call when there was no previously-active tab"
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
# --- capture / send_key / send_literal / current_path / kill -----------------
|
|
524
|
+
|
|
525
|
+
test_capture_small_reads_use_viewport_and_trim() {
|
|
526
|
+
local dir fb out
|
|
527
|
+
dir="$TMP_ROOT/capture"; mkdir -p "$dir/responses"
|
|
528
|
+
zellij_pane_response "$dir" 1 7 3
|
|
529
|
+
printf 'line one\nline two\nline three\nline four\n' > "$dir/responses/2.out"
|
|
530
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
531
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
532
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
533
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_capture firstmate:7 2' "$ROOT" )
|
|
534
|
+
[ "$out" = $'line three\nline four' ] || fail "capture should trim to the last N lines locally, got '$out'"
|
|
535
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''dump-screen' \
|
|
536
|
+
"capture did not verify the pane before dump-screen"
|
|
537
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''dump-screen'$'\x1f''--pane-id'$'\x1f''7' \
|
|
538
|
+
"capture did not call dump-screen --pane-id <pane>"
|
|
539
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''--full' \
|
|
540
|
+
"small capture should use zellij's viewport-only dump-screen path"
|
|
541
|
+
pass "fm_backend_zellij_capture: small reads use viewport-only dump-screen and trim to N lines locally"
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
test_capture_large_reads_use_full_scrollback_and_trim() {
|
|
545
|
+
local dir fb out
|
|
546
|
+
dir="$TMP_ROOT/capture-full"; mkdir -p "$dir/responses"
|
|
547
|
+
zellij_pane_response "$dir" 1 7 3
|
|
548
|
+
printf 'line one\nline two\nline three\nline four\n' > "$dir/responses/2.out"
|
|
549
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
550
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
551
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
552
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_capture firstmate:7 80' "$ROOT" )
|
|
553
|
+
[ "$out" = $'line one\nline two\nline three\nline four' ] || fail "large capture should keep available output when fewer than N lines exist, got '$out'"
|
|
554
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''dump-screen' \
|
|
555
|
+
"large capture did not verify the pane before dump-screen"
|
|
556
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''dump-screen'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''--full' \
|
|
557
|
+
"large capture did not request --full scrollback"
|
|
558
|
+
pass "fm_backend_zellij_capture: reads above the watcher-size threshold request --full scrollback"
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
test_capture_fails_when_pane_absent() {
|
|
562
|
+
local dir fb out status
|
|
563
|
+
dir="$TMP_ROOT/capture-no-pane"; mkdir -p "$dir/responses"
|
|
564
|
+
printf '[]\n' > "$dir/responses/1.out"
|
|
565
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
566
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
567
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
568
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_capture firstmate:7 5' "$ROOT" 2>&1 )
|
|
569
|
+
status=$?
|
|
570
|
+
[ "$status" -ne 0 ] || fail "capture should fail when the session exists but the pane does not"
|
|
571
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''dump-screen' \
|
|
572
|
+
"capture should not call dump-screen after pane readiness fails"
|
|
573
|
+
pass "fm_backend_zellij_capture: fails when the specific pane is absent"
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
test_capture_fails_when_session_absent() {
|
|
577
|
+
local dir fb out status
|
|
578
|
+
dir="$TMP_ROOT/capture-no-session"; mkdir -p "$dir/responses"
|
|
579
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
580
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
581
|
+
FM_ZELLIJ_SESSION_LIST="" \
|
|
582
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_capture firstmate:7 5' "$ROOT" 2>&1 )
|
|
583
|
+
status=$?
|
|
584
|
+
[ "$status" -ne 0 ] || fail "capture should fail when the session does not exist (never trust the CLI's unconditional exit 0)"
|
|
585
|
+
pass "fm_backend_zellij_capture: fails when the target session is not listed as active (session_exists pre-check)"
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
test_send_key_normalizes_and_targets_pane() {
|
|
589
|
+
local dir fb
|
|
590
|
+
dir="$TMP_ROOT/sendkey"; mkdir -p "$dir/responses"
|
|
591
|
+
zellij_pane_response "$dir" 1 7 3
|
|
592
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
593
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
594
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
595
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_key firstmate:7 Escape' "$ROOT"
|
|
596
|
+
expect_code 0 $? "send_key should succeed"
|
|
597
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''send-keys' \
|
|
598
|
+
"send_key did not verify the pane before send-keys"
|
|
599
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-keys'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''Esc' "send_key did not normalize Escape to Esc"
|
|
600
|
+
pass "fm_backend_zellij_send_key: normalizes the key (Escape -> Esc) and targets the explicit pane id"
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
test_send_literal_uses_paste_separator_for_option_shaped_text() {
|
|
604
|
+
local dir fb
|
|
605
|
+
dir="$TMP_ROOT/sendliteral"; mkdir -p "$dir/responses"
|
|
606
|
+
zellij_pane_response "$dir" 1 7 3
|
|
607
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
608
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
609
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
610
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_literal firstmate:7 "--help"' "$ROOT"
|
|
611
|
+
expect_code 0 $? "send_literal should succeed"
|
|
612
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''paste' \
|
|
613
|
+
"send_literal did not verify the pane before paste"
|
|
614
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''paste'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''--'$'\x1f''--help' \
|
|
615
|
+
"send_literal did not call paste with a -- separator before the literal payload"
|
|
616
|
+
pass "fm_backend_zellij_send_literal: calls paste with an explicit pane id and a -- separator"
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
test_send_text_line_clears_partial_input_when_enter_fails() {
|
|
620
|
+
local dir fb status log
|
|
621
|
+
dir="$TMP_ROOT/sendline-enter-failure"; mkdir -p "$dir/responses"
|
|
622
|
+
zellij_pane_response "$dir" 1 7 3
|
|
623
|
+
zellij_pane_response "$dir" 3 7 3
|
|
624
|
+
printf '1\n' > "$dir/responses/4.exit"
|
|
625
|
+
zellij_pane_response "$dir" 5 7 3
|
|
626
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
627
|
+
|
|
628
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
629
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" bash -c \
|
|
630
|
+
'. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_line "firstmate:7" "export TRACEPARENT=carrier"' "$ROOT"
|
|
631
|
+
status=$?
|
|
632
|
+
[ "$status" -ne 0 ] || fail "send_text_line should report a failed Enter"
|
|
633
|
+
log=$(cat "$dir/log")
|
|
634
|
+
assert_contains "$log" $'\x1f''paste'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''--'$'\x1f''export TRACEPARENT=carrier' \
|
|
635
|
+
"send_text_line did not paste the trace export before the simulated Enter failure"
|
|
636
|
+
zellij_assert_call_order "$dir/log" $'\x1f''Enter' $'\x1f''Ctrl c' \
|
|
637
|
+
"send_text_line did not clear the partial input after Enter failed"
|
|
638
|
+
pass "fm_backend_zellij_send_text_line: clears partial input when Enter fails"
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
test_send_text_line_reports_unsafe_input_when_cleanup_fails() {
|
|
642
|
+
local dir fb status
|
|
643
|
+
dir="$TMP_ROOT/sendline-cleanup-failure"; mkdir -p "$dir/responses"
|
|
644
|
+
zellij_pane_response "$dir" 1 7 3
|
|
645
|
+
zellij_pane_response "$dir" 3 7 3
|
|
646
|
+
printf '1\n' > "$dir/responses/4.exit"
|
|
647
|
+
zellij_pane_response "$dir" 5 7 3
|
|
648
|
+
printf '1\n' > "$dir/responses/6.exit"
|
|
649
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
650
|
+
|
|
651
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
652
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" bash -c \
|
|
653
|
+
'. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_line "firstmate:7" "export TRACEPARENT=carrier"' "$ROOT"
|
|
654
|
+
status=$?
|
|
655
|
+
expect_code 2 "$status" "send_text_line should distinguish uncleared input"
|
|
656
|
+
zellij_assert_call_order "$dir/log" $'\x1f''Enter' $'\x1f''Ctrl c' \
|
|
657
|
+
"send_text_line did not attempt cleanup after Enter failed"
|
|
658
|
+
pass "fm_backend_zellij_send_text_line: reports unsafe input when cleanup also fails"
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
test_expected_label_allows_matching_task_tab() {
|
|
662
|
+
local dir fb
|
|
663
|
+
dir="$TMP_ROOT/label-match"; mkdir -p "$dir/responses"
|
|
664
|
+
zellij_pane_response "$dir" 1 7 3
|
|
665
|
+
zellij_tab_response "$dir" 2 3 fm-label
|
|
666
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
667
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
668
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
669
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_key firstmate:7 Escape fm-label' "$ROOT"
|
|
670
|
+
expect_code 0 $? "send_key should succeed when the pane belongs to the expected fm-id tab"
|
|
671
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''list-tabs'$'\x1f''--json' \
|
|
672
|
+
"expected-label readiness did not resolve the pane's owning tab before label verification"
|
|
673
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-tabs'$'\x1f''--json' $'\x1f''send-keys' \
|
|
674
|
+
"send_key ran before verifying the owning tab label"
|
|
675
|
+
pass "fm_backend_zellij_target_ready: expected labels allow matching fm-<id> tabs"
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
test_expected_label_rejects_reused_pane_id() {
|
|
679
|
+
local dir fb status
|
|
680
|
+
dir="$TMP_ROOT/label-mismatch"; mkdir -p "$dir/responses"
|
|
681
|
+
zellij_pane_response "$dir" 1 7 3
|
|
682
|
+
zellij_tab_response "$dir" 2 3 not-the-task
|
|
683
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
684
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
685
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
686
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_key firstmate:7 Escape fm-label' "$ROOT"
|
|
687
|
+
status=$?
|
|
688
|
+
[ "$status" -ne 0 ] || fail "send_key should reject a pane whose tab name does not match the expected fm-id label"
|
|
689
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''list-tabs'$'\x1f''--json' \
|
|
690
|
+
"expected-label readiness did not check the pane's owning tab"
|
|
691
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
692
|
+
"send_key should not run after expected-label readiness fails"
|
|
693
|
+
pass "fm_backend_zellij_target_ready: expected labels reject stale pane ids reused by another tab"
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
test_current_path_probes_with_marker_and_ignores_prompt_paths() {
|
|
697
|
+
local dir fb out
|
|
698
|
+
# Verified real-zellij pitfall (docs/zellij-backend.md "Worktree-path
|
|
699
|
+
# discovery: pane_cwd does not track a subshell"): pane_cwd never updates
|
|
700
|
+
# once a subshell (e.g. treehouse get) takes over, so current_path actively
|
|
701
|
+
# prints a marked cwd line and reads only that marker from the capture,
|
|
702
|
+
# rather than reading a JSON field.
|
|
703
|
+
dir="$TMP_ROOT/cwd"; mkdir -p "$dir/responses"
|
|
704
|
+
zellij_pane_response "$dir" 1 7 3
|
|
705
|
+
zellij_pane_response "$dir" 2 7 3
|
|
706
|
+
zellij_pane_response "$dir" 4 7 3
|
|
707
|
+
zellij_pane_response "$dir" 6 7 3
|
|
708
|
+
printf '%s\n' 'scratch-e2e-project HEAD' \
|
|
709
|
+
'/home/fixture/src/project ❯ printf marker' \
|
|
710
|
+
'__FM_ZELLIJ_CWD_BEGIN__' \
|
|
711
|
+
'/home/fixture/.treehouse/fake-' \
|
|
712
|
+
'worktree' \
|
|
713
|
+
'__FM_ZELLIJ_CWD_END__' \
|
|
714
|
+
'/home/fixture/.treehouse/fake-worktree ❯' \
|
|
715
|
+
> "$dir/responses/7.out"
|
|
716
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
717
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
718
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
719
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_current_path firstmate:7' "$ROOT" )
|
|
720
|
+
[ "$out" = "/home/fixture/.treehouse/fake-worktree" ] || fail "current_path should read only the marked cwd line, got '$out'"
|
|
721
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''paste' \
|
|
722
|
+
"current_path did not verify the pane before the cwd probe paste"
|
|
723
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''dump-screen' \
|
|
724
|
+
"current_path did not verify the pane before capture"
|
|
725
|
+
assert_contains "$(cat "$dir/log")" "__FM_ZELLIJ_CWD_BEGIN__" "current_path did not send the cwd begin marker via paste"
|
|
726
|
+
assert_contains "$(cat "$dir/log")" "pwd;" "current_path did not send the pwd probe via paste"
|
|
727
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-keys'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''Enter' "current_path did not submit the cwd probe with Enter"
|
|
728
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''dump-screen'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''--full' "current_path did not capture the pane after probing"
|
|
729
|
+
pass "fm_backend_zellij_current_path: actively probes with marked begin/end lines and reconstructs wrapped cwd output"
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
test_current_path_ignores_tilde_prefixed_banner_lines() {
|
|
733
|
+
local dir fb out
|
|
734
|
+
dir="$TMP_ROOT/cwd-tilde"; mkdir -p "$dir/responses"
|
|
735
|
+
zellij_pane_response "$dir" 1 7 3
|
|
736
|
+
zellij_pane_response "$dir" 2 7 3
|
|
737
|
+
zellij_pane_response "$dir" 4 7 3
|
|
738
|
+
zellij_pane_response "$dir" 6 7 3
|
|
739
|
+
printf '%s\n' "🌳 Entered worktree at ~/.treehouse/scratch-e2e-project/1. Type 'exit' to return." \
|
|
740
|
+
'scratch-e2e-project HEAD' '__FM_ZELLIJ_CWD_BEGIN__' '/home/fixture/.treehouse/real-worktree' '__FM_ZELLIJ_CWD_END__' '❯' \
|
|
741
|
+
> "$dir/responses/7.out"
|
|
742
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
743
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
744
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
745
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_current_path firstmate:7' "$ROOT" )
|
|
746
|
+
[ "$out" = "/home/fixture/.treehouse/real-worktree" ] || fail "current_path should skip the ~-prefixed banner line and read the marked cwd output, got '$out'"
|
|
747
|
+
pass "fm_backend_zellij_current_path: never picks up a ~-prefixed banner line as the answer"
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
test_kill_resolves_tab_and_closes_by_id() {
|
|
751
|
+
local dir fb
|
|
752
|
+
dir="$TMP_ROOT/kill"; mkdir -p "$dir/responses"
|
|
753
|
+
zellij_pane_response "$dir" 1 7 3
|
|
754
|
+
printf '[{"id":7,"tab_id":3,"is_plugin":false}]\n' > "$dir/responses/2.out"
|
|
755
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
756
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
757
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
758
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_kill firstmate:7' "$ROOT"
|
|
759
|
+
expect_code 0 $? "kill should succeed (best-effort)"
|
|
760
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''close-tab-by-id' \
|
|
761
|
+
"kill did not verify the pane before close-tab-by-id"
|
|
762
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-tab-by-id'$'\x1f''3' \
|
|
763
|
+
"kill did not resolve the owning tab id and call close-tab-by-id (verified: close-pane alone leaves an empty ghost tab)"
|
|
764
|
+
pass "fm_backend_zellij_kill: resolves the owning tab id fresh and calls close-tab-by-id (never a bare close-pane)"
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
test_kill_falls_back_to_close_pane_when_tab_lookup_empty() {
|
|
768
|
+
local dir fb
|
|
769
|
+
dir="$TMP_ROOT/kill-fallback"; mkdir -p "$dir/responses"
|
|
770
|
+
printf '[]\n' > "$dir/responses/1.out"
|
|
771
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
772
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
773
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
774
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_kill firstmate:7' "$ROOT"
|
|
775
|
+
expect_code 0 $? "kill must stay best-effort even when the tab lookup comes up empty"
|
|
776
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''close-pane' \
|
|
777
|
+
"kill did not verify the pane before close-pane fallback"
|
|
778
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-pane'$'\x1f''--pane-id'$'\x1f''7' \
|
|
779
|
+
"kill did not fall back to a direct close-pane when no owning tab could be resolved"
|
|
780
|
+
pass "fm_backend_zellij_kill: falls back to close-pane when the owning tab cannot be resolved"
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
test_kill_closes_recorded_tab_when_pane_already_gone() {
|
|
784
|
+
local dir fb
|
|
785
|
+
dir="$TMP_ROOT/kill-recorded-tab"; mkdir -p "$dir/responses"
|
|
786
|
+
printf '[]\n' > "$dir/responses/1.out"
|
|
787
|
+
printf '[{"tab_id":3,"name":"fm-zghost"}]\n' > "$dir/responses/2.out"
|
|
788
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
789
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
790
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
791
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_kill firstmate:7 3 fm-zghost' "$ROOT"
|
|
792
|
+
expect_code 0 $? "kill must stay best-effort even when only the recorded tab id is usable"
|
|
793
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''list-tabs'$'\x1f''--json' \
|
|
794
|
+
"kill did not verify the recorded tab id by label before closing it"
|
|
795
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-tabs'$'\x1f''--json' $'\x1f''close-tab-by-id' \
|
|
796
|
+
"kill closed the recorded tab id before verifying its label"
|
|
797
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-tab-by-id'$'\x1f''3' \
|
|
798
|
+
"kill did not close the verified recorded tab id when the pane was already gone"
|
|
799
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-pane' \
|
|
800
|
+
"kill should close the verified recorded tab id instead of leaving an empty ghost tab"
|
|
801
|
+
pass "fm_backend_zellij_kill: closes the recorded tab id only after label verification"
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
test_kill_skips_recorded_tab_when_label_mismatches() {
|
|
805
|
+
local dir fb
|
|
806
|
+
dir="$TMP_ROOT/kill-recorded-tab-mismatch"; mkdir -p "$dir/responses"
|
|
807
|
+
printf '[]\n' > "$dir/responses/1.out"
|
|
808
|
+
printf '[{"tab_id":3,"name":"not-the-task"}]\n' > "$dir/responses/2.out"
|
|
809
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
810
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
811
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
812
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_kill firstmate:7 3 fm-zghost' "$ROOT"
|
|
813
|
+
expect_code 0 $? "kill must stay best-effort when the recorded tab id no longer belongs to the task"
|
|
814
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''list-tabs'$'\x1f''--json' \
|
|
815
|
+
"kill did not verify the recorded tab id by label"
|
|
816
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-tab-by-id' \
|
|
817
|
+
"kill should not close a recorded tab id whose name does not match the task"
|
|
818
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-pane' \
|
|
819
|
+
"kill should not fall back to closing a pane once an expected task label is available"
|
|
820
|
+
pass "fm_backend_zellij_kill: skips a stale recorded tab id whose label does not match"
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
test_kill_is_noop_when_session_absent() {
|
|
824
|
+
local dir fb
|
|
825
|
+
dir="$TMP_ROOT/kill-no-session"; mkdir -p "$dir/responses"
|
|
826
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
827
|
+
PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
828
|
+
FM_ZELLIJ_SESSION_LIST="" \
|
|
829
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_kill firstmate:7' "$ROOT"
|
|
830
|
+
expect_code 0 $? "kill must stay best-effort (never fail) even when the session is already gone"
|
|
831
|
+
pass "fm_backend_zellij_kill: never fails when the target session no longer exists"
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
test_teardown_passes_recorded_tab_id_to_zellij_kill() {
|
|
835
|
+
local dir state data config project fb out status
|
|
836
|
+
dir="$TMP_ROOT/teardown-zellij-ghost"; state="$dir/state"; data="$dir/data"; config="$dir/config"; project="$dir/project"
|
|
837
|
+
mkdir -p "$state" "$data/zghost" "$config" "$project" "$dir/responses"
|
|
838
|
+
printf 'report\n' > "$data/zghost/report.md"
|
|
839
|
+
fm_write_meta "$state/zghost.meta" \
|
|
840
|
+
"window=firstmate:7" \
|
|
841
|
+
"endpoint_task_id=zghost" \
|
|
842
|
+
"backend=zellij" \
|
|
843
|
+
"zellij_session=firstmate" \
|
|
844
|
+
"zellij_tab_id=3" \
|
|
845
|
+
"zellij_pane_id=7" \
|
|
846
|
+
"worktree=$dir/missing-worktree" \
|
|
847
|
+
"project=$project" \
|
|
848
|
+
"kind=scout" \
|
|
849
|
+
"decisions_reviewed=1" \
|
|
850
|
+
"decision_keys="
|
|
851
|
+
printf '[]\n' > "$dir/responses/1.out"
|
|
852
|
+
printf '[{"tab_id":3,"name":"fm-zghost"}]\n' > "$dir/responses/2.out"
|
|
853
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
854
|
+
out=$( PATH="$fb:$PATH" FM_STATE_OVERRIDE="$state" FM_DATA_OVERRIDE="$data" FM_CONFIG_OVERRIDE="$config" \
|
|
855
|
+
FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
856
|
+
"$ROOT/bin/fm-teardown.sh" zghost 2>&1 )
|
|
857
|
+
status=$?
|
|
858
|
+
expect_code 0 "$status" "fm-teardown should succeed for a zellij scout whose worktree is already gone: $out"
|
|
859
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''list-tabs'$'\x1f''--json' \
|
|
860
|
+
"fm-teardown did not verify the recorded zellij_tab_id against the task label"
|
|
861
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-tab-by-id'$'\x1f''3' \
|
|
862
|
+
"fm-teardown did not pass a verified recorded zellij_tab_id through to kill"
|
|
863
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''close-pane' \
|
|
864
|
+
"fm-teardown should close the recorded tab id instead of falling back to close-pane"
|
|
865
|
+
pass "fm-teardown.sh: passes recorded zellij_tab_id with the expected task label"
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
test_forced_secondmate_teardown_kills_zellij_children_with_child_home_tag() {
|
|
869
|
+
local dir state data config home project fb out status child_title
|
|
870
|
+
dir="$TMP_ROOT/teardown-zellij-secondmate-child"; state="$dir/state"; data="$dir/data"; config="$dir/config"; home="$dir/secondmate-home"; project="$dir/project"
|
|
871
|
+
mkdir -p "$state" "$data" "$config" "$home/state" "$home/data" "$home/config" "$home/projects" "$project" "$dir/responses"
|
|
872
|
+
printf 'smz\n' > "$home/.fm-secondmate-home"
|
|
873
|
+
fm_write_meta "$state/smz.meta" \
|
|
874
|
+
"window=firstmate:99" \
|
|
875
|
+
"endpoint_task_id=smz" \
|
|
876
|
+
"backend=zellij" \
|
|
877
|
+
"zellij_session=firstmate" \
|
|
878
|
+
"zellij_tab_id=99" \
|
|
879
|
+
"zellij_pane_id=99" \
|
|
880
|
+
"worktree=$home" \
|
|
881
|
+
"project=$home" \
|
|
882
|
+
"kind=secondmate" \
|
|
883
|
+
"mode=secondmate" \
|
|
884
|
+
"home=$home"
|
|
885
|
+
fm_write_meta "$home/state/childz.meta" \
|
|
886
|
+
"window=firstmate:7" \
|
|
887
|
+
"endpoint_task_id=childz" \
|
|
888
|
+
"backend=zellij" \
|
|
889
|
+
"zellij_session=firstmate" \
|
|
890
|
+
"zellij_tab_id=4" \
|
|
891
|
+
"zellij_pane_id=7" \
|
|
892
|
+
"worktree=$dir/missing-child-worktree" \
|
|
893
|
+
"project=$project" \
|
|
894
|
+
"kind=scout"
|
|
895
|
+
child_title=$(zellij_expected_scoped_title fm-childz "$home" "$home")
|
|
896
|
+
zellij_pane_response "$dir" 1 7 4
|
|
897
|
+
zellij_tab_response "$dir" 2 4 "$child_title"
|
|
898
|
+
printf '[]\n' > "$dir/responses/3.out"
|
|
899
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
900
|
+
out=$( PATH="$fb:$PATH" FM_STATE_OVERRIDE="$state" FM_DATA_OVERRIDE="$data" FM_CONFIG_OVERRIDE="$config" \
|
|
901
|
+
FM_ROOT_OVERRIDE="$ROOT" \
|
|
902
|
+
FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
903
|
+
"$ROOT/bin/fm-teardown.sh" smz --force 2>&1 )
|
|
904
|
+
status=$?
|
|
905
|
+
expect_code 0 "$status" "fm-teardown should force-retire a secondmate with a zellij child: $out"
|
|
906
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''close-tab-by-id'$'\x1f''4' \
|
|
907
|
+
"forced secondmate teardown did not close a child zellij tab scoped to the child home"
|
|
908
|
+
pass "fm-teardown.sh: force cleanup kills zellij children using the child home tag"
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
# --- send_text_submit: classifier-based verify-and-retry ---------------------
|
|
912
|
+
#
|
|
913
|
+
# The old content-diff strategy ("pane changed after Enter = submitted") was
|
|
914
|
+
# the fleet's only FALSE-POSITIVE delivery confirmation and is deleted; these
|
|
915
|
+
# tests pin its replacement: the shared composer classifier read through
|
|
916
|
+
# `dump-screen --ansi` (styled=1), where only a positively classified empty
|
|
917
|
+
# composer confirms delivery.
|
|
918
|
+
# Call numbering per attempt: list-panes + paste, then per Enter attempt
|
|
919
|
+
# list-panes + send-keys followed by list-panes + dump-screen --ansi.
|
|
920
|
+
|
|
921
|
+
test_send_text_submit_detects_landed_send() {
|
|
922
|
+
local dir fb out
|
|
923
|
+
dir="$TMP_ROOT/submit-ok"; mkdir -p "$dir/responses"
|
|
924
|
+
zellij_pane_response "$dir" 1 7 3
|
|
925
|
+
printf '%s' $'❯ ' > "$dir/responses/2.out"
|
|
926
|
+
zellij_pane_response "$dir" 3 7 3
|
|
927
|
+
zellij_pane_response "$dir" 5 7 3
|
|
928
|
+
printf '%s' $'❯ hello captain' > "$dir/responses/6.out"
|
|
929
|
+
zellij_pane_response "$dir" 7 7 3
|
|
930
|
+
zellij_pane_response "$dir" 9 7 3
|
|
931
|
+
printf '%s' $'hello captain\n❯ ' > "$dir/responses/10.out"
|
|
932
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
933
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
934
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
935
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "hello captain" 3 0.01 0.01' "$ROOT" )
|
|
936
|
+
[ "$out" = empty ] || fail "send_text_submit should report empty once the composer positively classifies empty, got '$out'"
|
|
937
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''paste' \
|
|
938
|
+
"send_text_submit did not verify the pane before paste"
|
|
939
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''dump-screen' \
|
|
940
|
+
"send_text_submit did not verify the pane before capture"
|
|
941
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''dump-screen'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''--ansi' \
|
|
942
|
+
"send_text_submit did not read the composer through the styled dump"
|
|
943
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''paste'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''--'$'\x1f''hello captain' "send_text_submit did not type the literal text first"
|
|
944
|
+
pass "fm_backend_zellij_send_text_submit: reports 'empty' once the composer classifies empty (submitted)"
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
test_send_text_submit_detects_swallowed_enter() {
|
|
948
|
+
local dir fb out
|
|
949
|
+
dir="$TMP_ROOT/submit-swallow"; mkdir -p "$dir/responses"
|
|
950
|
+
zellij_pane_response "$dir" 1 7 3
|
|
951
|
+
printf '%s' $'❯ ' > "$dir/responses/2.out"
|
|
952
|
+
zellij_pane_response "$dir" 3 7 3
|
|
953
|
+
zellij_pane_response "$dir" 5 7 3
|
|
954
|
+
printf '%s' $'❯ hello captain' > "$dir/responses/6.out"
|
|
955
|
+
zellij_pane_response "$dir" 7 7 3
|
|
956
|
+
zellij_pane_response "$dir" 9 7 3
|
|
957
|
+
printf '%s' $'❯ hello captain' > "$dir/responses/10.out"
|
|
958
|
+
zellij_pane_response "$dir" 11 7 3
|
|
959
|
+
zellij_pane_response "$dir" 13 7 3
|
|
960
|
+
printf '%s' $'❯ hello captain' > "$dir/responses/14.out"
|
|
961
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
962
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
963
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
964
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "hello captain" 2 0.01 0.01' "$ROOT" )
|
|
965
|
+
[ "$out" = pending ] || fail "send_text_submit should report pending once retries are exhausted with the text still in the composer, got '$out'"
|
|
966
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''send-keys' \
|
|
967
|
+
"send_text_submit did not verify the pane before send-keys"
|
|
968
|
+
pass "fm_backend_zellij_send_text_submit: reports 'pending' when the composer still holds the text after retried Enters (swallowed)"
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
test_send_text_submit_unrelated_change_is_not_delivery() {
|
|
972
|
+
# THE false-positive regression (audit fm-composer-consolidation-audit-s1,
|
|
973
|
+
# section 3.5, verified live): a pane whose content changes for reasons
|
|
974
|
+
# unrelated to submission - a clock, a spinner, streaming output - must NOT
|
|
975
|
+
# read as delivered while the typed text still sits in the composer. The
|
|
976
|
+
# deleted content-diff heuristic reported `empty` here and let fm-send close
|
|
977
|
+
# --resolve-key decision records for a message the crew never received.
|
|
978
|
+
local dir fb out
|
|
979
|
+
dir="$TMP_ROOT/submit-false-positive"; mkdir -p "$dir/responses"
|
|
980
|
+
zellij_pane_response "$dir" 1 7 3
|
|
981
|
+
printf '%s' $'clock 11:59:59\n❯ ' > "$dir/responses/2.out"
|
|
982
|
+
zellij_pane_response "$dir" 3 7 3
|
|
983
|
+
zellij_pane_response "$dir" 5 7 3
|
|
984
|
+
printf '%s' $'clock 12:00:00\n❯ hello captain' > "$dir/responses/6.out"
|
|
985
|
+
zellij_pane_response "$dir" 7 7 3
|
|
986
|
+
zellij_pane_response "$dir" 9 7 3
|
|
987
|
+
printf '%s' $'clock 12:00:01\n❯ hello captain' > "$dir/responses/10.out"
|
|
988
|
+
zellij_pane_response "$dir" 11 7 3
|
|
989
|
+
zellij_pane_response "$dir" 13 7 3
|
|
990
|
+
printf '%s' $'clock 12:00:02\n❯ hello captain' > "$dir/responses/14.out"
|
|
991
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
992
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
993
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
994
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "hello captain" 2 0.01 0.01' "$ROOT" )
|
|
995
|
+
[ "$out" != empty ] || fail "an unrelated pane change must never read as delivered (the content-diff false positive)"
|
|
996
|
+
[ "$out" = pending ] || fail "the still-typed composer should classify pending, got '$out'"
|
|
997
|
+
pass "fm_backend_zellij_send_text_submit: an unrelated pane change is not a delivery confirmation (false-positive regression)"
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
test_send_text_submit_rejects_unobserved_paste() {
|
|
1001
|
+
local dir fb out
|
|
1002
|
+
dir="$TMP_ROOT/submit-unobserved"; mkdir -p "$dir/responses"
|
|
1003
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1004
|
+
printf '%s' $'transcript line\n❯ ' > "$dir/responses/2.out"
|
|
1005
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1006
|
+
zellij_pane_response "$dir" 5 7 3
|
|
1007
|
+
printf '%s' $'transcript line\n❯ ' > "$dir/responses/6.out"
|
|
1008
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1009
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1010
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1011
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "hello captain" 2 0.01 0.01' "$ROOT" )
|
|
1012
|
+
[ "$out" = send-failed ] || fail "an unobserved paste should report send-failed, got '$out'"
|
|
1013
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
1014
|
+
"send_text_submit should not send Enter when the pasted text was not observed"
|
|
1015
|
+
pass "fm_backend_zellij_send_text_submit: refuses confirmation when paste exits successfully without typing"
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
test_send_text_submit_rejects_transcript_echo_with_unrelated_draft() {
|
|
1019
|
+
local dir fb out
|
|
1020
|
+
dir="$TMP_ROOT/submit-transcript-echo"; mkdir -p "$dir/responses"
|
|
1021
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1022
|
+
printf '%s' $'hello captain\n❯ unrelated draft' > "$dir/responses/2.out"
|
|
1023
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1024
|
+
zellij_pane_response "$dir" 5 7 3
|
|
1025
|
+
printf '%s' $'hello captain\n❯ unrelated draft' > "$dir/responses/6.out"
|
|
1026
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1027
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1028
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1029
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "hello captain" 2 0.01 0.01' "$ROOT" )
|
|
1030
|
+
[ "$out" = send-failed ] || fail "a transcript echo outside an unrelated draft should report send-failed, got '$out'"
|
|
1031
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
1032
|
+
"send_text_submit should not send Enter when only a transcript echo matches the intended text"
|
|
1033
|
+
pass "fm_backend_zellij_send_text_submit: transcript echoes outside the selected composer cannot prove typing"
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
test_send_text_submit_rejects_existing_intended_text_after_noop_paste() {
|
|
1037
|
+
local dir fb out
|
|
1038
|
+
dir="$TMP_ROOT/submit-existing-text-noop"; mkdir -p "$dir/responses"
|
|
1039
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1040
|
+
printf '%s' $'❯ hello captain' > "$dir/responses/2.out"
|
|
1041
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1042
|
+
zellij_pane_response "$dir" 5 7 3
|
|
1043
|
+
printf '%s' $'❯ hello captain' > "$dir/responses/6.out"
|
|
1044
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1045
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1046
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1047
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "hello captain" 2 0.01 0.01' "$ROOT" )
|
|
1048
|
+
[ "$out" = send-failed ] || fail "pre-existing intended text after a no-op paste should report send-failed, got '$out'"
|
|
1049
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
1050
|
+
"send_text_submit should not send Enter without an observed composer delta"
|
|
1051
|
+
pass "fm_backend_zellij_send_text_submit: pre-existing text cannot prove a no-op paste landed"
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
test_send_text_submit_rejects_furniture_match_after_noop_paste() {
|
|
1055
|
+
local dir fb out
|
|
1056
|
+
dir="$TMP_ROOT/submit-furniture-noop"; mkdir -p "$dir/responses"
|
|
1057
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1058
|
+
printf '%s' $'┃ unrelated draft\n┃ Build · GPT-5.5 Fast OpenAI · high' > "$dir/responses/2.out"
|
|
1059
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1060
|
+
zellij_pane_response "$dir" 5 7 3
|
|
1061
|
+
printf '%s' $'┃ unrelated draft\n┃ Build · GPT-5.5 Fast OpenAI · high' > "$dir/responses/6.out"
|
|
1062
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1063
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1064
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1065
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "high" 2 0.01 0.01' "$ROOT" )
|
|
1066
|
+
[ "$out" = send-failed ] || fail "footer furniture matching a short steer should report send-failed, got '$out'"
|
|
1067
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
1068
|
+
"send_text_submit should not send Enter when only furniture matches the steer"
|
|
1069
|
+
pass "fm_backend_zellij_send_text_submit: unrelated drafts and furniture cannot prove typing"
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
test_send_text_submit_accepts_wrapped_boxed_text() {
|
|
1073
|
+
local dir fb out
|
|
1074
|
+
dir="$TMP_ROOT/submit-wrapped-box"; mkdir -p "$dir/responses"
|
|
1075
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1076
|
+
printf '%s' $'╭────────────────────╮\n│ > Type a message...│\n╰────────────────────╯' > "$dir/responses/2.out"
|
|
1077
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1078
|
+
zellij_pane_response "$dir" 5 7 3
|
|
1079
|
+
printf '%s' $'╭────────────────────╮\n│ > hello │\n│ captain │\n╰────────────────────╯' > "$dir/responses/6.out"
|
|
1080
|
+
zellij_pane_response "$dir" 7 7 3
|
|
1081
|
+
zellij_pane_response "$dir" 9 7 3
|
|
1082
|
+
printf '%s' $'╭────────────────────╮\n│ ❯ │\n╰────────────────────╯' > "$dir/responses/10.out"
|
|
1083
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1084
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1085
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1086
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "hello captain" 2 0.01 0.01' "$ROOT" )
|
|
1087
|
+
[ "$out" = empty ] || fail "wrapped text replacing a shell-prompt placeholder should be observed and submitted, got '$out'"
|
|
1088
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
1089
|
+
"send_text_submit should send Enter after observing wrapped boxed text"
|
|
1090
|
+
pass "fm_backend_zellij_send_text_submit: observes wrapped text replacing a shell-prompt placeholder"
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
test_send_text_submit_accepts_wrapped_bare_text() {
|
|
1094
|
+
local dir fb out text
|
|
1095
|
+
dir="$TMP_ROOT/submit-wrapped-bare"; mkdir -p "$dir/responses"
|
|
1096
|
+
text='this deliberately long steer wraps across a bare continuation row'
|
|
1097
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1098
|
+
printf '%s' $'❯ ' > "$dir/responses/2.out"
|
|
1099
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1100
|
+
zellij_pane_response "$dir" 5 7 3
|
|
1101
|
+
printf '%s' $'❯ this deliberately long steer\nwraps across a bare continuation row' > "$dir/responses/6.out"
|
|
1102
|
+
zellij_pane_response "$dir" 7 7 3
|
|
1103
|
+
zellij_pane_response "$dir" 9 7 3
|
|
1104
|
+
printf '%s' $'this deliberately long steer wraps across a bare continuation row\n❯ ' > "$dir/responses/10.out"
|
|
1105
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1106
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1107
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1108
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "$1" 2 0.01 0.01' "$ROOT" "$text" )
|
|
1109
|
+
[ "$out" = empty ] || fail "wrapped text in a bare composer should be observed and submitted, got '$out'"
|
|
1110
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
1111
|
+
"send_text_submit should send Enter after observing wrapped bare text"
|
|
1112
|
+
pass "fm_backend_zellij_send_text_submit: observes wrapped text in a bare composer"
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
test_send_text_submit_preserves_agent_glyph_within_wrapped_content() {
|
|
1116
|
+
local dir fb out text
|
|
1117
|
+
dir="$TMP_ROOT/submit-wrapped-agent-glyph"; mkdir -p "$dir/responses"
|
|
1118
|
+
text='hello ❯ captain'
|
|
1119
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1120
|
+
printf '%s' $'❯ ' > "$dir/responses/2.out"
|
|
1121
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1122
|
+
zellij_pane_response "$dir" 5 7 3
|
|
1123
|
+
printf '%s' $'❯ hello ❯\ncaptain' > "$dir/responses/6.out"
|
|
1124
|
+
zellij_pane_response "$dir" 7 7 3
|
|
1125
|
+
zellij_pane_response "$dir" 9 7 3
|
|
1126
|
+
printf '%s' $'hello ❯ captain\n❯ ' > "$dir/responses/10.out"
|
|
1127
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1128
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1129
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1130
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "$1" 2 0.01 0.01' "$ROOT" "$text" )
|
|
1131
|
+
[ "$out" = empty ] || fail "an agent glyph within wrapped content should remain user content, got '$out'"
|
|
1132
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
1133
|
+
"send_text_submit should send Enter after preserving a mid-row agent glyph"
|
|
1134
|
+
pass "fm_backend_zellij_send_text_submit: preserves agent glyphs within wrapped content"
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
test_send_text_submit_rejects_stale_composer_above_live_shell() {
|
|
1138
|
+
local dir fb out
|
|
1139
|
+
dir="$TMP_ROOT/submit-live-shell"; mkdir -p "$dir/responses"
|
|
1140
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1141
|
+
printf '%s' $'❯\n$ ' > "$dir/responses/2.out"
|
|
1142
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1143
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1144
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1145
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "claude" 2 0.01 0.01' "$ROOT" )
|
|
1146
|
+
[ "$out" = send-failed ] || fail "a stale composer above a live shell should report send-failed, got '$out'"
|
|
1147
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''paste' \
|
|
1148
|
+
"send_text_submit must not paste into a live shell below a stale composer"
|
|
1149
|
+
pass "fm_backend_zellij_send_text_submit: refuses a live shell below a stale composer"
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
test_composer_state_reads_styled_dump() {
|
|
1153
|
+
local dir fb out
|
|
1154
|
+
dir="$TMP_ROOT/composer-styled"; mkdir -p "$dir/responses"
|
|
1155
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1156
|
+
# Real claude-in-zellij capture shape (audit section 3.5): ESC[m ❯ U+00A0.
|
|
1157
|
+
printf 'transcript line\n\033[m\342\235\257\302\240' > "$dir/responses/2.out"
|
|
1158
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1159
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1160
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1161
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_composer_state firstmate:7' "$ROOT" )
|
|
1162
|
+
[ "$out" = empty ] || fail "the real claude-in-zellij ANSI dump should classify empty, got '$out'"
|
|
1163
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''dump-screen'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''--ansi' \
|
|
1164
|
+
"composer_state did not request the styled dump"
|
|
1165
|
+
pass "fm_backend_zellij_composer_state: classifies the real claude-in-zellij --ansi dump as empty"
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
test_composer_state_dead_pane_is_unknown() {
|
|
1169
|
+
# The unconditional-exit-0 CLI quirk (file header): a dead target dumps
|
|
1170
|
+
# nothing. Both the styled and the plain fallback come back empty, so the
|
|
1171
|
+
# verdict must be unknown - never a confirmation.
|
|
1172
|
+
local dir fb out
|
|
1173
|
+
dir="$TMP_ROOT/composer-dead"; mkdir -p "$dir/responses"
|
|
1174
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1175
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1176
|
+
: > "$dir/responses/2.out"
|
|
1177
|
+
: > "$dir/responses/4.out"
|
|
1178
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1179
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1180
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1181
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_composer_state firstmate:7' "$ROOT" )
|
|
1182
|
+
[ "$out" = unknown ] || fail "a dead pane's empty dumps must classify unknown, got '$out'"
|
|
1183
|
+
pass "fm_backend_zellij_composer_state: a dead pane (empty dumps) reads unknown, never a confirmation"
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
test_send_text_submit_send_failed_when_session_absent() {
|
|
1187
|
+
local dir fb out
|
|
1188
|
+
dir="$TMP_ROOT/submit-no-session"; mkdir -p "$dir/responses"
|
|
1189
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1190
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1191
|
+
FM_ZELLIJ_SESSION_LIST="" \
|
|
1192
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "x" 2 0.01 0.01' "$ROOT" )
|
|
1193
|
+
[ "$out" = send-failed ] || fail "send_text_submit should report send-failed when the session does not exist, got '$out'"
|
|
1194
|
+
pass "fm_backend_zellij_send_text_submit: reports 'send-failed' when the target session is not active"
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
test_send_text_submit_send_failed_when_pane_absent() {
|
|
1198
|
+
local dir fb out
|
|
1199
|
+
dir="$TMP_ROOT/submit-no-pane"; mkdir -p "$dir/responses"
|
|
1200
|
+
printf '[]\n' > "$dir/responses/1.out"
|
|
1201
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1202
|
+
out=$( PATH="$fb:$PATH" FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" \
|
|
1203
|
+
FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1204
|
+
bash -c '. "$0/bin/backends/zellij.sh"; fm_backend_zellij_send_text_submit firstmate:7 "x" 2 0.01 0.01' "$ROOT" )
|
|
1205
|
+
[ "$out" = send-failed ] || fail "send_text_submit should report send-failed when the pane does not exist, got '$out'"
|
|
1206
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''paste' \
|
|
1207
|
+
"send_text_submit should not paste text after pane readiness fails"
|
|
1208
|
+
pass "fm_backend_zellij_send_text_submit: reports 'send-failed' when the target pane is absent"
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
# --- fm-*.sh script routing via explicit backend-tagged meta ------------------
|
|
1212
|
+
|
|
1213
|
+
test_scripts_route_explicit_target_through_meta_backend() {
|
|
1214
|
+
local dir state fb neutral out
|
|
1215
|
+
dir="$TMP_ROOT/script-explicit-target"; state="$dir/state"; mkdir -p "$state" "$dir/responses"
|
|
1216
|
+
neutral="$dir/neutral-root"; mkdir -p "$neutral"
|
|
1217
|
+
fm_write_meta "$state/zellij-stale.meta" "window=firstmate:7" "backend=zellij"
|
|
1218
|
+
touch "$state/.last-watcher-beat"
|
|
1219
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1220
|
+
printf 'captured zellij pane\n' > "$dir/responses/2.out"
|
|
1221
|
+
zellij_pane_response "$dir" 3 7 3
|
|
1222
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1223
|
+
cat > "$fb/tmux" <<'SH'
|
|
1224
|
+
#!/usr/bin/env bash
|
|
1225
|
+
set -u
|
|
1226
|
+
printf 'tmux should not be used for a metadata-matched zellij target\n' >&2
|
|
1227
|
+
exit 42
|
|
1228
|
+
SH
|
|
1229
|
+
chmod +x "$fb/tmux"
|
|
1230
|
+
|
|
1231
|
+
out=$( PATH="$fb:$PATH" FM_ROOT_OVERRIDE="$neutral" FM_STATE_OVERRIDE="$state" \
|
|
1232
|
+
FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1233
|
+
"$ROOT/bin/fm-peek.sh" firstmate:7 5 2>/dev/null )
|
|
1234
|
+
[ "$out" = "captured zellij pane" ] || fail "fm-peek did not capture through zellij for an explicit metadata-matched target, got '$out'"
|
|
1235
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''dump-screen' \
|
|
1236
|
+
"fm-peek did not verify the pane before capture"
|
|
1237
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''dump-screen'$'\x1f''--pane-id'$'\x1f''7' \
|
|
1238
|
+
"fm-peek did not route the explicit metadata-matched target through zellij capture"
|
|
1239
|
+
|
|
1240
|
+
: > "$dir/log"
|
|
1241
|
+
PATH="$fb:$PATH" FM_ROOT_OVERRIDE="$neutral" FM_HOME="$neutral" FM_STATE_OVERRIDE="$state" \
|
|
1242
|
+
FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1243
|
+
"$ROOT/bin/fm-send.sh" firstmate:7 --key Escape >/dev/null 2>&1
|
|
1244
|
+
expect_code 0 $? "fm-send --key should route an explicit metadata-matched target through zellij"
|
|
1245
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-panes'$'\x1f''--json' $'\x1f''send-keys' \
|
|
1246
|
+
"fm-send did not verify the pane before send-key"
|
|
1247
|
+
assert_contains "$(cat "$dir/log")" $'\x1f''send-keys'$'\x1f''--pane-id'$'\x1f''7'$'\x1f''Esc' \
|
|
1248
|
+
"fm-send did not route the explicit metadata-matched target through zellij send-key"
|
|
1249
|
+
|
|
1250
|
+
pass "fm-peek/fm-send: explicit metadata-matched targets use the recorded zellij backend"
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
test_scripts_verify_label_for_fm_targets() {
|
|
1254
|
+
local dir state fb neutral out
|
|
1255
|
+
dir="$TMP_ROOT/script-fm-target-label"; state="$dir/state"; mkdir -p "$state" "$dir/responses"
|
|
1256
|
+
neutral="$dir/neutral-root"; mkdir -p "$neutral"
|
|
1257
|
+
fm_write_meta "$state/zlabel.meta" "window=firstmate:7" "backend=zellij"
|
|
1258
|
+
touch "$state/.last-watcher-beat"
|
|
1259
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1260
|
+
zellij_tab_response "$dir" 2 3 fm-zlabel
|
|
1261
|
+
printf 'captured through fm-id\n' > "$dir/responses/3.out"
|
|
1262
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1263
|
+
|
|
1264
|
+
out=$( PATH="$fb:$PATH" FM_ROOT_OVERRIDE="$neutral" FM_STATE_OVERRIDE="$state" \
|
|
1265
|
+
FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1266
|
+
"$ROOT/bin/fm-peek.sh" fm-zlabel 5 2>/dev/null )
|
|
1267
|
+
[ "$out" = "captured through fm-id" ] || fail "fm-peek did not capture through zellij for an fm-id target with a matching tab label, got '$out'"
|
|
1268
|
+
zellij_assert_call_order "$dir/log" $'\x1f''list-tabs'$'\x1f''--json' $'\x1f''dump-screen' \
|
|
1269
|
+
"fm-peek did not verify the fm-id tab label before capture"
|
|
1270
|
+
|
|
1271
|
+
pass "fm-peek: fm-id zellij targets verify the owning tab label before capture"
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
test_scripts_reject_fm_target_label_mismatch() {
|
|
1275
|
+
local dir state fb neutral status
|
|
1276
|
+
dir="$TMP_ROOT/script-fm-target-label-mismatch"; state="$dir/state"; mkdir -p "$state" "$dir/responses"
|
|
1277
|
+
neutral="$dir/neutral-root"; mkdir -p "$neutral"
|
|
1278
|
+
fm_write_meta "$state/zreuse.meta" "window=firstmate:7" "backend=zellij"
|
|
1279
|
+
touch "$state/.last-watcher-beat"
|
|
1280
|
+
zellij_pane_response "$dir" 1 7 3
|
|
1281
|
+
zellij_tab_response "$dir" 2 3 not-the-task
|
|
1282
|
+
fb=$(make_zellij_fakebin "$dir")
|
|
1283
|
+
|
|
1284
|
+
PATH="$fb:$PATH" FM_ROOT_OVERRIDE="$neutral" FM_HOME="$neutral" FM_STATE_OVERRIDE="$state" \
|
|
1285
|
+
FM_ZELLIJ_LOG="$dir/log" FM_ZELLIJ_RESPONSES="$dir/responses" FM_ZELLIJ_SESSION_LIST="firstmate" \
|
|
1286
|
+
"$ROOT/bin/fm-send.sh" fm-zreuse --key Escape >/dev/null 2>&1
|
|
1287
|
+
status=$?
|
|
1288
|
+
[ "$status" -ne 0 ] || fail "fm-send --key should reject an fm-id zellij target whose pane belongs to a differently named tab"
|
|
1289
|
+
assert_not_contains "$(cat "$dir/log")" $'\x1f''send-keys' \
|
|
1290
|
+
"fm-send should not send a key after fm-id label verification fails"
|
|
1291
|
+
pass "fm-send: fm-id zellij targets reject pane ids whose tab label no longer matches"
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
# shellcheck source=/dev/null
|
|
1295
|
+
. "$ROOT/bin/fm-backend.sh"
|
|
1296
|
+
|
|
1297
|
+
test_version_check_accepts_current_version
|
|
1298
|
+
test_version_check_accepts_newer_version
|
|
1299
|
+
test_version_check_refuses_old_version
|
|
1300
|
+
test_version_check_refuses_missing_zellij
|
|
1301
|
+
test_session_defaults_to_firstmate
|
|
1302
|
+
test_session_honors_override
|
|
1303
|
+
test_parse_target
|
|
1304
|
+
test_normalize_key
|
|
1305
|
+
test_scoped_title_uses_primary_home_label
|
|
1306
|
+
test_scoped_title_uses_secondmate_home_label
|
|
1307
|
+
test_scoped_title_changes_with_root_path
|
|
1308
|
+
test_expected_label_accepts_unambiguous_untagged_legacy_tab
|
|
1309
|
+
test_expected_label_refuses_ambiguous_untagged_tab
|
|
1310
|
+
test_list_live_scopes_to_own_home_tag
|
|
1311
|
+
test_resolve_bare_selector_prefers_scoped_title
|
|
1312
|
+
test_resolve_bare_selector_refuses_ambiguous_untagged
|
|
1313
|
+
test_resolve_bare_selector_prefers_later_session_scoped_title_over_legacy
|
|
1314
|
+
test_resolve_bare_selector_refuses_cross_session_ambiguous_untagged
|
|
1315
|
+
test_session_exists_true_when_listed
|
|
1316
|
+
test_session_exists_false_when_absent
|
|
1317
|
+
test_server_ensure_skips_attach_when_already_exists
|
|
1318
|
+
test_dispatch_routes_zellij_backend
|
|
1319
|
+
test_dispatch_busy_state_unknown_for_zellij
|
|
1320
|
+
test_create_task_refuses_duplicate_label
|
|
1321
|
+
test_create_task_creates_and_parses_ids
|
|
1322
|
+
test_create_task_restores_previously_active_tab
|
|
1323
|
+
test_create_task_no_restore_when_new_tab_was_already_active
|
|
1324
|
+
test_capture_small_reads_use_viewport_and_trim
|
|
1325
|
+
test_capture_large_reads_use_full_scrollback_and_trim
|
|
1326
|
+
test_capture_fails_when_pane_absent
|
|
1327
|
+
test_capture_fails_when_session_absent
|
|
1328
|
+
test_send_key_normalizes_and_targets_pane
|
|
1329
|
+
test_send_literal_uses_paste_separator_for_option_shaped_text
|
|
1330
|
+
test_send_text_line_clears_partial_input_when_enter_fails
|
|
1331
|
+
test_send_text_line_reports_unsafe_input_when_cleanup_fails
|
|
1332
|
+
test_expected_label_allows_matching_task_tab
|
|
1333
|
+
test_expected_label_rejects_reused_pane_id
|
|
1334
|
+
test_current_path_probes_with_marker_and_ignores_prompt_paths
|
|
1335
|
+
test_current_path_ignores_tilde_prefixed_banner_lines
|
|
1336
|
+
test_kill_resolves_tab_and_closes_by_id
|
|
1337
|
+
test_kill_falls_back_to_close_pane_when_tab_lookup_empty
|
|
1338
|
+
test_kill_closes_recorded_tab_when_pane_already_gone
|
|
1339
|
+
test_kill_skips_recorded_tab_when_label_mismatches
|
|
1340
|
+
test_kill_is_noop_when_session_absent
|
|
1341
|
+
test_teardown_passes_recorded_tab_id_to_zellij_kill
|
|
1342
|
+
test_forced_secondmate_teardown_kills_zellij_children_with_child_home_tag
|
|
1343
|
+
test_send_text_submit_detects_landed_send
|
|
1344
|
+
test_send_text_submit_detects_swallowed_enter
|
|
1345
|
+
test_send_text_submit_unrelated_change_is_not_delivery
|
|
1346
|
+
test_send_text_submit_rejects_unobserved_paste
|
|
1347
|
+
test_send_text_submit_rejects_transcript_echo_with_unrelated_draft
|
|
1348
|
+
test_send_text_submit_rejects_existing_intended_text_after_noop_paste
|
|
1349
|
+
test_send_text_submit_rejects_furniture_match_after_noop_paste
|
|
1350
|
+
test_send_text_submit_accepts_wrapped_boxed_text
|
|
1351
|
+
test_send_text_submit_accepts_wrapped_bare_text
|
|
1352
|
+
test_send_text_submit_preserves_agent_glyph_within_wrapped_content
|
|
1353
|
+
test_send_text_submit_rejects_stale_composer_above_live_shell
|
|
1354
|
+
test_composer_state_reads_styled_dump
|
|
1355
|
+
test_composer_state_dead_pane_is_unknown
|
|
1356
|
+
test_send_text_submit_send_failed_when_session_absent
|
|
1357
|
+
test_send_text_submit_send_failed_when_pane_absent
|
|
1358
|
+
test_scripts_route_explicit_target_through_meta_backend
|
|
1359
|
+
test_scripts_verify_label_for_fm_targets
|
|
1360
|
+
test_scripts_reject_fm_target_label_mismatch
|