@north-light/crouter 0.3.170 → 0.3.172
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/dist/api/client.d.ts +37 -11
- package/dist/api/client.js +84 -15
- package/dist/api/dto/human.d.ts +53 -39
- package/dist/api/dto/human.js +2 -3
- package/dist/api/dto/inbox.d.ts +0 -1
- package/dist/api/dto/nodes.d.ts +12 -0
- package/dist/api/dto/review-comments.d.ts +137 -0
- package/dist/api/dto/review-comments.js +5 -0
- package/dist/api/dto/reviews.d.ts +109 -0
- package/dist/api/dto/reviews.js +5 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/routes.d.ts +17 -4
- package/dist/api/routes.js +21 -5
- package/dist/build-root.d.ts +1 -16
- package/dist/build-root.js +1 -25
- package/dist/builtin-memory/00-runtime-base.md +7 -5
- package/dist/builtin-memory/04-base-worker.md +1 -1
- package/dist/builtin-memory/04-orchestration-kernel.md +7 -7
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/review/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +20 -0
- package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -7
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +3 -3
- package/dist/builtin-memory/05-kinds/spec/requirements.md +3 -3
- package/dist/builtin-memory/design.md +2 -2
- package/dist/builtin-memory/insights/capture.md +50 -0
- package/dist/builtin-memory/insights/init.md +45 -0
- package/dist/builtin-memory/insights/listen.md +9 -0
- package/dist/builtin-memory/internal/agent-shaping.md +2 -2
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/plugins.md +12 -11
- package/dist/builtin-memory/internal/storage-tiers.md +2 -2
- package/dist/builtin-memory/{planning.md → plan/roadmap.md} +3 -3
- package/dist/builtin-memory/plan.md +19 -0
- package/dist/builtin-memory/spec/guide.md +38 -0
- package/dist/builtin-memory/spec/requirements.md +31 -0
- package/dist/builtin-memory/spec/roadmap.md +35 -0
- package/dist/builtin-memory/spec.md +10 -86
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +3 -4
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +49 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +56 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +920 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +221 -156
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +4 -5
- package/dist/cli.js +2 -6
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +7 -35
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +32 -16
- package/dist/clients/attach/__tests__/context-message.test.js +142 -6
- package/dist/clients/attach/__tests__/crtr-output.test.js +29 -45
- package/dist/clients/attach/__tests__/diagram.test.js +1 -1
- package/dist/clients/attach/__tests__/file-review-focus.test.js +48 -0
- package/dist/clients/attach/__tests__/group-activity.test.js +266 -0
- package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +32 -0
- package/dist/clients/attach/__tests__/session-identity-refresh.test.js +32 -0
- package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -2
- package/dist/clients/attach/chrome/header.d.ts +10 -0
- package/dist/clients/attach/chrome/header.js +30 -0
- package/dist/clients/attach/chrome/recap.d.ts +7 -0
- package/dist/clients/attach/chrome/recap.js +19 -0
- package/dist/clients/attach/chrome/status-line.d.ts +3 -0
- package/dist/clients/attach/chrome/status-line.js +5 -0
- package/dist/clients/attach/chrome/widgets.js +11 -21
- package/dist/clients/attach/command.js +11 -7
- package/dist/clients/attach/config.d.ts +4 -0
- package/dist/clients/attach/config.js +3 -0
- package/dist/clients/attach/input/controller.d.ts +19 -0
- package/dist/clients/attach/input/controller.js +62 -28
- package/dist/clients/attach/input/titled-editor.d.ts +0 -4
- package/dist/clients/attach/input/titled-editor.js +2 -10
- package/dist/clients/attach/overlays/file-review.d.ts +9 -5
- package/dist/clients/attach/overlays/file-review.js +9 -70
- package/dist/clients/attach/overlays/help.d.ts +33 -0
- package/dist/clients/attach/overlays/help.js +204 -0
- package/dist/clients/attach/render/activity-slot.d.ts +4 -0
- package/dist/clients/attach/render/activity-slot.js +13 -0
- package/dist/clients/attach/render/chat-view.d.ts +117 -12
- package/dist/clients/attach/render/chat-view.js +604 -135
- package/dist/clients/attach/render/context-message.d.ts +2 -1
- package/dist/clients/attach/render/context-message.js +17 -13
- package/dist/clients/attach/render/crtr-output.js +1 -3
- package/dist/clients/attach/render/diagram.js +2 -2
- package/dist/clients/attach/render/edit-diff.d.ts +4 -1
- package/dist/clients/attach/render/edit-diff.js +7 -11
- package/dist/clients/attach/render/group-activity.d.ts +32 -0
- package/dist/clients/attach/render/group-activity.js +206 -0
- package/dist/clients/attach/render/group-recap.d.ts +10 -0
- package/dist/clients/attach/render/group-recap.js +93 -0
- package/dist/clients/attach/render/html-markdown.d.ts +17 -0
- package/dist/clients/attach/render/html-markdown.js +228 -0
- package/dist/clients/attach/render/measured-container.d.ts +96 -0
- package/dist/clients/attach/render/measured-container.js +322 -0
- package/dist/clients/attach/render/scroll-animation.d.ts +8 -0
- package/dist/clients/attach/render/scroll-animation.js +80 -0
- package/dist/clients/attach/render/tool-calls.js +6 -6
- package/dist/clients/attach/render/transcript-copy.d.ts +9 -0
- package/dist/clients/attach/render/transcript-copy.js +81 -0
- package/dist/clients/attach/render/viewport.d.ts +29 -0
- package/dist/clients/attach/render/viewport.js +159 -0
- package/dist/clients/attach/session/bindings.d.ts +10 -7
- package/dist/clients/attach/session/bindings.js +23 -8
- package/dist/clients/attach/session/connection.js +4 -5
- package/dist/clients/attach/session/context.d.ts +6 -4
- package/dist/clients/attach/session/frame.d.ts +34 -0
- package/dist/clients/attach/session/frame.js +176 -0
- package/dist/clients/attach/session/frames.d.ts +4 -1
- package/dist/clients/attach/session/frames.js +14 -1
- package/dist/clients/attach/session/identity.js +4 -3
- package/dist/clients/attach/session/input-wiring.d.ts +14 -4
- package/dist/clients/attach/session/input-wiring.js +15 -13
- package/dist/clients/attach/session/invariant.d.ts +9 -0
- package/dist/clients/attach/session/invariant.js +15 -0
- package/dist/clients/attach/session/keys.d.ts +10 -2
- package/dist/clients/attach/session/keys.js +13 -5
- package/dist/clients/attach/session/layout.d.ts +10 -8
- package/dist/clients/attach/session/layout.js +33 -34
- package/dist/clients/attach/session/mouse.d.ts +18 -0
- package/dist/clients/attach/session/mouse.js +64 -0
- package/dist/clients/attach/session/pane-tag.d.ts +3 -0
- package/dist/clients/attach/session/pane-tag.js +51 -8
- package/dist/clients/attach/session/reconnect.d.ts +3 -0
- package/dist/clients/attach/session/reconnect.js +42 -5
- package/dist/clients/attach/session/surface-frame.d.ts +20 -0
- package/dist/clients/attach/session/surface-frame.js +73 -0
- package/dist/clients/attach/session/surface-host.d.ts +64 -0
- package/dist/clients/attach/session/surface-host.js +255 -0
- package/dist/clients/attach/session/whip.d.ts +17 -0
- package/dist/clients/attach/session/whip.js +334 -0
- package/dist/clients/attach/slash/dispatch.d.ts +13 -1
- package/dist/clients/attach/slash/dispatch.js +51 -29
- package/dist/clients/attach/viewer.js +907 -819
- package/dist/clients/conversation/conversation.d.ts +65 -0
- package/dist/clients/conversation/conversation.js +318 -0
- package/dist/clients/conversation/frames.d.ts +30 -0
- package/dist/clients/conversation/frames.js +52 -0
- package/dist/clients/conversation/region.d.ts +51 -0
- package/dist/clients/conversation/region.js +265 -0
- package/dist/clients/conversation/submit.d.ts +38 -0
- package/dist/clients/conversation/submit.js +51 -0
- package/dist/clients/inbox/__tests__/inbox-controller.test.js +247 -0
- package/dist/clients/inbox/__tests__/mount-panel.test.js +776 -0
- package/dist/clients/inbox/controller.d.ts +79 -0
- package/dist/clients/inbox/controller.js +448 -0
- package/dist/clients/inbox/deck-adapter.d.ts +29 -0
- package/dist/clients/inbox/deck-adapter.js +62 -0
- package/dist/clients/inbox/layout.d.ts +8 -0
- package/dist/clients/inbox/layout.js +9 -0
- package/dist/clients/inbox/resolve.d.ts +10 -0
- package/dist/clients/inbox/resolve.js +34 -0
- package/dist/clients/inbox/review/__tests__/editor-roundtrip.test.js +29 -0
- package/dist/clients/inbox/review/__tests__/remap.test.js +196 -0
- package/dist/clients/inbox/review/__tests__/review-core.test.js +341 -0
- package/dist/clients/inbox/review/anchors.d.ts +41 -0
- package/dist/clients/inbox/review/anchors.js +89 -0
- package/dist/clients/inbox/review/columns.d.ts +22 -0
- package/dist/clients/inbox/review/columns.js +40 -0
- package/dist/clients/inbox/review/comments-client.d.ts +66 -0
- package/dist/clients/inbox/review/comments-client.js +98 -0
- package/dist/clients/inbox/review/composition.d.ts +60 -0
- package/dist/clients/inbox/review/composition.js +278 -0
- package/dist/clients/inbox/review/document-region.d.ts +120 -0
- package/dist/clients/inbox/review/document-region.js +902 -0
- package/dist/clients/inbox/review/frame.d.ts +45 -0
- package/dist/clients/inbox/review/frame.js +215 -0
- package/dist/clients/inbox/review/keys.d.ts +67 -0
- package/dist/clients/inbox/review/keys.js +130 -0
- package/dist/clients/inbox/review/launch.d.ts +10 -0
- package/dist/clients/inbox/review/launch.js +129 -0
- package/dist/clients/inbox/review/live-source.d.ts +24 -0
- package/dist/clients/inbox/review/live-source.js +160 -0
- package/dist/clients/inbox/review/nvim.d.ts +13 -0
- package/dist/clients/inbox/review/nvim.js +42 -0
- package/dist/clients/inbox/review/remap.d.ts +51 -0
- package/dist/clients/inbox/review/remap.js +157 -0
- package/dist/clients/inbox/review/review-client.d.ts +30 -0
- package/dist/clients/inbox/review/review-client.js +107 -0
- package/dist/clients/inbox/review/roundtrip.d.ts +5 -0
- package/dist/clients/inbox/review/roundtrip.js +36 -0
- package/dist/clients/inbox/review/state.d.ts +143 -0
- package/dist/clients/inbox/review/state.js +282 -0
- package/dist/clients/inbox/review-adapter.d.ts +29 -0
- package/dist/clients/inbox/review-adapter.js +92 -0
- package/dist/clients/inbox/review-visibility.d.ts +5 -0
- package/dist/clients/inbox/review-visibility.js +19 -0
- package/dist/clients/inbox/surface.d.ts +19 -0
- package/dist/clients/inbox/surface.js +128 -0
- package/dist/clients/inbox/tui/ansi.d.ts +66 -0
- package/dist/clients/inbox/tui/ansi.js +317 -0
- package/dist/clients/inbox/tui/clipboard.d.ts +2 -0
- package/dist/clients/inbox/tui/clipboard.js +26 -0
- package/dist/clients/inbox/tui/input.d.ts +7 -0
- package/dist/clients/inbox/tui/input.js +653 -0
- package/dist/clients/inbox/tui/keys.d.ts +28 -0
- package/dist/clients/inbox/tui/keys.js +111 -0
- package/dist/clients/inbox/tui/panel.d.ts +2 -0
- package/dist/clients/inbox/tui/panel.js +217 -0
- package/dist/clients/inbox/tui/render.d.ts +37 -0
- package/dist/clients/inbox/tui/render.js +476 -0
- package/dist/clients/inbox/tui/types.d.ts +57 -0
- package/dist/clients/inbox/tui/types.js +1 -0
- package/dist/clients/inbox/tui.d.ts +6 -0
- package/dist/clients/inbox/tui.js +74 -0
- package/dist/clients/surfaces/host.d.ts +66 -0
- package/dist/clients/surfaces/host.js +1 -0
- package/dist/clients/surfaces/region.d.ts +16 -0
- package/dist/clients/surfaces/region.js +1 -0
- package/dist/clients/web/__tests__/push-engine.test.js +14 -158
- package/dist/clients/web/dev-server.d.ts +3 -3
- package/dist/clients/web/dev-server.js +9 -23
- package/dist/clients/web/events.js +4 -6
- package/dist/clients/web/push-engine.d.ts +2 -66
- package/dist/clients/web/push-engine.js +30 -161
- package/dist/clients/web/push-registry.d.ts +0 -4
- package/dist/clients/web/push-registry.js +2 -7
- package/dist/clients/web/server.js +3 -35
- package/dist/clients/web/web-client/shared/protocol.d.ts +0 -108
- package/dist/clients/web/web-cmd.js +2 -2
- package/dist/commands/__tests__/human.test.js +27 -15
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/canvas-issue.js +1 -1
- package/dist/commands/canvas-use.js +1 -1
- package/dist/commands/human/doc.d.ts +2 -0
- package/dist/commands/human/doc.js +98 -0
- package/dist/commands/human/inbox.d.ts +2 -0
- package/dist/commands/human/inbox.js +101 -0
- package/dist/commands/human/prompts.d.ts +2 -3
- package/dist/commands/human/prompts.js +27 -180
- package/dist/commands/human/queue.d.ts +1 -5
- package/dist/commands/human/queue.js +54 -216
- package/dist/commands/human/review.d.ts +2 -0
- package/dist/commands/human/review.js +298 -0
- package/dist/commands/human/shared.d.ts +2 -69
- package/dist/commands/human/shared.js +1 -181
- package/dist/commands/human.js +15 -20
- package/dist/commands/memory/__tests__/lint.test.d.ts +1 -0
- package/dist/commands/memory/__tests__/lint.test.js +15 -0
- package/dist/commands/memory/delete.js +2 -2
- package/dist/commands/memory/lint.d.ts +1 -0
- package/dist/commands/memory/lint.js +21 -3
- package/dist/commands/memory/read.js +4 -4
- package/dist/commands/node.js +8 -7
- package/dist/commands/pkg/plugin-manage.js +143 -68
- package/dist/commands/surface-inspect.js +2 -2
- package/dist/commands/surface.js +6 -12
- package/dist/commands/sys/__tests__/setup-core.test.js +9 -5
- package/dist/commands/sys/__tests__/setup-front-door.test.js +7 -42
- package/dist/commands/sys/config.js +29 -3
- package/dist/commands/sys/feedback.js +1 -1
- package/dist/commands/sys/setup-core.d.ts +12 -11
- package/dist/commands/sys/setup-core.js +55 -46
- package/dist/commands/sys/setup-wizard.d.ts +4 -1
- package/dist/commands/sys/setup-wizard.js +50 -9
- package/dist/commands/sys/sync-deps.js +1 -1
- package/dist/commands/sys.js +4 -6
- package/dist/core/__tests__/broker-sdk-wiring.test.js +6 -1
- package/dist/core/__tests__/broker-snapshot-history.test.js +9 -1
- package/dist/core/__tests__/dead-node-policy-table.test.js +3 -4
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +8 -5
- package/dist/core/__tests__/fixtures/fake-engine.js +28 -2
- package/dist/core/__tests__/full/broker-provider-retry.test.js +87 -2
- package/dist/core/__tests__/full/broker-whip-interrupt.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-whip-interrupt.test.js +39 -0
- package/dist/core/__tests__/full/detach-focus.test.js +1 -1
- package/dist/core/__tests__/human-deliver-e2e.test.js +64 -64
- package/dist/core/__tests__/human-deliver.test.js +71 -345
- package/dist/core/__tests__/inline-memory-refs.test.js +2 -12
- package/dist/core/__tests__/memory-resolver-precedence.test.js +12 -15
- package/dist/core/__tests__/migration.test.js +10 -0
- package/dist/core/__tests__/model-pin-durability.test.js +14 -0
- package/dist/core/__tests__/phase4-review-migration.test.d.ts +1 -0
- package/dist/core/__tests__/phase4-review-migration.test.js +100 -0
- package/dist/core/__tests__/phase4-review-store.test.d.ts +1 -0
- package/dist/core/__tests__/phase4-review-store.test.js +143 -0
- package/dist/core/__tests__/remote-canvas-target.test.js +2 -2
- package/dist/core/__tests__/revive.test.js +22 -53
- package/dist/core/__tests__/session-cycles.test.js +48 -18
- package/dist/core/__tests__/spawn-no-kickoff.test.js +8 -12
- package/dist/core/__tests__/stop-guard.test.js +16 -1
- package/dist/core/__tests__/tmux-surface.test.js +21 -25
- package/dist/core/asset-root.d.ts +7 -0
- package/dist/core/asset-root.js +18 -0
- package/dist/core/broker-client/client.d.ts +0 -7
- package/dist/core/broker-client/client.js +4 -10
- package/dist/core/broker-client/transport-relay.d.ts +3 -4
- package/dist/core/broker-client/transport-relay.js +3 -4
- package/dist/core/canvas/__tests__/attention.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
- package/dist/core/canvas/__tests__/remote-transport.test.d.ts +1 -0
- package/dist/core/{view/__tests__/transport-remote.test.js → canvas/__tests__/remote-transport.test.js} +2 -2
- package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
- package/dist/core/canvas/attention.js +4 -4
- package/dist/core/canvas/canvas.d.ts +6 -11
- package/dist/core/canvas/canvas.js +11 -33
- package/dist/core/canvas/db.js +207 -38
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +8 -1
- package/dist/core/canvas/index.d.ts +0 -1
- package/dist/core/canvas/index.js +0 -1
- package/dist/core/canvas/nav-model.d.ts +1 -1
- package/dist/core/canvas/nav-model.js +1 -1
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +16 -0
- package/dist/core/canvas/remote-canvas-source.js +2 -2
- package/dist/core/canvas/remote-transport.d.ts +7 -0
- package/dist/core/{view/transport-remote.js → canvas/remote-transport.js} +3 -4
- package/dist/core/canvas/source.js +1 -1
- package/dist/core/canvas/status-glyph.js +4 -12
- package/dist/core/canvas/types.d.ts +10 -35
- package/dist/core/clipboard-text.d.ts +8 -0
- package/dist/core/clipboard-text.js +18 -0
- package/dist/core/command-manifests/registry.d.ts +1 -3
- package/dist/core/command-plugins/bundle.d.ts +29 -0
- package/dist/core/command-plugins/bundle.js +198 -0
- package/dist/core/command-plugins/discovery.d.ts +0 -8
- package/dist/core/command-plugins/discovery.js +2 -39
- package/dist/core/command-plugins/transport/http-fetch.d.ts +4 -30
- package/dist/core/command-plugins/transport/http-fetch.js +17 -95
- package/dist/core/command.d.ts +1 -13
- package/dist/core/command.js +1 -16
- package/dist/core/config.d.ts +4 -0
- package/dist/core/config.js +28 -3
- package/dist/core/document-lines.d.ts +4 -0
- package/dist/core/document-lines.js +6 -0
- package/dist/core/feed/feed.d.ts +2 -0
- package/dist/core/feed/feed.js +11 -7
- package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-cancel-worker.js +7 -0
- package/dist/core/human/__tests__/inbox-claim-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-claim-worker.js +17 -0
- package/dist/core/human/__tests__/inbox-core.test.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-core.test.js +144 -0
- package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +1 -0
- package/dist/core/human/__tests__/inbox-stale-claim-worker.js +8 -0
- package/dist/core/human/__tests__/visible-paths.test.d.ts +1 -0
- package/dist/{clients/attach → core/human}/__tests__/visible-paths.test.js +9 -0
- package/dist/core/human/claim.d.ts +23 -0
- package/dist/core/human/claim.js +67 -0
- package/dist/core/human/convention.d.ts +30 -0
- package/dist/core/human/convention.js +202 -0
- package/dist/core/human/deck-factories.d.ts +8 -0
- package/dist/core/human/deck-factories.js +15 -0
- package/dist/core/human/deck-schema.d.ts +70 -0
- package/dist/core/human/deck-schema.js +92 -0
- package/dist/core/human/root.d.ts +7 -0
- package/dist/core/human/root.js +37 -0
- package/dist/core/human/scan.d.ts +3 -0
- package/dist/core/human/scan.js +76 -0
- package/dist/core/human/summary.d.ts +8 -0
- package/dist/core/human/summary.js +44 -0
- package/dist/core/human/tickets.d.ts +49 -0
- package/dist/core/human/tickets.js +190 -0
- package/dist/core/human/types.d.ts +136 -0
- package/dist/core/human/types.js +5 -0
- package/dist/{clients/attach → core/human}/visible-paths.d.ts +1 -1
- package/dist/{clients/attach → core/human}/visible-paths.js +3 -2
- package/dist/core/inspector/chrome.d.ts +6 -0
- package/dist/core/inspector/chrome.js +14 -0
- package/dist/core/inspector/contract.d.ts +105 -0
- package/dist/core/inspector/contract.js +1 -0
- package/dist/core/inspector/core.d.ts +4 -5
- package/dist/core/inspector/core.js +3 -5
- package/dist/core/inspector/host.d.ts +46 -0
- package/dist/core/{tui → inspector}/host.js +42 -208
- package/dist/core/inspector/text.d.ts +2 -2
- package/dist/core/inspector/transport.d.ts +6 -0
- package/dist/core/inspector/transport.js +36 -0
- package/dist/core/inspector/tui.d.ts +2 -2
- package/dist/core/inspector/tui.js +15 -15
- package/dist/core/keybindings/__tests__/inbox-affordance.test.js +11 -11
- package/dist/core/keybindings/__tests__/resolve.test.js +10 -19
- package/dist/core/keybindings/attach-control.d.ts +9 -3
- package/dist/core/keybindings/attach-control.js +3 -1
- package/dist/core/keybindings/catalog.d.ts +6 -5
- package/dist/core/keybindings/catalog.js +61 -133
- package/dist/core/keybindings/inbox.d.ts +5 -6
- package/dist/core/keybindings/inbox.js +9 -10
- package/dist/core/keybindings/index.d.ts +3 -3
- package/dist/core/keybindings/index.js +3 -3
- package/dist/core/keybindings/persistence.d.ts +5 -1
- package/dist/core/keybindings/persistence.js +24 -6
- package/dist/core/keybindings/resolve.d.ts +6 -0
- package/dist/core/keybindings/resolve.js +10 -3
- package/dist/core/memory/inline-ref-grammar.js +2 -3
- package/dist/core/memory-resolver.d.ts +27 -29
- package/dist/core/memory-resolver.js +45 -39
- package/dist/core/model-routes.d.ts +33 -0
- package/dist/core/model-routes.js +42 -3
- package/dist/core/preview-registry.js +17 -55
- package/dist/core/profiles/select.js +3 -3
- package/dist/core/{view/remote-canvas-target.d.ts → remote-canvas-target.d.ts} +2 -2
- package/dist/core/{view/remote-canvas-target.js → remote-canvas-target.js} +4 -4
- package/dist/core/review/__tests__/capture-origin.test.d.ts +1 -0
- package/dist/core/review/__tests__/capture-origin.test.js +139 -0
- package/dist/core/review/__tests__/comments.test.d.ts +1 -0
- package/dist/core/review/__tests__/comments.test.js +98 -0
- package/dist/core/review/__tests__/project.test.d.ts +1 -0
- package/dist/core/review/__tests__/project.test.js +71 -0
- package/dist/core/review/__tests__/remap.test.d.ts +1 -0
- package/dist/core/review/__tests__/remap.test.js +159 -0
- package/dist/core/review/__tests__/stage-identity.test.d.ts +1 -0
- package/dist/core/review/__tests__/stage-identity.test.js +140 -0
- package/dist/core/review/birth.d.ts +7 -0
- package/dist/core/review/birth.js +23 -0
- package/dist/core/review/comments.d.ts +69 -0
- package/dist/core/review/comments.js +295 -0
- package/dist/core/review/companion.d.ts +35 -0
- package/dist/core/review/companion.js +205 -0
- package/dist/core/review/document.d.ts +27 -0
- package/dist/core/review/document.js +138 -0
- package/dist/core/review/project.d.ts +13 -0
- package/dist/core/review/project.js +58 -0
- package/dist/core/review/realize.d.ts +19 -0
- package/dist/core/review/realize.js +217 -0
- package/dist/core/review/remap.d.ts +24 -0
- package/dist/core/review/remap.js +156 -0
- package/dist/core/review/signal.d.ts +5 -0
- package/dist/core/review/signal.js +31 -0
- package/dist/core/review/stage.d.ts +26 -0
- package/dist/core/review/stage.js +184 -0
- package/dist/core/review/store.d.ts +46 -0
- package/dist/core/review/store.js +163 -0
- package/dist/core/review/ticket-filter.d.ts +17 -0
- package/dist/core/review/ticket-filter.js +32 -0
- package/dist/core/review/types.d.ts +130 -0
- package/dist/core/review/types.js +23 -0
- package/dist/core/runtime/__tests__/session-visibility.test.d.ts +1 -0
- package/dist/core/runtime/__tests__/session-visibility.test.js +119 -0
- package/dist/core/runtime/bearings.d.ts +5 -17
- package/dist/core/runtime/bearings.js +88 -71
- package/dist/core/runtime/broker-protocol.d.ts +31 -8
- package/dist/core/runtime/broker.d.ts +15 -1
- package/dist/core/runtime/broker.js +573 -141
- package/dist/core/runtime/canvas-extensions.d.ts +4 -5
- package/dist/core/runtime/canvas-extensions.js +5 -5
- package/dist/core/runtime/front-door.js +4 -4
- package/dist/core/runtime/headless-pi.d.ts +24 -0
- package/dist/core/runtime/headless-pi.js +152 -0
- package/dist/core/runtime/kickoff.d.ts +1 -5
- package/dist/core/runtime/kickoff.js +1 -5
- package/dist/core/runtime/model-registry.d.ts +3 -0
- package/dist/core/runtime/model-registry.js +13 -0
- package/dist/core/runtime/naming-persist.d.ts +4 -0
- package/dist/core/runtime/naming-persist.js +27 -1
- package/dist/core/runtime/naming.d.ts +17 -12
- package/dist/core/runtime/naming.js +91 -140
- package/dist/core/runtime/node-read.js +19 -10
- package/dist/core/runtime/nodes.d.ts +9 -14
- package/dist/core/runtime/nodes.js +3 -53
- package/dist/core/runtime/package-health.d.ts +6 -21
- package/dist/core/runtime/package-health.js +12 -123
- package/dist/core/runtime/placement-tmux.d.ts +2 -1
- package/dist/core/runtime/placement-tmux.js +6 -4
- package/dist/core/runtime/placement.d.ts +1 -0
- package/dist/core/runtime/placement.js +4 -8
- package/dist/core/runtime/promote.js +3 -0
- package/dist/core/runtime/recap.d.ts +2 -7
- package/dist/core/runtime/recap.js +35 -69
- package/dist/core/runtime/revive.js +34 -77
- package/dist/core/runtime/session-cycles.d.ts +4 -0
- package/dist/core/runtime/session-cycles.js +14 -1
- package/dist/core/runtime/session-visibility.d.ts +37 -0
- package/dist/core/runtime/session-visibility.js +83 -0
- package/dist/core/runtime/spawn.js +10 -22
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +18 -13
- package/dist/core/runtime/surface-bg.js +1 -1
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +1 -1
- package/dist/core/runtime/tmux.d.ts +35 -7
- package/dist/core/runtime/tmux.js +439 -62
- package/dist/core/runtime/tool-group-summary.d.ts +19 -0
- package/dist/core/runtime/tool-group-summary.js +88 -0
- package/dist/core/scope.d.ts +2 -7
- package/dist/core/scope.js +9 -35
- package/dist/core/substrate/schema.d.ts +2 -2
- package/dist/core/termrender/code-doc.d.ts +12 -0
- package/dist/core/termrender/code-doc.js +92 -0
- package/dist/core/termrender/display.d.ts +12 -0
- package/dist/core/termrender/display.js +19 -0
- package/dist/core/termrender/termrender.d.ts +73 -0
- package/dist/core/termrender/termrender.js +795 -0
- package/dist/core/termrender/version.d.ts +1 -0
- package/dist/core/termrender/version.js +1 -0
- package/dist/core/tui/terminal.js +1 -2
- package/dist/core/user-settings.d.ts +71 -3
- package/dist/core/user-settings.js +83 -1
- package/dist/daemon/api/__tests__/inbox.test.js +44 -133
- package/dist/daemon/api/handlers/human.d.ts +0 -10
- package/dist/daemon/api/handlers/human.js +104 -702
- package/dist/daemon/api/handlers/inbox.js +113 -142
- package/dist/daemon/api/handlers/review-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/review-comments.js +500 -0
- package/dist/daemon/api/handlers/reviews.d.ts +2 -0
- package/dist/daemon/api/handlers/reviews.js +287 -0
- package/dist/daemon/api/map.d.ts +22 -7
- package/dist/daemon/api/map.js +159 -11
- package/dist/daemon/api/server.js +4 -0
- package/dist/daemon/cron-run.js +6 -9
- package/dist/daemon/crtrd.js +17 -33
- package/dist/daemon/fleet.d.ts +1 -8
- package/dist/daemon/fleet.js +4 -30
- package/dist/daemon/human/finish.d.ts +14 -0
- package/dist/daemon/human/finish.js +115 -0
- package/dist/daemon/human/sweep.d.ts +6 -0
- package/dist/daemon/human/sweep.js +49 -0
- package/dist/daemon/messaging/node-message.d.ts +18 -0
- package/dist/daemon/messaging/node-message.js +70 -0
- package/dist/daemon/review/comment-notify.d.ts +26 -0
- package/dist/daemon/review/comment-notify.js +107 -0
- package/dist/daemon/review/deliver.d.ts +7 -0
- package/dist/daemon/review/deliver.js +56 -0
- package/dist/daemon/review/finish.d.ts +22 -0
- package/dist/daemon/review/finish.js +254 -0
- package/dist/daemon/review/sweep.d.ts +11 -0
- package/dist/daemon/review/sweep.js +120 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +42 -64
- package/dist/pi-extensions/__tests__/canvas-recap.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-recap.test.js +159 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +16 -38
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +63 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +8 -11
- package/dist/pi-extensions/canvas-context-intro.js +50 -104
- package/dist/pi-extensions/canvas-recap.d.ts +15 -2
- package/dist/pi-extensions/canvas-recap.js +192 -69
- package/dist/pi-extensions/canvas-review-boundary.d.ts +59 -0
- package/dist/pi-extensions/canvas-review-boundary.js +81 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +3 -3
- package/dist/pi-extensions/canvas-stophook.js +78 -128
- package/dist/pi-extensions/canvas-structured-output.js +52 -8
- package/dist/shared/generated-context.d.ts +29 -4
- package/dist/shared/generated-context.js +122 -7
- package/dist/shared/tool-groups.d.ts +23 -0
- package/dist/shared/tool-groups.js +60 -0
- package/dist/shared/working-activity.d.ts +8 -0
- package/dist/shared/working-activity.js +26 -0
- package/dist/types.d.ts +36 -2
- package/dist/types.js +51 -0
- package/dist/web-client/assets/index-CCPaLeW7.css +2 -0
- package/dist/web-client/assets/index-kyOWsnZK.js +86 -0
- package/dist/web-client/index.html +2 -2
- package/dist/web-client/sw.js +1 -1
- package/docs/compat/hearth-crtr-v4.md +2 -2
- package/docs/compat/hearth-crtr-v5.md +4 -2
- package/docs/public-api.md +3 -3
- package/package.json +21 -14
- package/runtime.lock.json +4661 -1453
- package/scripts/postinstall.mjs +15 -2
- package/dist/builtin-pi-packages/pi-mode-switch/README.md +0 -36
- package/dist/builtin-pi-packages/pi-mode-switch/bin/mode +0 -36
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +0 -449
- package/dist/builtin-pi-packages/pi-mode-switch/package.json +0 -14
- package/dist/builtin-pi-packages/pi-mode-switch/tsconfig.json +0 -9
- package/dist/builtin-views/_lib/states.mjs +0 -161
- package/dist/builtin-views/canvas/core.mjs +0 -657
- package/dist/builtin-views/canvas/text.mjs +0 -58
- package/dist/builtin-views/canvas/tui.mjs +0 -168
- package/dist/builtin-views/canvas/web.jsx +0 -121
- package/dist/builtin-views/chat/core.mjs +0 -684
- package/dist/builtin-views/chat/text.mjs +0 -101
- package/dist/builtin-views/chat/tui.mjs +0 -360
- package/dist/builtin-views/chat/web.jsx +0 -362
- package/dist/builtin-views/git-pr/core.mjs +0 -673
- package/dist/builtin-views/git-pr/text.mjs +0 -84
- package/dist/builtin-views/git-pr/tui.mjs +0 -301
- package/dist/builtin-views/git-pr/web.jsx +0 -216
- package/dist/builtin-views/inbox/_lib/render.mjs +0 -175
- package/dist/builtin-views/inbox/core.mjs +0 -1273
- package/dist/builtin-views/inbox/text.mjs +0 -73
- package/dist/builtin-views/inbox/tui.mjs +0 -314
- package/dist/builtin-views/inbox/web.jsx +0 -188
- package/dist/builtin-views/linkedin/core.mjs +0 -906
- package/dist/builtin-views/linkedin/text.mjs +0 -69
- package/dist/builtin-views/linkedin/tui.mjs +0 -413
- package/dist/builtin-views/linkedin/web.jsx +0 -206
- package/dist/builtin-views/prompt-review/core.mjs +0 -735
- package/dist/builtin-views/prompt-review/text.mjs +0 -15
- package/dist/builtin-views/prompt-review/tui.mjs +0 -196
- package/dist/builtin-views/prompt-review/web.jsx +0 -484
- package/dist/builtin-views/workspace-sidebar/__tests__/core.test.js +0 -50
- package/dist/builtin-views/workspace-sidebar/__tests__/core.test.ts +0 -53
- package/dist/builtin-views/workspace-sidebar/core.mjs +0 -670
- package/dist/builtin-views/workspace-sidebar/text.mjs +0 -53
- package/dist/builtin-views/workspace-sidebar/tui.mjs +0 -141
- package/dist/builtin-views/workspace-sidebar/web.jsx +0 -109
- package/dist/clients/attach/session/mode.d.ts +0 -25
- package/dist/clients/attach/session/mode.js +0 -97
- package/dist/commands/sys/promptstudio.d.ts +0 -2
- package/dist/commands/sys/promptstudio.js +0 -65
- package/dist/commands/view-cycle.d.ts +0 -2
- package/dist/commands/view-cycle.js +0 -130
- package/dist/commands/view-list.d.ts +0 -2
- package/dist/commands/view-list.js +0 -66
- package/dist/commands/view-new.d.ts +0 -2
- package/dist/commands/view-new.js +0 -74
- package/dist/commands/view-pick.d.ts +0 -6
- package/dist/commands/view-pick.js +0 -129
- package/dist/commands/view-run.d.ts +0 -2
- package/dist/commands/view-run.js +0 -214
- package/dist/commands/view.d.ts +0 -2
- package/dist/commands/view.js +0 -31
- package/dist/commands/workspace.d.ts +0 -2
- package/dist/commands/workspace.js +0 -165
- package/dist/core/__tests__/chat-view-reconnect.test.js +0 -105
- package/dist/core/__tests__/full/consult-startup-failure.test.js +0 -124
- package/dist/core/canvas/__tests__/human-work-outbox.test.js +0 -123
- package/dist/core/canvas/human-work-outbox.d.ts +0 -73
- package/dist/core/canvas/human-work-outbox.js +0 -261
- package/dist/core/command-plugins/store.d.ts +0 -16
- package/dist/core/command-plugins/store.js +0 -64
- package/dist/core/keybindings/__tests__/bespoke-consumers.test.js +0 -40
- package/dist/core/runtime/fault-recovery-nudge.d.ts +0 -3
- package/dist/core/runtime/fault-recovery-nudge.js +0 -3
- package/dist/core/tui/__tests__/host-keybindings.test.js +0 -113
- package/dist/core/tui/host.d.ts +0 -66
- package/dist/core/view/__tests__/transport-cache.test.js +0 -62
- package/dist/core/view/bridge.d.ts +0 -10
- package/dist/core/view/bridge.js +0 -31
- package/dist/core/view/chrome.d.ts +0 -9
- package/dist/core/view/chrome.js +0 -22
- package/dist/core/view/contract.d.ts +0 -205
- package/dist/core/view/contract.js +0 -23
- package/dist/core/view/loader.d.ts +0 -31
- package/dist/core/view/loader.js +0 -188
- package/dist/core/view/stream-local.d.ts +0 -3
- package/dist/core/view/stream-local.js +0 -231
- package/dist/core/view/transport-cache.d.ts +0 -8
- package/dist/core/view/transport-cache.js +0 -38
- package/dist/core/view/transport-local.d.ts +0 -7
- package/dist/core/view/transport-local.js +0 -83
- package/dist/core/view/transport-remote.d.ts +0 -2
- package/dist/core/view/transport.d.ts +0 -10
- package/dist/core/view/transport.js +0 -15
- package/dist/pi-extensions/naming-tool.d.ts +0 -24
- package/dist/pi-extensions/naming-tool.js +0 -67
- package/dist/prompts/view.d.ts +0 -7
- package/dist/prompts/view.js +0 -172
- package/dist/web/ViewChrome.d.ts +0 -7
- package/dist/web/ViewChrome.js +0 -28
- package/dist/web/ViewPane.d.ts +0 -39
- package/dist/web/ViewPane.js +0 -48
- package/dist/web/index.d.ts +0 -8
- package/dist/web/index.js +0 -22
- package/dist/web/runtime.d.ts +0 -39
- package/dist/web/runtime.js +0 -232
- package/dist/web/states.d.ts +0 -24
- package/dist/web/states.js +0 -24
- package/dist/web/transport-http.d.ts +0 -5
- package/dist/web/transport-http.js +0 -34
- package/dist/web/transport-stream.d.ts +0 -3
- package/dist/web/transport-stream.js +0 -204
- package/dist/web-client/assets/index-U_NZ66VE.js +0 -79
- package/dist/web-client/assets/index-aaeu4adv.css +0 -2
- /package/dist/{builtin-views/workspace-sidebar/__tests__/core.test.d.ts → clients/attach/__tests__/file-review-focus.test.d.ts} +0 -0
- /package/dist/clients/attach/__tests__/{visible-paths.test.d.ts → group-activity.test.d.ts} +0 -0
- /package/dist/{core/__tests__/chat-view-reconnect.test.d.ts → clients/attach/__tests__/input-controller-extension-command.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/consult-startup-failure.test.d.ts → clients/attach/__tests__/session-identity-refresh.test.d.ts} +0 -0
- /package/dist/{core/canvas/__tests__/human-work-outbox.test.d.ts → clients/inbox/__tests__/inbox-controller.test.d.ts} +0 -0
- /package/dist/{core/keybindings/__tests__/bespoke-consumers.test.d.ts → clients/inbox/__tests__/mount-panel.test.d.ts} +0 -0
- /package/dist/{core/tui/__tests__/host-keybindings.test.d.ts → clients/inbox/review/__tests__/editor-roundtrip.test.d.ts} +0 -0
- /package/dist/{core/view/__tests__/transport-cache.test.d.ts → clients/inbox/review/__tests__/remap.test.d.ts} +0 -0
- /package/dist/{core/view/__tests__/transport-remote.test.d.ts → clients/inbox/review/__tests__/review-core.test.d.ts} +0 -0
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
// UI unless you switch to it. Bring one forefront with select-window (within a
|
|
8
8
|
// root) or switch-client + select-window (across roots). done/dead nodes close
|
|
9
9
|
// their window; reviving opens a fresh one.
|
|
10
|
-
import { spawnSync } from 'node:child_process';
|
|
11
|
-
import { readFileSync, rmSync } from 'node:fs';
|
|
12
|
-
import { homedir } from 'node:os';
|
|
10
|
+
import { execFile, spawnSync } from 'node:child_process';
|
|
11
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { homedir, tmpdir } from 'node:os';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import { ATTACH_CONTROL_BINDINGS, BINDING_CATALOG, encodeAttachControlInput, formatGesture,
|
|
14
|
+
import { ATTACH_CONTROL_BINDINGS, BINDING_CATALOG, BINDING_IDS, encodeAttachControlInput, formatGesture, isAttachPaneBinding, resolveUserKeybindingSettings, } from '../keybindings/index.js';
|
|
15
|
+
import { normalizeTmuxPassthrough } from '../config.js';
|
|
16
|
+
import { withExclusiveDirectoryLock } from '../human/convention.js';
|
|
15
17
|
import { bundledPiCommandForShell } from './pi-cli.js';
|
|
16
18
|
import { surfaceTmuxStyleArgs } from './surface-bg.js';
|
|
17
|
-
import { inboxToggleTmuxCommand } from '@crouton-kit/humanloop';
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// Shell quoting + tmux invocation
|
|
20
21
|
// ---------------------------------------------------------------------------
|
|
@@ -30,6 +31,9 @@ function tmux(args) {
|
|
|
30
31
|
stderr: (r.stderr ?? '').trim(),
|
|
31
32
|
};
|
|
32
33
|
}
|
|
34
|
+
function tmuxOnSocket(socket, args) {
|
|
35
|
+
return socket === undefined || socket === '' ? tmux(args) : tmux(['-S', socket, ...args]);
|
|
36
|
+
}
|
|
33
37
|
export function inTmux() {
|
|
34
38
|
return process.env['TMUX'] !== undefined && process.env['TMUX'] !== '';
|
|
35
39
|
}
|
|
@@ -311,8 +315,7 @@ export function listLivePanes() {
|
|
|
311
315
|
return new Set(r.stdout.split('\n').filter((p) => p !== ''));
|
|
312
316
|
}
|
|
313
317
|
/** The working directory of a pane (`display-message -p -t <pane>
|
|
314
|
-
* '#{pane_current_path}'`).
|
|
315
|
-
* view-cycle respawn so project-scoped views still resolve. Null if tmux fails. */
|
|
318
|
+
* '#{pane_current_path}'`). Null if tmux fails. */
|
|
316
319
|
export function paneCurrentPath(pane) {
|
|
317
320
|
const r = tmux(['display-message', '-p', '-t', pane, '#{pane_current_path}']);
|
|
318
321
|
return r.ok && r.stdout !== '' ? r.stdout : null;
|
|
@@ -329,8 +332,6 @@ export function displayMessage(message, pane, ms = 4000) {
|
|
|
329
332
|
return tmux(args).ok;
|
|
330
333
|
}
|
|
331
334
|
/** Set a PANE-scoped tmux option (`tmux set-option -p -t <pane> <name> <value>`).
|
|
332
|
-
* Used to tag a pane with the view id it currently hosts (`@crtr_view`) so the
|
|
333
|
-
* view-nav cycle can read it back and switch to the next/prev view in place.
|
|
334
335
|
* Best-effort; never throws. */
|
|
335
336
|
export function setPaneOption(pane, name, value) {
|
|
336
337
|
return tmux(['set-option', '-p', '-t', pane, name, value]).ok;
|
|
@@ -342,6 +343,121 @@ export function getPaneOption(pane, name) {
|
|
|
342
343
|
const r = tmux(['show-options', '-p', '-t', pane, '-q', '-v', name]);
|
|
343
344
|
return r.ok ? r.stdout : undefined;
|
|
344
345
|
}
|
|
346
|
+
/** Pane option recording when an attach viewer was last started or focused.
|
|
347
|
+
* `liveViewerPanes` uses it to select the most recently used viewer. */
|
|
348
|
+
export const VIEWER_STAMP_OPTION = '@crtr_viewer_at';
|
|
349
|
+
let lastViewerStampMs = 0;
|
|
350
|
+
let viewerStampQueue = Promise.resolve();
|
|
351
|
+
/** Stamp a viewer pane without blocking the input path that observed focus.
|
|
352
|
+
* Stamps are monotonically increasing and their tmux writes are serialized, so
|
|
353
|
+
* two rapid focus events cannot leave an older pane with the newer stamp. */
|
|
354
|
+
export function stampViewerPaneAsync(pane) {
|
|
355
|
+
if (pane === '')
|
|
356
|
+
return;
|
|
357
|
+
const stampMs = Math.max(Date.now(), lastViewerStampMs + 1);
|
|
358
|
+
lastViewerStampMs = stampMs;
|
|
359
|
+
viewerStampQueue = viewerStampQueue.then(() => new Promise((resolve) => {
|
|
360
|
+
try {
|
|
361
|
+
execFile('tmux', ['set-option', '-p', '-t', pane, VIEWER_STAMP_OPTION, String(stampMs)], () => resolve());
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
resolve();
|
|
365
|
+
}
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
368
|
+
function escapeRegex(text) {
|
|
369
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
370
|
+
}
|
|
371
|
+
function looksLikeViewerCommand(command, nodeId) {
|
|
372
|
+
const unquoted = command.replace(/^"|"$/g, '');
|
|
373
|
+
return new RegExp(`(?:^|\\s)surface\\s+attach\\s+to\\s+${escapeRegex(nodeId)}(?:\\s|$)`).test(unquoted);
|
|
374
|
+
}
|
|
375
|
+
function readProcessTree() {
|
|
376
|
+
const result = spawnSync('ps', ['-ax', '-o', 'pid=,ppid=,command='], { encoding: 'utf8' });
|
|
377
|
+
if (result.status !== 0 || typeof result.stdout !== 'string')
|
|
378
|
+
return null;
|
|
379
|
+
const childrenOf = new Map();
|
|
380
|
+
const commandOf = new Map();
|
|
381
|
+
for (const line of result.stdout.split('\n')) {
|
|
382
|
+
const match = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
|
|
383
|
+
if (match === null)
|
|
384
|
+
continue;
|
|
385
|
+
const pid = Number(match[1]);
|
|
386
|
+
const parentPid = Number(match[2]);
|
|
387
|
+
commandOf.set(pid, match[3]);
|
|
388
|
+
const children = childrenOf.get(parentPid) ?? [];
|
|
389
|
+
children.push(pid);
|
|
390
|
+
childrenOf.set(parentPid, children);
|
|
391
|
+
}
|
|
392
|
+
return { childrenOf, commandOf };
|
|
393
|
+
}
|
|
394
|
+
function processTreeHasViewer(tree, rootPid, nodeId) {
|
|
395
|
+
const pending = [rootPid];
|
|
396
|
+
while (pending.length > 0) {
|
|
397
|
+
const pid = pending.pop();
|
|
398
|
+
if (looksLikeViewerCommand(tree.commandOf.get(pid) ?? '', nodeId))
|
|
399
|
+
return true;
|
|
400
|
+
const children = tree.childrenOf.get(pid);
|
|
401
|
+
if (children !== undefined)
|
|
402
|
+
pending.push(...children);
|
|
403
|
+
}
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
/** List viewer panes tagged by a running local attach viewer. Pane tags can
|
|
407
|
+
* survive a killed process, so every tag must match a viewer for the same node.
|
|
408
|
+
* Shell-hosted viewers are recognized through their live child process. Unstamped
|
|
409
|
+
* valid viewers remain eligible at stamp zero while an older viewer rolls forward. */
|
|
410
|
+
export function liveViewerPanes(socket) {
|
|
411
|
+
const listed = tmuxOnSocket(socket, [
|
|
412
|
+
'list-panes', '-a', '-F',
|
|
413
|
+
'#{pane_id}\t#{session_name}\t#{@crtr_node}\t#{@crtr_viewer_at}',
|
|
414
|
+
]);
|
|
415
|
+
if (!listed.ok || listed.stdout === '')
|
|
416
|
+
return [];
|
|
417
|
+
const viewers = [];
|
|
418
|
+
let processTree;
|
|
419
|
+
for (const line of listed.stdout.split('\n')) {
|
|
420
|
+
const [pane, session, nodeId, stamp] = line.split('\t');
|
|
421
|
+
if (pane === undefined || pane === '' || session === undefined || session === '' || nodeId === undefined || nodeId === '')
|
|
422
|
+
continue;
|
|
423
|
+
const command = tmuxOnSocket(socket, [
|
|
424
|
+
'display-message', '-p', '-t', pane,
|
|
425
|
+
'#{pane_current_command}\t#{pane_start_command}\t#{pane_pid}',
|
|
426
|
+
]);
|
|
427
|
+
if (!command.ok)
|
|
428
|
+
continue;
|
|
429
|
+
const [currentCommand = '', startCommand = '', pid] = command.stdout.split('\t');
|
|
430
|
+
const directViewer = looksLikeViewerCommand(currentCommand, nodeId) || looksLikeViewerCommand(startCommand, nodeId);
|
|
431
|
+
if (!directViewer) {
|
|
432
|
+
processTree ??= readProcessTree();
|
|
433
|
+
const rootPid = Number.parseInt(pid, 10);
|
|
434
|
+
if (processTree === null || !Number.isInteger(rootPid) || rootPid <= 0 || !processTreeHasViewer(processTree, rootPid, nodeId))
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const parsedStamp = Number.parseInt(stamp ?? '', 10);
|
|
438
|
+
viewers.push({
|
|
439
|
+
pane,
|
|
440
|
+
session,
|
|
441
|
+
nodeId,
|
|
442
|
+
stampMs: Number.isSafeInteger(parsedStamp) && parsedStamp >= 0 ? parsedStamp : 0,
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
return viewers;
|
|
446
|
+
}
|
|
447
|
+
/** Deliver one private attach-control sequence literally into a viewer pane.
|
|
448
|
+
* `-l` bypasses tmux key-table lookup, so delivery cannot re-trigger a root
|
|
449
|
+
* binding that initiated it. */
|
|
450
|
+
export function sendAttachControl(socket, pane, actionId) {
|
|
451
|
+
return tmuxOnSocket(socket, ['send-keys', '-l', '-t', pane, encodeAttachControlInput(actionId)]).ok;
|
|
452
|
+
}
|
|
453
|
+
/** Show a tmux status-line message on a specific client when one is known. */
|
|
454
|
+
export function displayClientMessage(socket, client, text) {
|
|
455
|
+
const args = ['display-message'];
|
|
456
|
+
if (client !== undefined && client !== '')
|
|
457
|
+
args.push('-c', client);
|
|
458
|
+
args.push(text);
|
|
459
|
+
return tmuxOnSocket(socket, args).ok;
|
|
460
|
+
}
|
|
345
461
|
/** The `respawn-pane -k` argv for `opts`. `-k` kills the pane's current process
|
|
346
462
|
* (e.g. a yielding pi) and re-execs `command` in the SAME pane, preserving its
|
|
347
463
|
* `%id` (§1.5 F3: a frozen focus pane resumes in place, no new window). The
|
|
@@ -472,12 +588,12 @@ const TMUX_BINDING_MANIFEST = '@crtr_keybindings_manifest';
|
|
|
472
588
|
const ROOT_TABLE = 'root';
|
|
473
589
|
const LEAF_MENU_ACTIONS = {
|
|
474
590
|
'crtr.tmux.menu.promote': { description: 'promote to orchestrator', action: { kind: 'keys', keys: '/promote' } },
|
|
475
|
-
'crtr.tmux.menu.resume': { description: '
|
|
591
|
+
'crtr.tmux.menu.resume': { description: 'find node', action: { kind: 'keys', keys: '/resume-node' } },
|
|
476
592
|
'crtr.tmux.menu.fork': { description: 'fork into idle root', action: { kind: 'direct', run: 'node fork --pane {pane}' } },
|
|
477
|
-
'crtr.tmux.menu.demote': { description: 'demote
|
|
478
|
-
'crtr.tmux.menu.detach': { description: '
|
|
593
|
+
'crtr.tmux.menu.demote': { description: 'demote (finish when done)', action: { kind: 'command', run: 'node lifecycle demote --pane {pane}' } },
|
|
594
|
+
'crtr.tmux.menu.detach': { description: 'demote + close viewer', action: { kind: 'command', run: 'node lifecycle demote --pane {pane} --detach' } },
|
|
479
595
|
'crtr.tmux.menu.close-subtree': { description: 'close agent + subtree', action: { kind: 'command', run: 'node lifecycle close --pane {pane}' } },
|
|
480
|
-
'crtr.tmux.menu.background-bash': { description: 'background bash
|
|
596
|
+
'crtr.tmux.menu.background-bash': { description: 'background running bash', action: { kind: 'direct', run: 'node bash background --pane {pane}' } },
|
|
481
597
|
// M-S-r, not M-R: with extended-keys csi-u tmux encodes M-R as alt+uppercase-R
|
|
482
598
|
// WITHOUT the shift modifier bit, which pi-tui's `alt+shift+r` matcher rejects;
|
|
483
599
|
// M-S-r carries shift+alt and lands as the same chord the keyboard produces.
|
|
@@ -486,10 +602,11 @@ const LEAF_MENU_ACTIONS = {
|
|
|
486
602
|
'crtr.tmux.menu.fold-tools': { description: 'fold finished tool calls', action: { kind: 'keys', keys: '/fold-tools' } },
|
|
487
603
|
'crtr.tmux.menu.context': { description: 'context + reports', action: { kind: 'keys', keys: '/context' } },
|
|
488
604
|
'crtr.tmux.menu.providers': { description: 'settings', action: { kind: 'popup', run: 'sys setup' } },
|
|
489
|
-
'crtr.tmux.menu.graph': { description: 'graph
|
|
490
|
-
'crtr.tmux.menu.focus-manager': { description: '
|
|
605
|
+
'crtr.tmux.menu.graph': { description: 'canvas graph', action: { kind: 'keys', keys: '/graph' } },
|
|
606
|
+
'crtr.tmux.menu.focus-manager': { description: 'go to manager', action: { kind: 'command', run: 'surface node focus {manager}' } },
|
|
491
607
|
'crtr.tmux.menu.copy-id': { description: 'copy node id', action: { kind: 'command', run: 'surface node id --pane {pane}' } },
|
|
492
608
|
'crtr.tmux.menu.reload-viewer': { description: 'reload viewer', action: { kind: 'direct', run: 'surface reload --pane {pane}' } },
|
|
609
|
+
'crtr.tmux.menu.recap': { description: 'generate recap', action: { kind: 'keys', keys: '/recap' } },
|
|
493
610
|
'crtr.tmux.menu.pin': { description: 'pin conversation', action: { kind: 'direct', run: 'surface node pin --pane {pane}' } },
|
|
494
611
|
'crtr.tmux.menu.issue.idea': { description: 'idea', action: { kind: 'popup', run: 'canvas issue new --type idea' } },
|
|
495
612
|
'crtr.tmux.menu.issue.bug': { description: 'bug', action: { kind: 'popup', run: 'canvas issue new --type bug' } },
|
|
@@ -601,10 +718,17 @@ const TMUX_NAMED_KEYS = {
|
|
|
601
718
|
space: 'Space', up: 'Up', down: 'Down', left: 'Left', right: 'Right',
|
|
602
719
|
pageup: 'PPage', pagedown: 'NPage', home: 'Home', end: 'End',
|
|
603
720
|
};
|
|
604
|
-
function
|
|
721
|
+
function normalizedStrokeParts(stroke) {
|
|
722
|
+
if (stroke === '+')
|
|
723
|
+
return { key: '+', modifiers: [] };
|
|
724
|
+
if (stroke.endsWith('++'))
|
|
725
|
+
return { key: '+', modifiers: stroke.slice(0, -2).split('+') };
|
|
605
726
|
const parts = stroke.split('+');
|
|
606
|
-
|
|
607
|
-
|
|
727
|
+
return { key: parts.pop(), modifiers: parts };
|
|
728
|
+
}
|
|
729
|
+
function tmuxKey(stroke) {
|
|
730
|
+
let { key, modifiers: modifierParts } = normalizedStrokeParts(stroke);
|
|
731
|
+
const modifiers = new Set(modifierParts);
|
|
608
732
|
if (modifiers.has('shift') && key.length === 1 && /[a-z]/.test(key)) {
|
|
609
733
|
// tmux names a shifted printable with its uppercase character even when an
|
|
610
734
|
// extended-key terminal also preserves Shift as a modifier: Alt+Shift+F is
|
|
@@ -622,9 +746,8 @@ function tmuxKey(stroke) {
|
|
|
622
746
|
return `${prefix}${key}`;
|
|
623
747
|
}
|
|
624
748
|
function menuSelector(stroke) {
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
return parts.includes('shift') && /[a-z]/.test(key) ? key.toUpperCase() : key === 'space' ? ' ' : key;
|
|
749
|
+
const { key, modifiers } = normalizedStrokeParts(stroke);
|
|
750
|
+
return modifiers.includes('shift') && /[a-z]/.test(key) ? key.toUpperCase() : key === 'space' ? ' ' : key;
|
|
628
751
|
}
|
|
629
752
|
function boundMenuItems(ids, entries, bindings) {
|
|
630
753
|
const items = [];
|
|
@@ -659,8 +782,8 @@ function resolvedMenuActions(bindings) {
|
|
|
659
782
|
const attachItems = boundMenuItems(ATTACH_CONTROL_BINDINGS.map(({ menuId }) => menuId), entries, bindings);
|
|
660
783
|
if (attachItems.length > 0) {
|
|
661
784
|
entries.set('crtr.tmux.menu.attach', {
|
|
662
|
-
description: 'attach ›',
|
|
663
|
-
action: { kind: 'submenu', title: ' attach ', items: attachItems },
|
|
785
|
+
description: 'attach viewer ›',
|
|
786
|
+
action: { kind: 'submenu', title: ' attach viewer ', items: attachItems },
|
|
664
787
|
});
|
|
665
788
|
}
|
|
666
789
|
const showItems = boundMenuItems([
|
|
@@ -668,8 +791,8 @@ function resolvedMenuActions(bindings) {
|
|
|
668
791
|
'crtr.tmux.menu.context',
|
|
669
792
|
], entries, bindings);
|
|
670
793
|
entries.set('crtr.tmux.menu.show', {
|
|
671
|
-
description: '
|
|
672
|
-
action: { kind: 'submenu', title: '
|
|
794
|
+
description: 'inspect ›',
|
|
795
|
+
action: { kind: 'submenu', title: ' inspect node ', items: showItems },
|
|
673
796
|
});
|
|
674
797
|
// How this viewer PAINTS the transcript, as opposed to what it shows. Each item
|
|
675
798
|
// is a toggle the viewer applies to its own render state (a `/`-command sent to
|
|
@@ -677,8 +800,8 @@ function resolvedMenuActions(bindings) {
|
|
|
677
800
|
// reads as the action and the viewer answers with a notice.
|
|
678
801
|
const viewSettingsItems = boundMenuItems(['crtr.tmux.menu.fold-tools'], entries, bindings);
|
|
679
802
|
entries.set('crtr.tmux.menu.view-settings', {
|
|
680
|
-
description: '
|
|
681
|
-
action: { kind: 'submenu', title: '
|
|
803
|
+
description: 'display ›',
|
|
804
|
+
action: { kind: 'submenu', title: ' display ', items: viewSettingsItems },
|
|
682
805
|
});
|
|
683
806
|
const issueItems = boundMenuItems([
|
|
684
807
|
'crtr.tmux.menu.issue.idea',
|
|
@@ -716,6 +839,7 @@ function menuItems(bindings, entries) {
|
|
|
716
839
|
'crtr.tmux.menu.pin',
|
|
717
840
|
'crtr.tmux.menu.copy-id',
|
|
718
841
|
'crtr.tmux.menu.reload-viewer',
|
|
842
|
+
'crtr.tmux.menu.recap',
|
|
719
843
|
]));
|
|
720
844
|
addGroup('Lifecycle', boundItems([
|
|
721
845
|
'crtr.tmux.menu.fork',
|
|
@@ -743,37 +867,194 @@ function menuItems(bindings, entries) {
|
|
|
743
867
|
}
|
|
744
868
|
return items;
|
|
745
869
|
}
|
|
870
|
+
/** Read one token from tmux's sourceable `list-keys` output. Key names that
|
|
871
|
+
* contain punctuation are quoted, so splitting on whitespace would make their
|
|
872
|
+
* displayed token differ from the key name accepted by bind/unbind commands. */
|
|
873
|
+
function readTmuxToken(line, from) {
|
|
874
|
+
let index = from;
|
|
875
|
+
while (index < line.length && /\s/.test(line[index]))
|
|
876
|
+
index += 1;
|
|
877
|
+
if (index >= line.length)
|
|
878
|
+
return null;
|
|
879
|
+
let quote = null;
|
|
880
|
+
let value = '';
|
|
881
|
+
while (index < line.length) {
|
|
882
|
+
const char = line[index];
|
|
883
|
+
if (quote === null && /\s/.test(char))
|
|
884
|
+
break;
|
|
885
|
+
if (quote === null && (char === "'" || char === '"')) {
|
|
886
|
+
quote = char;
|
|
887
|
+
index += 1;
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if (quote === char) {
|
|
891
|
+
quote = null;
|
|
892
|
+
index += 1;
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (char === '\\' && quote !== "'" && index + 1 < line.length) {
|
|
896
|
+
value += line[index + 1];
|
|
897
|
+
index += 2;
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
value += char;
|
|
901
|
+
index += 1;
|
|
902
|
+
}
|
|
903
|
+
return { value, end: index };
|
|
904
|
+
}
|
|
905
|
+
function parseTmuxBinding(line) {
|
|
906
|
+
const commandName = readTmuxToken(line, 0);
|
|
907
|
+
if (commandName?.value !== 'bind-key')
|
|
908
|
+
return null;
|
|
909
|
+
let cursor = commandName.end;
|
|
910
|
+
let repeat = false;
|
|
911
|
+
while (true) {
|
|
912
|
+
const token = readTmuxToken(line, cursor);
|
|
913
|
+
if (token === null)
|
|
914
|
+
return null;
|
|
915
|
+
cursor = token.end;
|
|
916
|
+
if (token.value === '-r') {
|
|
917
|
+
repeat = true;
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
if (token.value !== '-T')
|
|
921
|
+
continue;
|
|
922
|
+
const table = readTmuxToken(line, cursor);
|
|
923
|
+
if (table === null)
|
|
924
|
+
return null;
|
|
925
|
+
const key = readTmuxToken(line, table.end);
|
|
926
|
+
if (key === null)
|
|
927
|
+
return null;
|
|
928
|
+
return {
|
|
929
|
+
table: table.value,
|
|
930
|
+
key: key.value,
|
|
931
|
+
command: line.slice(key.end).trimStart(),
|
|
932
|
+
repeat,
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
}
|
|
746
936
|
function bindingAt(table, key) {
|
|
747
937
|
const result = tmux(['list-keys', '-T', table]);
|
|
748
938
|
if (!result.ok)
|
|
749
939
|
return null;
|
|
750
940
|
return result.stdout.split('\n').find((line) => {
|
|
751
|
-
const
|
|
752
|
-
return
|
|
941
|
+
const parsed = parseTmuxBinding(line);
|
|
942
|
+
return parsed?.table === table && parsed.key === key;
|
|
753
943
|
}) ?? null;
|
|
754
944
|
}
|
|
755
|
-
|
|
756
|
-
|
|
945
|
+
/** Effective crouter gestures that tmux's root table currently consumes before
|
|
946
|
+
* pane input can see them. One list-keys snapshot serves the whole query. */
|
|
947
|
+
export function occupiedTmuxRootGestures(gestures) {
|
|
948
|
+
const result = tmux(['list-keys', '-T', ROOT_TABLE]);
|
|
949
|
+
if (!result.ok)
|
|
950
|
+
return [];
|
|
951
|
+
const occupied = new Set(result.stdout.split('\n').flatMap((line) => {
|
|
952
|
+
const parsed = parseTmuxBinding(line);
|
|
953
|
+
return parsed?.table === ROOT_TABLE ? [parsed.key] : [];
|
|
954
|
+
}));
|
|
955
|
+
return gestures.filter((gesture) => !gesture.includes(' > ') && occupied.has(tmuxKey(gesture)));
|
|
956
|
+
}
|
|
957
|
+
function isInstalledPair(value) {
|
|
958
|
+
return typeof value === 'object' && value !== null
|
|
959
|
+
&& typeof value.table === 'string'
|
|
960
|
+
&& typeof value.key === 'string';
|
|
961
|
+
}
|
|
962
|
+
function isWrappedRootBinding(value) {
|
|
963
|
+
if (typeof value !== 'object' || value === null)
|
|
964
|
+
return false;
|
|
965
|
+
const wrapper = value;
|
|
966
|
+
return typeof wrapper.bindingId === 'string'
|
|
967
|
+
&& BINDING_IDS.has(wrapper.bindingId)
|
|
968
|
+
&& typeof wrapper.gesture === 'string'
|
|
969
|
+
&& typeof wrapper.key === 'string'
|
|
970
|
+
&& (typeof wrapper.original === 'string' || wrapper.original === null)
|
|
971
|
+
&& typeof wrapper.wrapper === 'string'
|
|
972
|
+
&& (wrapper.state === 'owned' || wrapper.state === 'relinquished');
|
|
757
973
|
}
|
|
758
974
|
function readManifest() {
|
|
759
975
|
const option = tmux(['show-options', '-s', '-q', TMUX_BINDING_MANIFEST]);
|
|
760
976
|
if (!option.ok)
|
|
761
977
|
return null;
|
|
762
978
|
if (option.stdout === '')
|
|
763
|
-
return { pairs: [],
|
|
979
|
+
return { pairs: [], wrappers: [] };
|
|
764
980
|
const result = tmux(['show-options', '-s', '-qv', TMUX_BINDING_MANIFEST]);
|
|
765
981
|
if (!result.ok)
|
|
766
982
|
return null;
|
|
767
983
|
try {
|
|
768
984
|
const value = JSON.parse(result.stdout);
|
|
769
|
-
|
|
985
|
+
// Before passthrough wrappers, the manifest was the pair array itself.
|
|
986
|
+
if (Array.isArray(value))
|
|
987
|
+
return value.every(isInstalledPair) ? { pairs: value, wrappers: [] } : null;
|
|
988
|
+
if (typeof value !== 'object' || value === null)
|
|
989
|
+
return null;
|
|
990
|
+
const manifest = value;
|
|
991
|
+
if (!Array.isArray(manifest.pairs) || !manifest.pairs.every(isInstalledPair)
|
|
992
|
+
|| !Array.isArray(manifest.wrappers) || !manifest.wrappers.every(isWrappedRootBinding))
|
|
770
993
|
return null;
|
|
771
|
-
return { pairs:
|
|
994
|
+
return { pairs: manifest.pairs, wrappers: manifest.wrappers };
|
|
772
995
|
}
|
|
773
996
|
catch {
|
|
774
997
|
return null;
|
|
775
998
|
}
|
|
776
999
|
}
|
|
1000
|
+
function writeManifest(manifest) {
|
|
1001
|
+
return tmux(['set-option', '-s', TMUX_BINDING_MANIFEST, JSON.stringify(manifest)]);
|
|
1002
|
+
}
|
|
1003
|
+
/** Tmux parses semicolons once more even when argv already isolated them. */
|
|
1004
|
+
function tmuxKeyArgument(key) {
|
|
1005
|
+
return key.replace(/\\/g, '\\\\').replace(/;/g, '\\;');
|
|
1006
|
+
}
|
|
1007
|
+
function tmuxCommandToken(value) {
|
|
1008
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/;/g, '\\;')}"`;
|
|
1009
|
+
}
|
|
1010
|
+
/** A top-level separator is printed as `\;` in a bind-key line, but the same
|
|
1011
|
+
* sequence is literal after the command tail becomes one if-shell branch. */
|
|
1012
|
+
function bindingCommandForBranch(command) {
|
|
1013
|
+
let quote = null;
|
|
1014
|
+
let out = '';
|
|
1015
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
1016
|
+
const char = command[index];
|
|
1017
|
+
if (quote === null && (char === "'" || char === '"')) {
|
|
1018
|
+
quote = char;
|
|
1019
|
+
out += char;
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
if (quote === char) {
|
|
1023
|
+
quote = null;
|
|
1024
|
+
out += char;
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
if (char === '\\' && index + 1 < command.length) {
|
|
1028
|
+
const next = command[index + 1];
|
|
1029
|
+
if (quote === null && next === ';') {
|
|
1030
|
+
out += ';';
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
out += char + next;
|
|
1034
|
+
}
|
|
1035
|
+
index += 1;
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
out += char;
|
|
1039
|
+
}
|
|
1040
|
+
return out;
|
|
1041
|
+
}
|
|
1042
|
+
function sourceBindingLine(line) {
|
|
1043
|
+
const dir = mkdtempSync(join(tmpdir(), 'crtr-tmux-binding-'));
|
|
1044
|
+
const file = join(dir, 'binding.conf');
|
|
1045
|
+
try {
|
|
1046
|
+
writeFileSync(file, `${line}\n`, 'utf8');
|
|
1047
|
+
return tmux(['source-file', file]);
|
|
1048
|
+
}
|
|
1049
|
+
finally {
|
|
1050
|
+
rmSync(dir, { recursive: true, force: true });
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
function restoreWrappedBinding(wrapper) {
|
|
1054
|
+
return wrapper.original === null
|
|
1055
|
+
? tmux(['unbind-key', '-T', ROOT_TABLE, tmuxKeyArgument(wrapper.key)])
|
|
1056
|
+
: sourceBindingLine(wrapper.original);
|
|
1057
|
+
}
|
|
777
1058
|
// Crouter's own pre-manifest root bindings. Reconciliation always adopts an
|
|
778
1059
|
// untracked binding matching these crouter-specific markers, even when a partial
|
|
779
1060
|
// ownership manifest already exists. Older installs could leave M-c outside a
|
|
@@ -838,18 +1119,49 @@ function removeLegacyInboxBindings(manifestPairs) {
|
|
|
838
1119
|
}
|
|
839
1120
|
catch { /* best-effort */ }
|
|
840
1121
|
}
|
|
1122
|
+
// --quiet keeps the background `run-shell -b` binding from printing its result JSON, which
|
|
1123
|
+
// tmux would otherwise surface as a view-mode overlay on the active pane. The popup opening
|
|
1124
|
+
// is the feedback; a genuine failure still prints through the CLI toggle action. This deliberately
|
|
1125
|
+
// invokes bare `crtr` from PATH: it is installed once and must resolve to the selected generation,
|
|
1126
|
+
// not the sealed generation that installed it. The neighboring `crtr surface node cycle` builders
|
|
1127
|
+
// share that property.
|
|
1128
|
+
export function inboxToggleTmuxCommand() {
|
|
1129
|
+
return ['run-shell', '-b', 'crtr human inbox toggle --quiet --tmux-socket "#{socket_path}" --tmux-client "#{client_name}" --target-pane "#{pane_id}"'];
|
|
1130
|
+
}
|
|
841
1131
|
function actionCommand(id) {
|
|
842
1132
|
switch (id) {
|
|
843
1133
|
case 'crtr.tmux.node.next': return ['run-shell', `crtr surface node cycle --dir next --pane '#{pane_id}' >/dev/null 2>&1`];
|
|
844
1134
|
case 'crtr.tmux.node.previous': return ['run-shell', `crtr surface node cycle --dir prev --pane '#{pane_id}' >/dev/null 2>&1`];
|
|
845
1135
|
case 'crtr.tmux.inbox.toggle': return inboxToggleTmuxCommand();
|
|
846
|
-
case 'crtr.tmux.view.next': return ['run-shell', `crtr surface view cycle --dir next --pane '#{pane_id}' >/dev/null 2>&1`];
|
|
847
|
-
case 'crtr.tmux.view.previous': return ['run-shell', `crtr surface view cycle --dir prev --pane '#{pane_id}' >/dev/null 2>&1`];
|
|
848
1136
|
default: throw new Error(`no tmux command for ${id}`);
|
|
849
1137
|
}
|
|
850
1138
|
}
|
|
851
|
-
/**
|
|
852
|
-
|
|
1139
|
+
/** A directory beside the server socket serializes every process driving that
|
|
1140
|
+
* exact tmux server. Outside tmux, ask the default server for its socket first;
|
|
1141
|
+
* an unavailable server uses one per-user fallback lock for the error path. */
|
|
1142
|
+
function tmuxBindingLockPath() {
|
|
1143
|
+
const envSocket = process.env['TMUX']?.split(',')[0]?.trim();
|
|
1144
|
+
if (envSocket !== undefined && envSocket !== '')
|
|
1145
|
+
return `${envSocket}.crtr-keybindings.lock`;
|
|
1146
|
+
const result = tmux(['display-message', '-p', '#{socket_path}']);
|
|
1147
|
+
if (result.ok && result.stdout !== '')
|
|
1148
|
+
return `${result.stdout}.crtr-keybindings.lock`;
|
|
1149
|
+
const user = typeof process.getuid === 'function' ? process.getuid() : 'user';
|
|
1150
|
+
return join(tmpdir(), `crtr-tmux-keybindings-${user}.lock`);
|
|
1151
|
+
}
|
|
1152
|
+
/** Reconcile the tmux server with one immutable crouter settings snapshot.
|
|
1153
|
+
* Passing snapshots is hermetic; the no-argument front door reads both user
|
|
1154
|
+
* settings from the same config read. The server lock covers the entire
|
|
1155
|
+
* manifest read, restore, install, fingerprint, and write transition. */
|
|
1156
|
+
export function installTmuxBindings(bindingSnapshot, passthroughSnapshot = []) {
|
|
1157
|
+
return withExclusiveDirectoryLock(tmuxBindingLockPath(), () => reconcileTmuxBindings(bindingSnapshot, passthroughSnapshot));
|
|
1158
|
+
}
|
|
1159
|
+
function reconcileTmuxBindings(bindingSnapshot, passthroughSnapshot = []) {
|
|
1160
|
+
const userSettings = bindingSnapshot === undefined ? resolveUserKeybindingSettings() : undefined;
|
|
1161
|
+
const bindings = bindingSnapshot ?? userSettings.bindings;
|
|
1162
|
+
const passthrough = bindingSnapshot === undefined
|
|
1163
|
+
? userSettings.tmuxPassthrough
|
|
1164
|
+
: normalizeTmuxPassthrough(passthroughSnapshot);
|
|
853
1165
|
const diagnostics = [];
|
|
854
1166
|
const installed = [];
|
|
855
1167
|
const manifest = readManifest();
|
|
@@ -857,15 +1169,61 @@ export function installTmuxBindings(bindings = resolveUserKeybindings()) {
|
|
|
857
1169
|
diagnostics.push({ code: 'tmux-error', message: `tmux is unavailable or ${TMUX_BINDING_MANIFEST} is malformed` });
|
|
858
1170
|
return { ok: false, installed, diagnostics };
|
|
859
1171
|
}
|
|
1172
|
+
const desiredWrappers = new Map();
|
|
1173
|
+
for (const bindingId of passthrough) {
|
|
1174
|
+
const definition = BINDING_CATALOG.find((candidate) => candidate.id === bindingId);
|
|
1175
|
+
if (definition === undefined || !isAttachPaneBinding(definition))
|
|
1176
|
+
continue;
|
|
1177
|
+
for (const gesture of bindings.gestures(bindingId)) {
|
|
1178
|
+
if (gesture.includes(' > '))
|
|
1179
|
+
continue;
|
|
1180
|
+
const key = tmuxKey(gesture);
|
|
1181
|
+
if (!desiredWrappers.has(key))
|
|
1182
|
+
desiredWrappers.set(key, { bindingId, gesture });
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
// Restore every wrapper crouter still owns before the ordinary binding sweep.
|
|
1186
|
+
// A live line that differs from the fingerprint is a user's later rebind: it
|
|
1187
|
+
// is protected from this and every later reconciliation until they toggle the
|
|
1188
|
+
// passthrough preference off and on again.
|
|
1189
|
+
const protectedRootKeys = new Set();
|
|
1190
|
+
const wrappers = [];
|
|
1191
|
+
for (const prior of manifest.wrappers) {
|
|
1192
|
+
const desired = desiredWrappers.get(prior.key);
|
|
1193
|
+
const live = bindingAt(ROOT_TABLE, prior.key);
|
|
1194
|
+
if (prior.state === 'relinquished' || live !== prior.wrapper) {
|
|
1195
|
+
protectedRootKeys.add(prior.key);
|
|
1196
|
+
if (desired !== undefined) {
|
|
1197
|
+
wrappers.push({ ...prior, ...desired, state: 'relinquished' });
|
|
1198
|
+
}
|
|
1199
|
+
continue;
|
|
1200
|
+
}
|
|
1201
|
+
const restored = restoreWrappedBinding(prior);
|
|
1202
|
+
if (!restored.ok) {
|
|
1203
|
+
protectedRootKeys.add(prior.key);
|
|
1204
|
+
wrappers.push(prior);
|
|
1205
|
+
diagnostics.push({
|
|
1206
|
+
code: 'tmux-error',
|
|
1207
|
+
bindingId: prior.bindingId,
|
|
1208
|
+
gesture: prior.gesture,
|
|
1209
|
+
message: `could not restore tmux binding ${ROOT_TABLE}:${prior.key}: ${restored.stderr}`,
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
860
1213
|
adoptLegacyBindings();
|
|
861
1214
|
removeLegacyInboxBindings(manifest.pairs);
|
|
862
1215
|
for (const pair of manifest.pairs) {
|
|
863
|
-
|
|
1216
|
+
if (pair.table === ROOT_TABLE && protectedRootKeys.has(pair.key))
|
|
1217
|
+
continue;
|
|
1218
|
+
const result = tmux(['unbind-key', '-T', pair.table, tmuxKeyArgument(pair.key)]);
|
|
864
1219
|
if (!result.ok && bindingAt(pair.table, pair.key) !== null) {
|
|
865
1220
|
diagnostics.push({ code: 'tmux-error', message: `could not remove crouter binding ${pair.table}:${pair.key}: ${result.stderr}` });
|
|
866
1221
|
}
|
|
867
1222
|
}
|
|
868
1223
|
if (diagnostics.some((diagnostic) => diagnostic.code === 'tmux-error')) {
|
|
1224
|
+
const save = writeManifest({ pairs: manifest.pairs, wrappers });
|
|
1225
|
+
if (!save.ok)
|
|
1226
|
+
diagnostics.push({ code: 'tmux-error', message: `could not save crouter tmux ownership manifest: ${save.stderr}` });
|
|
869
1227
|
return { ok: false, installed, diagnostics };
|
|
870
1228
|
}
|
|
871
1229
|
const install = (table, key, command, id, gesture) => {
|
|
@@ -873,7 +1231,7 @@ export function installTmuxBindings(bindings = resolveUserKeybindings()) {
|
|
|
873
1231
|
diagnostics.push({ code: 'occupied', bindingId: id, gesture, message: `${table}:${key} is already bound by tmux; ${id} was not installed` });
|
|
874
1232
|
return false;
|
|
875
1233
|
}
|
|
876
|
-
const result = tmux(['bind-key', '-T', table, key, ...command]);
|
|
1234
|
+
const result = tmux(['bind-key', '-T', table, tmuxKeyArgument(key), ...command]);
|
|
877
1235
|
if (!result.ok) {
|
|
878
1236
|
diagnostics.push({ code: 'tmux-error', bindingId: id, gesture, message: `could not install ${id}: ${result.stderr}` });
|
|
879
1237
|
return false;
|
|
@@ -906,32 +1264,51 @@ export function installTmuxBindings(bindings = resolveUserKeybindings()) {
|
|
|
906
1264
|
for (const gesture of bindings.gestures(id))
|
|
907
1265
|
install(ROOT_TABLE, tmuxKey(gesture), actionCommand(id), id, gesture);
|
|
908
1266
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1267
|
+
// Wrap only occupied roots. Unoccupied gestures already reach the pane and do
|
|
1268
|
+
// not need crouter ownership. Re-read the installed line as the fingerprint;
|
|
1269
|
+
// tmux canonicalizes quoting and braces in ways hand serialization cannot.
|
|
1270
|
+
const newlyWrapped = [];
|
|
1271
|
+
for (const [key, desired] of desiredWrappers) {
|
|
1272
|
+
if (protectedRootKeys.has(key))
|
|
1273
|
+
continue;
|
|
1274
|
+
const original = bindingAt(ROOT_TABLE, key);
|
|
1275
|
+
if (original === null)
|
|
1276
|
+
continue;
|
|
1277
|
+
const parsed = parseTmuxBinding(original);
|
|
1278
|
+
if (parsed === null || parsed.command === '') {
|
|
1279
|
+
diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not parse occupied tmux binding ${ROOT_TABLE}:${key}` });
|
|
1280
|
+
continue;
|
|
917
1281
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1282
|
+
const result = tmux([
|
|
1283
|
+
'bind-key',
|
|
1284
|
+
...(parsed.repeat ? ['-r'] : []),
|
|
1285
|
+
'-T', ROOT_TABLE, tmuxKeyArgument(key),
|
|
1286
|
+
'if-shell', '-F', '#{!=:#{@crtr_attach},}',
|
|
1287
|
+
`send-keys ${tmuxCommandToken(key)}`,
|
|
1288
|
+
bindingCommandForBranch(parsed.command),
|
|
1289
|
+
]);
|
|
1290
|
+
if (!result.ok) {
|
|
1291
|
+
diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not wrap ${ROOT_TABLE}:${key}: ${result.stderr}` });
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
const wrapper = bindingAt(ROOT_TABLE, key);
|
|
1295
|
+
if (wrapper === null || !wrapper.includes('@crtr_attach')) {
|
|
1296
|
+
diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not fingerprint wrapped tmux binding ${ROOT_TABLE}:${key}` });
|
|
923
1297
|
continue;
|
|
924
1298
|
}
|
|
925
|
-
const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
if (install(table, entry.second, actionCommand(entry.id), entry.id, entry.gesture))
|
|
929
|
-
children++;
|
|
930
|
-
if (children > 0)
|
|
931
|
-
install(ROOT_TABLE, prefix, ['switch-client', '-T', table], entries[0].id, entries[0].gesture);
|
|
1299
|
+
const record = { ...desired, key, original, wrapper, state: 'owned' };
|
|
1300
|
+
wrappers.push(record);
|
|
1301
|
+
newlyWrapped.push(record);
|
|
932
1302
|
}
|
|
933
|
-
const save =
|
|
934
|
-
if (!save.ok)
|
|
1303
|
+
const save = writeManifest({ pairs: installed, wrappers });
|
|
1304
|
+
if (!save.ok) {
|
|
935
1305
|
diagnostics.push({ code: 'tmux-error', message: `could not save crouter tmux ownership manifest: ${save.stderr}` });
|
|
1306
|
+
// A wrapper without restoration data is unsafe. Put each newly wrapped key
|
|
1307
|
+
// back immediately when manifest persistence fails.
|
|
1308
|
+
for (const wrapper of newlyWrapped) {
|
|
1309
|
+
if (bindingAt(ROOT_TABLE, wrapper.key) === wrapper.wrapper)
|
|
1310
|
+
restoreWrappedBinding(wrapper);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
936
1313
|
return { ok: diagnostics.every((diagnostic) => diagnostic.code !== 'tmux-error'), installed, diagnostics };
|
|
937
1314
|
}
|