@crouton-kit/crouter 0.3.27 → 0.3.29
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/build-root.js +4 -2
- package/dist/builtin-memory/crouter-development/marketplaces.md +7 -6
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +1 -1
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +1 -1
- package/dist/builtin-memory/crouter-development/personas.md +1 -1
- package/dist/builtin-memory/crouter-development/plugins.md +20 -18
- package/dist/builtin-memory/design.md +1 -1
- package/dist/builtin-memory/development.md +1 -1
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/planning.md +1 -1
- package/dist/builtin-memory/spec.md +6 -4
- package/dist/builtin-personas/orchestration-kernel.md +4 -5
- package/dist/builtin-personas/runtime-base.md +8 -4
- package/dist/builtin-personas/spec/PERSONA.md +8 -4
- package/dist/builtin-personas/spec/orchestrator.md +5 -3
- package/dist/builtin-personas/spec/requirements/PERSONA.md +10 -0
- package/dist/builtin-views/canvas/core.mjs +586 -0
- package/dist/builtin-views/canvas/text.mjs +58 -0
- package/dist/builtin-views/canvas/tui.mjs +165 -0
- package/dist/builtin-views/canvas/web.jsx +120 -0
- package/dist/builtin-views/git-pr/core.mjs +673 -0
- package/dist/builtin-views/git-pr/text.mjs +84 -0
- package/dist/builtin-views/git-pr/tui.mjs +302 -0
- package/dist/builtin-views/git-pr/web.jsx +216 -0
- package/dist/builtin-views/inbox/_lib/render.mjs +1 -3
- package/dist/builtin-views/inbox/core.mjs +1273 -0
- package/dist/builtin-views/inbox/text.mjs +73 -0
- package/dist/builtin-views/inbox/tui.mjs +312 -0
- package/dist/builtin-views/inbox/web.jsx +188 -0
- package/dist/builtin-views/linkedin/core.mjs +906 -0
- package/dist/builtin-views/linkedin/text.mjs +69 -0
- package/dist/builtin-views/linkedin/tui.mjs +427 -0
- package/dist/builtin-views/linkedin/web.jsx +206 -0
- package/dist/builtin-views/workspace-sidebar/core.mjs +621 -0
- package/dist/builtin-views/workspace-sidebar/text.mjs +53 -0
- package/dist/builtin-views/workspace-sidebar/tui.mjs +142 -0
- package/dist/builtin-views/workspace-sidebar/web.jsx +109 -0
- package/dist/cli.js +3 -3
- package/dist/clients/attach/__tests__/action-parity.test.js +48 -0
- package/dist/clients/attach/__tests__/clipboard-image.test.js +76 -0
- package/dist/clients/attach/__tests__/editor-newline.test.js +57 -0
- package/dist/clients/attach/__tests__/git-info.test.js +31 -0
- package/dist/clients/attach/__tests__/onrequest-wired.test.js +20 -0
- package/dist/clients/attach/__tests__/reconnect-giveup.test.js +30 -0
- package/dist/clients/attach/__tests__/slash-quit-copy.test.js +42 -0
- package/dist/clients/attach/__tests__/titled-editor.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/titled-editor.test.js +39 -0
- package/dist/clients/attach/attach-cmd.js +445 -41
- package/dist/clients/attach/auth-pickers.d.ts +9 -0
- package/dist/clients/attach/auth-pickers.js +194 -0
- package/dist/clients/attach/canvas-panels.d.ts +12 -0
- package/dist/clients/attach/canvas-panels.js +78 -0
- package/dist/clients/attach/chat-view.d.ts +27 -2
- package/dist/clients/attach/chat-view.js +98 -9
- package/dist/clients/attach/clipboard-image.d.ts +47 -8
- package/dist/clients/attach/clipboard-image.js +150 -26
- package/dist/clients/attach/clipboard-text.d.ts +3 -0
- package/dist/clients/attach/clipboard-text.js +39 -0
- package/dist/clients/attach/config-load.d.ts +54 -7
- package/dist/clients/attach/config-load.js +99 -3
- package/dist/clients/attach/context-message.d.ts +12 -0
- package/dist/clients/attach/context-message.js +72 -0
- package/dist/clients/attach/git-info.d.ts +20 -0
- package/dist/clients/attach/git-info.js +44 -0
- package/dist/clients/attach/graph-overlay.d.ts +43 -0
- package/dist/clients/attach/graph-overlay.js +306 -0
- package/dist/clients/attach/input-controller.d.ts +81 -13
- package/dist/clients/attach/input-controller.js +307 -67
- package/dist/clients/attach/pickers.d.ts +58 -0
- package/dist/clients/attach/pickers.js +171 -0
- package/dist/clients/attach/slash-commands.d.ts +44 -1
- package/dist/clients/attach/slash-commands.js +250 -27
- package/dist/clients/attach/titled-editor.d.ts +33 -0
- package/dist/clients/attach/titled-editor.js +84 -0
- package/dist/clients/attach/view-socket.d.ts +37 -2
- package/dist/clients/attach/view-socket.js +131 -7
- package/dist/clients/web/dev-server.d.ts +7 -0
- package/dist/clients/web/dev-server.js +59 -0
- package/dist/clients/web/events.d.ts +14 -0
- package/dist/clients/web/events.js +151 -0
- package/dist/clients/web/server.d.ts +18 -0
- package/dist/clients/web/server.js +450 -0
- package/dist/clients/web/web-cmd.d.ts +2 -0
- package/dist/clients/web/web-cmd.js +120 -0
- package/dist/commands/canvas.js +1 -2
- package/dist/commands/chord.js +1 -1
- package/dist/commands/dashboard.js +5 -1
- package/dist/commands/human/prompts.js +3 -3
- package/dist/commands/human/queue.d.ts +17 -0
- package/dist/commands/human/queue.js +111 -4
- package/dist/commands/memory/__tests__/lint-schema.test.js +27 -4
- package/dist/commands/memory/lint.d.ts +5 -4
- package/dist/commands/memory/lint.js +23 -7
- package/dist/commands/memory/read.js +2 -2
- package/dist/commands/memory/shared.d.ts +7 -1
- package/dist/commands/memory/shared.js +18 -3
- package/dist/commands/memory/write.js +20 -7
- package/dist/commands/memory.js +4 -4
- package/dist/commands/node.d.ts +1 -1
- package/dist/commands/node.js +64 -65
- package/dist/commands/pkg.js +1 -2
- package/dist/commands/revive.js +11 -3
- package/dist/commands/sys/feedback.d.ts +1 -0
- package/dist/commands/sys/feedback.js +163 -0
- package/dist/commands/sys/sync.d.ts +1 -0
- package/dist/commands/sys/sync.js +187 -0
- package/dist/commands/sys.js +4 -2
- package/dist/commands/view-cycle.js +2 -2
- package/dist/commands/view-list.js +8 -8
- package/dist/commands/view-new.js +21 -17
- package/dist/commands/view-pick.js +1 -1
- package/dist/commands/view-run.js +35 -14
- package/dist/commands/view.js +8 -6
- package/dist/commands/workspace.d.ts +2 -0
- package/dist/commands/workspace.js +161 -0
- package/dist/core/__tests__/broker-double-spawn.test.d.ts +1 -0
- package/dist/core/__tests__/broker-double-spawn.test.js +143 -0
- package/dist/core/__tests__/broker-fork-seam.test.d.ts +1 -0
- package/dist/core/__tests__/broker-fork-seam.test.js +102 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.js +2 -0
- package/dist/core/__tests__/broker-snapshot-history.test.d.ts +1 -0
- package/dist/core/__tests__/broker-snapshot-history.test.js +105 -0
- package/dist/core/__tests__/child-death-wake.test.js +56 -111
- package/dist/core/__tests__/close.test.js +14 -20
- package/dist/core/__tests__/context-intro.test.js +19 -19
- package/dist/core/__tests__/daemon-boot.test.js +30 -23
- package/dist/core/__tests__/draw-style.test.js +1 -1
- package/dist/core/__tests__/error-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/error-stall-recycle.test.js +141 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +24 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +32 -2
- package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
- package/dist/core/__tests__/focuses.test.js +25 -13
- package/dist/core/__tests__/fork.test.js +22 -5
- package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-control-preempt.test.js +61 -0
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +57 -0
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +80 -0
- package/dist/core/__tests__/full/broker-pane-resolution.test.js +90 -0
- package/dist/core/__tests__/full/cascade-close.test.js +22 -8
- package/dist/core/__tests__/full/detach-focus.test.js +66 -169
- package/dist/core/__tests__/helpers/harness.d.ts +6 -9
- package/dist/core/__tests__/helpers/harness.js +19 -36
- package/dist/core/__tests__/human-node-not-supervised.test.d.ts +1 -0
- package/dist/core/__tests__/human-node-not-supervised.test.js +83 -0
- package/dist/core/__tests__/human-stranded-deliver.test.d.ts +1 -0
- package/dist/core/__tests__/human-stranded-deliver.test.js +108 -0
- package/dist/core/__tests__/lifecycle.test.js +1 -19
- package/dist/core/__tests__/live-mutation-verbs.test.js +5 -5
- package/dist/core/__tests__/memory-resolver.test.js +1 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.d.ts +1 -0
- package/dist/core/__tests__/on-read-dedup-resume.test.js +81 -0
- package/dist/core/__tests__/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/refresh-stall-recycle.test.js +88 -0
- package/dist/core/__tests__/relaunch-root.test.d.ts +1 -0
- package/dist/core/__tests__/relaunch-root.test.js +114 -0
- package/dist/core/__tests__/reset.test.js +19 -79
- package/dist/core/__tests__/steer-note.test.js +9 -7
- package/dist/core/__tests__/tmux-surface.test.js +13 -7
- package/dist/core/bootstrap.d.ts +0 -3
- package/dist/core/bootstrap.js +1 -143
- package/dist/core/canvas/browse/__tests__/model.test.js +86 -1
- package/dist/core/canvas/browse/__tests__/render.test.js +2 -1
- package/dist/core/canvas/browse/app.js +131 -8
- package/dist/core/canvas/browse/model.d.ts +23 -5
- package/dist/core/canvas/browse/model.js +68 -13
- package/dist/core/canvas/browse/render.d.ts +7 -1
- package/dist/core/canvas/browse/render.js +178 -47
- package/dist/core/canvas/canvas.js +1 -1
- package/dist/core/canvas/focuses.d.ts +4 -4
- package/dist/core/canvas/focuses.js +7 -7
- package/dist/core/canvas/nav-model.d.ts +162 -0
- package/dist/core/canvas/nav-model.js +487 -0
- package/dist/core/canvas/paths.d.ts +7 -0
- package/dist/core/canvas/paths.js +9 -0
- package/dist/core/canvas/render.d.ts +68 -9
- package/dist/core/canvas/render.js +195 -61
- package/dist/core/canvas/types.d.ts +16 -23
- package/dist/core/config.js +1 -4
- package/dist/core/memory-resolver.js +43 -5
- package/dist/core/runtime/bearings.d.ts +1 -1
- package/dist/core/runtime/bearings.js +8 -8
- package/dist/core/runtime/branded-host.d.ts +16 -0
- package/dist/core/runtime/branded-host.js +127 -0
- package/dist/core/runtime/broker-protocol.d.ts +212 -6
- package/dist/core/runtime/broker-sdk.d.ts +0 -12
- package/dist/core/runtime/broker-sdk.js +77 -6
- package/dist/core/runtime/broker.d.ts +5 -1
- package/dist/core/runtime/broker.js +520 -59
- package/dist/core/runtime/close.d.ts +12 -2
- package/dist/core/runtime/close.js +37 -16
- package/dist/core/runtime/front-door.js +14 -7
- package/dist/core/runtime/host.d.ts +14 -34
- package/dist/core/runtime/host.js +14 -50
- package/dist/core/runtime/launch.d.ts +12 -9
- package/dist/core/runtime/launch.js +27 -15
- package/dist/core/runtime/lifecycle.d.ts +1 -1
- package/dist/core/runtime/lifecycle.js +15 -19
- package/dist/core/runtime/nodes.d.ts +0 -55
- package/dist/core/runtime/nodes.js +6 -74
- package/dist/core/runtime/placement.d.ts +91 -334
- package/dist/core/runtime/placement.js +262 -828
- package/dist/core/runtime/promote.d.ts +2 -0
- package/dist/core/runtime/promote.js +24 -6
- package/dist/core/runtime/recap.d.ts +8 -0
- package/dist/core/runtime/recap.js +107 -0
- package/dist/core/runtime/recycle.js +25 -61
- package/dist/core/runtime/reset.d.ts +43 -37
- package/dist/core/runtime/reset.js +131 -218
- package/dist/core/runtime/revive.d.ts +9 -29
- package/dist/core/runtime/revive.js +45 -135
- package/dist/core/runtime/spawn.d.ts +15 -11
- package/dist/core/runtime/spawn.js +168 -136
- package/dist/core/runtime/surface-bg.d.ts +11 -0
- package/dist/core/runtime/surface-bg.js +68 -0
- package/dist/core/runtime/tmux.d.ts +15 -43
- package/dist/core/runtime/tmux.js +38 -80
- package/dist/core/scope.d.ts +1 -2
- package/dist/core/scope.js +1 -5
- package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts +1 -0
- package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +57 -0
- package/dist/core/skill-sync/builtins.d.ts +42 -0
- package/dist/core/skill-sync/builtins.js +112 -0
- package/dist/core/skill-sync/claude-plugins.d.ts +23 -0
- package/dist/core/skill-sync/claude-plugins.js +71 -0
- package/dist/core/skill-sync/engine.d.ts +42 -0
- package/dist/core/skill-sync/engine.js +633 -0
- package/dist/core/skill-sync/export.d.ts +23 -0
- package/dist/core/skill-sync/export.js +86 -0
- package/dist/core/skill-sync/manifest.d.ts +64 -0
- package/dist/core/skill-sync/manifest.js +181 -0
- package/dist/core/skill-sync/profile.d.ts +76 -0
- package/dist/core/skill-sync/profile.js +173 -0
- package/dist/core/skill-sync/snapshot.d.ts +57 -0
- package/dist/core/skill-sync/snapshot.js +120 -0
- package/dist/core/spawn.d.ts +14 -0
- package/dist/core/spawn.js +29 -9
- package/dist/core/substrate/index.d.ts +2 -2
- package/dist/core/substrate/index.js +7 -7
- package/dist/core/substrate/injected-store.d.ts +10 -0
- package/dist/core/substrate/injected-store.js +55 -0
- package/dist/core/substrate/on-read.js +68 -6
- package/dist/core/substrate/render.d.ts +8 -11
- package/dist/core/substrate/render.js +29 -43
- package/dist/core/substrate/schema.d.ts +16 -11
- package/dist/core/substrate/schema.js +32 -31
- package/dist/core/tui/host.d.ts +12 -4
- package/dist/core/tui/host.js +280 -149
- package/dist/core/view/bridge.d.ts +10 -0
- package/dist/core/view/bridge.js +31 -0
- package/dist/core/view/chrome.d.ts +9 -0
- package/dist/core/view/chrome.js +22 -0
- package/dist/core/view/contract.d.ts +171 -0
- package/dist/core/view/contract.js +23 -0
- package/dist/core/view/loader.d.ts +31 -0
- package/dist/core/view/loader.js +188 -0
- package/dist/core/view/transport-local.d.ts +7 -0
- package/dist/core/view/transport-local.js +70 -0
- package/dist/core/view/transport.d.ts +4 -0
- package/dist/core/view/transport.js +15 -0
- package/dist/daemon/crtrd.d.ts +38 -27
- package/dist/daemon/crtrd.js +303 -376
- package/dist/daemon/manage.js +6 -1
- package/dist/index.js +1 -1
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +28 -20
- package/dist/pi-extensions/canvas-context-intro.js +4 -4
- package/dist/pi-extensions/canvas-doc-substrate.js +29 -20
- package/dist/pi-extensions/canvas-nav.js +35 -385
- package/dist/pi-extensions/canvas-recap.d.ts +37 -0
- package/dist/pi-extensions/canvas-recap.js +236 -0
- package/dist/pi-extensions/canvas-resume.js +2 -1
- package/dist/pi-extensions/canvas-stophook.d.ts +15 -13
- package/dist/pi-extensions/canvas-stophook.js +119 -103
- package/dist/pi-extensions/canvas-view.js +2 -1
- package/dist/pi-extensions/widget-order-bus.d.ts +6 -0
- package/dist/pi-extensions/widget-order-bus.js +34 -0
- package/dist/prompts/view.d.ts +2 -2
- package/dist/prompts/view.js +145 -73
- package/dist/types.d.ts +1 -6
- package/dist/types.js +1 -3
- package/dist/web/ViewChrome.d.ts +7 -0
- package/dist/web/ViewChrome.js +28 -0
- package/dist/web/ViewPane.d.ts +39 -0
- package/dist/web/ViewPane.js +48 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web/index.js +16 -0
- package/dist/web/runtime.d.ts +39 -0
- package/dist/web/runtime.js +133 -0
- package/dist/web/states.d.ts +24 -0
- package/dist/web/states.js +24 -0
- package/dist/web/transport-http.d.ts +5 -0
- package/dist/web/transport-http.js +28 -0
- package/dist/web-client/assets/index-BUvQb4hR.css +2 -0
- package/dist/web-client/assets/index-ClLQXYAE.js +10 -0
- package/dist/web-client/index.html +13 -0
- package/package.json +20 -6
- package/dist/builtin-views/canvas/client.mjs +0 -303
- package/dist/builtin-views/canvas/view.mjs +0 -576
- package/dist/builtin-views/git-pr/client.mjs +0 -440
- package/dist/builtin-views/git-pr/view.mjs +0 -675
- package/dist/builtin-views/inbox/sources/gmail.mjs +0 -965
- package/dist/builtin-views/inbox/sources/linkedin.mjs +0 -427
- package/dist/builtin-views/inbox/view.mjs +0 -889
- package/dist/builtin-views/linkedin/client.mjs +0 -610
- package/dist/builtin-views/linkedin/view.mjs +0 -1171
- package/dist/commands/pkg/bridge.d.ts +0 -1
- package/dist/commands/pkg/bridge.js +0 -137
- package/dist/commands/skill/author.d.ts +0 -3
- package/dist/commands/skill/author.js +0 -140
- package/dist/commands/skill/shared.d.ts +0 -3
- package/dist/commands/skill/shared.js +0 -19
- package/dist/commands/skill.d.ts +0 -2
- package/dist/commands/skill.js +0 -21
- package/dist/commands/tmux-spread.d.ts +0 -2
- package/dist/commands/tmux-spread.js +0 -144
- package/dist/core/__tests__/full/daemon-liveness-pane.full.test.js +0 -292
- package/dist/core/__tests__/full/placement-focus.test.js +0 -309
- package/dist/core/__tests__/full/placement-reconcile.test.js +0 -212
- package/dist/core/__tests__/full/placement-revive.test.js +0 -238
- package/dist/core/__tests__/full/placement-teardown.test.js +0 -221
- package/dist/core/__tests__/home-session.test.js +0 -186
- package/dist/core/__tests__/relaunch.test.js +0 -335
- package/dist/core/bridge-map.d.ts +0 -19
- package/dist/core/bridge-map.js +0 -73
- package/dist/core/tui/contract.d.ts +0 -83
- package/dist/core/tui/contract.js +0 -8
- package/dist/core/tui/loader.d.ts +0 -16
- package/dist/core/tui/loader.js +0 -94
- package/dist/prompts/skill.d.ts +0 -2
- package/dist/prompts/skill.js +0 -650
- /package/dist/{core/__tests__/full/daemon-liveness-pane.full.test.d.ts → clients/attach/__tests__/action-parity.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-focus.test.d.ts → clients/attach/__tests__/clipboard-image.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-reconcile.test.d.ts → clients/attach/__tests__/editor-newline.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-revive.test.d.ts → clients/attach/__tests__/git-info.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-teardown.test.d.ts → clients/attach/__tests__/onrequest-wired.test.d.ts} +0 -0
- /package/dist/{core/__tests__/home-session.test.d.ts → clients/attach/__tests__/reconnect-giveup.test.d.ts} +0 -0
- /package/dist/{core/__tests__/relaunch.test.d.ts → clients/attach/__tests__/slash-quit-copy.test.d.ts} +0 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// pickers.ts — native interactive pickers for `crtr attach`, built against the
|
|
2
|
+
// operator-view wire contract (broker read-op `data` frames). Each builder turns
|
|
3
|
+
// a serialized picker payload into the REAL pi `*SelectorComponent`, wired so a
|
|
4
|
+
// selection dispatches the SAME command frame the text-arg slash path sends and
|
|
5
|
+
// `close()` tears the overlay down. InputController owns the overlay lifecycle
|
|
6
|
+
// (showOverlay/dismiss/focus) and the async data fetch; these are pure builders.
|
|
7
|
+
//
|
|
8
|
+
// FOCUS (load-bearing): some selectors put `handleInput` on an INNER list, not
|
|
9
|
+
// the outer Container (UserMessageSelectorComponent → getMessageList(),
|
|
10
|
+
// SettingsSelectorComponent → getSettingsList()). pi-tui drops every keystroke
|
|
11
|
+
// when the focused component has no `handleInput`, so each builder returns BOTH
|
|
12
|
+
// the overlay `component` AND the `focus` target the caller must focus — exactly
|
|
13
|
+
// as pi's own interactive-mode does (`{ component, focus }`).
|
|
14
|
+
//
|
|
15
|
+
// R1 (contract): the tree/fork/settings payloads are PURE DATA — their selectors
|
|
16
|
+
// build verbatim, no reconstruction. The MODEL picker's `ModelSelectorComponent`
|
|
17
|
+
// needs a live `ModelRegistry`/`SettingsManager`: we feed the REAL component a
|
|
18
|
+
// minimal in-memory `SettingsManager` plus a tiny `ModelRegistry` adapter (the
|
|
19
|
+
// component calls only refresh/getError/getAvailable/find — verified against
|
|
20
|
+
// model-selector.js), preserving its full search + scope-toggle UI without
|
|
21
|
+
// crossing engine state over the socket. The SESSION picker reconstructs
|
|
22
|
+
// faithfully from async loaders (no shim). The SCOPED-MODELS component is NOT
|
|
23
|
+
// re-exported by pi (through 0.79.1) AND has no mutation frame in the contract,
|
|
24
|
+
// so it renders read-only via a pi-tui SelectList.
|
|
25
|
+
//
|
|
26
|
+
// pi pin is `^0.79.1` (a RANGE): the duck-typed registry adapter relies on the
|
|
27
|
+
// component's current 4-method registry surface — a future 0.79.x adding a
|
|
28
|
+
// registry call would surface at runtime (caught by live verification).
|
|
29
|
+
import { SelectList, } from '@earendil-works/pi-tui';
|
|
30
|
+
import { getSelectListTheme, ModelSelectorComponent, SessionSelectorComponent, SettingsSelectorComponent, TreeSelectorComponent, UserMessageSelectorComponent, SettingsManager, } from '@earendil-works/pi-coding-agent';
|
|
31
|
+
/** A minimal `ModelRegistry` for `ModelSelectorComponent`: the component calls
|
|
32
|
+
* only `refresh()`, `getError()`, `getAvailable()`, and `find()` (verified
|
|
33
|
+
* against pi `model-selector.js`). We satisfy exactly those from the wire
|
|
34
|
+
* payload — no auth/registry state crosses the socket — and cast to the nominal
|
|
35
|
+
* class the ctor wants. */
|
|
36
|
+
function modelRegistryAdapter(all, available) {
|
|
37
|
+
const adapter = {
|
|
38
|
+
refresh() { },
|
|
39
|
+
getError() {
|
|
40
|
+
return undefined;
|
|
41
|
+
},
|
|
42
|
+
getAvailable() {
|
|
43
|
+
return available;
|
|
44
|
+
},
|
|
45
|
+
find(provider, id) {
|
|
46
|
+
return all.find((m) => m.provider === provider && m.id === id);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
return adapter;
|
|
50
|
+
}
|
|
51
|
+
/** `/model` (and ctrl+l) — the real model selector with search + scope toggle.
|
|
52
|
+
* Select → `set_model` with the resolved `provider/id` (the broker's
|
|
53
|
+
* `findModelSpec` requires that form). */
|
|
54
|
+
export function buildModelPicker(tui, data, send, close) {
|
|
55
|
+
const all = data.models;
|
|
56
|
+
const availableSet = new Set(data.availableIds);
|
|
57
|
+
const available = all.filter((m) => availableSet.has(`${m.provider}/${m.id}`));
|
|
58
|
+
const current = data.current
|
|
59
|
+
? all.find((m) => m.provider === data.current.provider && m.id === data.current.id)
|
|
60
|
+
: undefined;
|
|
61
|
+
const component = new ModelSelectorComponent(tui, current, SettingsManager.inMemory(), modelRegistryAdapter(all, available), data.scopedModels, (model) => {
|
|
62
|
+
send({ type: 'set_model', model: `${model.provider}/${model.id}` });
|
|
63
|
+
close();
|
|
64
|
+
}, () => close());
|
|
65
|
+
return { component, focus: component };
|
|
66
|
+
}
|
|
67
|
+
/** `/resume` — the real session selector. Reconstructs from async loaders that
|
|
68
|
+
* fetch each scope on demand (cwd is pre-fetched so the current-session marker
|
|
69
|
+
* shows immediately; the `all` scope loads only on toggle). Select →
|
|
70
|
+
* `switch_session`. Rename/delete are out of scope (no command frame). */
|
|
71
|
+
export function buildSessionPicker(tui, prefetchedCwd, request, keybindings, send, close) {
|
|
72
|
+
const revive = (d) => d.sessions.map((s) => ({ ...s, created: new Date(s.created), modified: new Date(s.modified) }));
|
|
73
|
+
let firstCwd = true;
|
|
74
|
+
const currentLoader = async () => {
|
|
75
|
+
if (firstCwd) {
|
|
76
|
+
firstCwd = false;
|
|
77
|
+
return revive(prefetchedCwd);
|
|
78
|
+
}
|
|
79
|
+
return revive((await request({ type: 'list_sessions', scope: 'cwd' })));
|
|
80
|
+
};
|
|
81
|
+
const allLoader = async () => revive((await request({ type: 'list_sessions', scope: 'all' })));
|
|
82
|
+
const component = new SessionSelectorComponent(currentLoader, allLoader, (path) => {
|
|
83
|
+
send({ type: 'switch_session', path });
|
|
84
|
+
close();
|
|
85
|
+
}, () => close(), () => close(), () => tui.requestRender(), { showRenameHint: false, keybindings: keybindings }, prefetchedCwd.currentSessionFile);
|
|
86
|
+
return { component, focus: component };
|
|
87
|
+
}
|
|
88
|
+
/** `/tree` — the real tree navigator (pure data). Select → `navigate_tree`.
|
|
89
|
+
* Label-edit is out of scope (no command frame) → no `onLabelChange`. */
|
|
90
|
+
export function buildTreePicker(tui, data, send, close) {
|
|
91
|
+
const terminalHeight = process.stdout.rows ?? 24;
|
|
92
|
+
const component = new TreeSelectorComponent(data.tree, data.currentLeafId, terminalHeight, (entryId) => {
|
|
93
|
+
send({ type: 'navigate_tree', targetId: entryId });
|
|
94
|
+
close();
|
|
95
|
+
}, () => close());
|
|
96
|
+
return { component, focus: component };
|
|
97
|
+
}
|
|
98
|
+
/** `/fork` — the real prior-user-message selector (pure data, from
|
|
99
|
+
* `get_tree.forkPoints`). Select → `fork`. The outer component has no
|
|
100
|
+
* `handleInput`; the inner `getMessageList()` does (focus target). */
|
|
101
|
+
export function buildForkPicker(data, send, close) {
|
|
102
|
+
const component = new UserMessageSelectorComponent(data.forkPoints, (entryId) => {
|
|
103
|
+
send({ type: 'fork', entryId });
|
|
104
|
+
close();
|
|
105
|
+
}, () => close());
|
|
106
|
+
return { component, focus: component.getMessageList() };
|
|
107
|
+
}
|
|
108
|
+
/** `/settings` — the real settings menu (the wire `settings` IS a SettingsConfig
|
|
109
|
+
* superset). Only the contract-enumerated mutations are wired: thinking level →
|
|
110
|
+
* `set_thinking_level`, auto-compact → `set_auto_compaction`. Every other toggle
|
|
111
|
+
* has no command frame (out of scope), so its callback notifies rather than
|
|
112
|
+
* silently lying that the change took. The theme submenu is viewer-local (CTO).
|
|
113
|
+
* The outer component has no `handleInput`; `getSettingsList()` is the focus
|
|
114
|
+
* target. */
|
|
115
|
+
export function buildSettingsPicker(data, send, close, notify) {
|
|
116
|
+
const out = () => notify('Only thinking level + auto-compaction are adjustable over view.sock');
|
|
117
|
+
const callbacks = {
|
|
118
|
+
onThinkingLevelChange: (level) => send({ type: 'set_thinking_level', level }),
|
|
119
|
+
onAutoCompactChange: (enabled) => send({ type: 'set_auto_compaction', enabled }),
|
|
120
|
+
onCancel: () => close(),
|
|
121
|
+
onShowImagesChange: out,
|
|
122
|
+
onImageWidthCellsChange: out,
|
|
123
|
+
onAutoResizeImagesChange: out,
|
|
124
|
+
onBlockImagesChange: out,
|
|
125
|
+
onEnableSkillCommandsChange: out,
|
|
126
|
+
onSteeringModeChange: out,
|
|
127
|
+
onFollowUpModeChange: out,
|
|
128
|
+
onTransportChange: out,
|
|
129
|
+
onHttpIdleTimeoutMsChange: out,
|
|
130
|
+
onThemeChange: out,
|
|
131
|
+
onHideThinkingBlockChange: out,
|
|
132
|
+
onCollapseChangelogChange: out,
|
|
133
|
+
onEnableInstallTelemetryChange: out,
|
|
134
|
+
onDoubleEscapeActionChange: out,
|
|
135
|
+
onTreeFilterModeChange: out,
|
|
136
|
+
onShowHardwareCursorChange: out,
|
|
137
|
+
onEditorPaddingXChange: out,
|
|
138
|
+
onAutocompleteMaxVisibleChange: out,
|
|
139
|
+
onQuietStartupChange: out,
|
|
140
|
+
onDefaultProjectTrustChange: out,
|
|
141
|
+
onClearOnShrinkChange: out,
|
|
142
|
+
onShowTerminalProgressChange: out,
|
|
143
|
+
onWarningsChange: out,
|
|
144
|
+
};
|
|
145
|
+
const component = new SettingsSelectorComponent(data.settings, callbacks);
|
|
146
|
+
return { component, focus: component.getSettingsList() };
|
|
147
|
+
}
|
|
148
|
+
/** `/scoped-models` — READ-ONLY via a pi-tui SelectList. CONTRACT GAP: pi (through
|
|
149
|
+
* 0.79.1) does not re-export `ScopedModelsSelectorComponent`, and the contract
|
|
150
|
+
* exposes no command frame for its enable/disable/persist mutations, so a
|
|
151
|
+
* faithful editable picker is not buildable in scope. This shows the registry
|
|
152
|
+
* with the enabled set marked; select notifies that toggling is unsupported. */
|
|
153
|
+
export function buildScopedModelsPicker(data, close, notify) {
|
|
154
|
+
const enabled = data.enabledModelIds;
|
|
155
|
+
const isEnabled = (m) => {
|
|
156
|
+
if (enabled === null)
|
|
157
|
+
return true; // null = no scoping, every model cycles
|
|
158
|
+
const ref = `${m.provider}/${m.id}`;
|
|
159
|
+
return enabled.includes(ref) || enabled.includes(m.id) || enabled.includes('*');
|
|
160
|
+
};
|
|
161
|
+
const items = data.allModels.map((m) => ({
|
|
162
|
+
value: `${m.provider}/${m.id}`,
|
|
163
|
+
label: `${isEnabled(m) ? '✓' : ' '} ${m.provider}/${m.id}`,
|
|
164
|
+
description: m.name,
|
|
165
|
+
}));
|
|
166
|
+
const theme = getSelectListTheme();
|
|
167
|
+
const list = new SelectList(items, 12, theme);
|
|
168
|
+
list.onSelect = () => notify("Enabling/disabling scoped models isn't supported over view.sock yet");
|
|
169
|
+
list.onCancel = () => close();
|
|
170
|
+
return { component: list, focus: list };
|
|
171
|
+
}
|
|
@@ -12,7 +12,49 @@ export interface SlashContext {
|
|
|
12
12
|
state?: BrokerSnapshot['state'];
|
|
13
13
|
/** cwd for the default `/export` path; defaults to `process.cwd()`. */
|
|
14
14
|
cwd?: string;
|
|
15
|
+
/** The canvas node this viewer is attached to — used by `/promote` (passed
|
|
16
|
+
* as `--node`). Falls back to `CRTR_NODE_ID` when absent; if neither is set
|
|
17
|
+
* `/promote` notifies and no-ops. (Unit Q wires this from `runAttach`.) */
|
|
18
|
+
nodeId?: string;
|
|
19
|
+
/** Toggle the in-process GRAPH overlay (the local subscription-tree view).
|
|
20
|
+
* `/graph` is inherently an in-viewer overlay, not a tmux popup, so this unit
|
|
21
|
+
* registers `/graph` as a stub that calls this hook. Unit C/Q owns the overlay
|
|
22
|
+
* itself and populates this; when absent `/graph` notifies it isn't wired yet. */
|
|
23
|
+
onGraph?: () => void;
|
|
24
|
+
/** Detach this viewer (`/quit`). A viewer quit DETACHES — it tears down the
|
|
25
|
+
* local TUI/socket and leaves the shared engine running. Wired by attach-cmd to
|
|
26
|
+
* `teardown('detach')`; without it `/quit` cannot actually exit (just sending
|
|
27
|
+
* `bye` makes the broker drop the socket, which the viewer treats as a dropped
|
|
28
|
+
* connection and reconnects). */
|
|
29
|
+
onQuit?: () => void;
|
|
30
|
+
/** Copy the last assistant message to the clipboard (`/copy`). Wired by
|
|
31
|
+
* attach-cmd (it owns the ChatView that holds the text + the clipboard writer). */
|
|
32
|
+
onCopy?: () => void;
|
|
33
|
+
/** OPTIONAL native-picker openers — the InputController fills these from its
|
|
34
|
+
* read-op request channel. When absent (channel unwired) the value-picker
|
|
35
|
+
* builtins fall back to the text-arg notice. */
|
|
36
|
+
openModelPicker?: () => void;
|
|
37
|
+
openSessionPicker?: () => void;
|
|
38
|
+
openForkPicker?: () => void;
|
|
39
|
+
openTreePicker?: () => void;
|
|
40
|
+
openSettingsPicker?: () => void;
|
|
41
|
+
openScopedModelsPicker?: () => void;
|
|
42
|
+
/** Open the native /login provider selector. */
|
|
43
|
+
openLoginPicker?: () => void;
|
|
44
|
+
/** Open the native /logout provider selector. */
|
|
45
|
+
openLogoutPicker?: () => void;
|
|
15
46
|
}
|
|
47
|
+
/** Canvas slash-commands — reimplemented NATIVE in the viewer (the canvas chrome
|
|
48
|
+
* pi-extensions cannot load here; see findings-chrome-parity.md Feature 3). Each
|
|
49
|
+
* ports the action of its extension: `/promote` ← canvas-commands.ts, `/resume-node`
|
|
50
|
+
* ← canvas-resume.ts, `/view` ← canvas-view.ts, `/graph` ← canvas-nav.ts. Three of
|
|
51
|
+
* them shell a `crtr` subcommand via `tmux display-popup` (the viewer runs inside a
|
|
52
|
+
* tmux pane, so this works exactly as in the extensions); `/graph` is a stub for the
|
|
53
|
+
* overlay (Unit C/Q). Exported so Unit Q can fold these into the autocomplete list. */
|
|
54
|
+
export declare const CANVAS_SLASH_COMMANDS: ReadonlyArray<{
|
|
55
|
+
name: string;
|
|
56
|
+
description: string;
|
|
57
|
+
}>;
|
|
16
58
|
/** True if `text` is a leading-slash command (vs. a normal prompt). */
|
|
17
59
|
export declare function isSlashCommand(text: string): boolean;
|
|
18
60
|
/**
|
|
@@ -24,7 +66,8 @@ export declare function isSlashCommand(text: string): boolean;
|
|
|
24
66
|
export declare function dispatchSlashCommand(text: string, ctx: SlashContext): boolean;
|
|
25
67
|
/** Build slash-command autocomplete entries from the merged command list (the
|
|
26
68
|
* broker's `get_commands` result, which T7 may inject) — defaults to the
|
|
27
|
-
* vendored builtins.
|
|
69
|
+
* vendored builtins. The native canvas commands are always appended. Shaped for
|
|
70
|
+
* pi-tui's `CombinedAutocompleteProvider`. */
|
|
28
71
|
export declare function slashCommandList(commands?: ReadonlyArray<{
|
|
29
72
|
name: string;
|
|
30
73
|
description?: string;
|
|
@@ -7,24 +7,39 @@
|
|
|
7
7
|
// doesn't recognize) fall through to be sent as a `prompt` for the engine's
|
|
8
8
|
// extension runner to handle.
|
|
9
9
|
//
|
|
10
|
-
// SELECTORS —
|
|
11
|
-
// `/
|
|
12
|
-
//
|
|
13
|
-
// socket
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// notice with the arg form. (This protocol gap is reported up to the parent.)
|
|
10
|
+
// SELECTORS — native interactive pickers. `/model`, `/resume`, `/fork`, `/tree`,
|
|
11
|
+
// `/settings`, `/scoped-models` open pi's real `*SelectorComponent`s as overlays,
|
|
12
|
+
// fed by the operator-view read-op `data` frames (broker-protocol) over the
|
|
13
|
+
// socket. With no arg they open the picker (via the `openXPicker` SlashContext
|
|
14
|
+
// callbacks the InputController exposes, fed by its read-op request channel —
|
|
15
|
+
// which attach-cmd MUST wire via `onRequest: (f) => socket.request(f)`). With an
|
|
16
|
+
// arg the same resolved value is sent directly (`/model <provider/id>`, `/resume
|
|
17
|
+
// <path>`, `/fork <entryId>`, `/tree <entryId>`) without the UI. If the request
|
|
18
|
+
// channel is unwired the picker degrades to the arg-form notice. (Verified
|
|
19
|
+
// against pi 0.79.1 — `ScopedModelsSelectorComponent` is the one selector pi does
|
|
20
|
+
// not re-export, so /scoped-models renders read-only via a SelectList.)
|
|
22
21
|
//
|
|
23
22
|
// SCOPED OUT (review m2 — no engine method): `/login`, `/logout`, `/share`,
|
|
24
23
|
// `/trust` — a brief "not supported in attach" notice, never a frame.
|
|
24
|
+
import { execFile } from 'node:child_process';
|
|
25
25
|
import { BUILTIN_SLASH_COMMANDS } from '../../core/runtime/pi-vendored.js';
|
|
26
|
+
import { surfaceTmuxStyleArgs } from '../../core/runtime/surface-bg.js';
|
|
27
|
+
/** Canvas slash-commands — reimplemented NATIVE in the viewer (the canvas chrome
|
|
28
|
+
* pi-extensions cannot load here; see findings-chrome-parity.md Feature 3). Each
|
|
29
|
+
* ports the action of its extension: `/promote` ← canvas-commands.ts, `/resume-node`
|
|
30
|
+
* ← canvas-resume.ts, `/view` ← canvas-view.ts, `/graph` ← canvas-nav.ts. Three of
|
|
31
|
+
* them shell a `crtr` subcommand via `tmux display-popup` (the viewer runs inside a
|
|
32
|
+
* tmux pane, so this works exactly as in the extensions); `/graph` is a stub for the
|
|
33
|
+
* overlay (Unit C/Q). Exported so Unit Q can fold these into the autocomplete list. */
|
|
34
|
+
export const CANVAS_SLASH_COMMANDS = [
|
|
35
|
+
{ name: 'graph', description: 'Toggle the canvas GRAPH view (your local subscription tree)' },
|
|
36
|
+
{ name: 'promote', description: 'Promote this node to an orchestrator — /promote, or /promote <kind> to specialize' },
|
|
37
|
+
{ name: 'resume-node', description: 'Open the canvas navigator (search/scope/sort/tree) and resume the chosen node' },
|
|
38
|
+
{ name: 'view', description: 'Open a view in a popup — bare for the picker, or /view <name> to open that view directly' },
|
|
39
|
+
];
|
|
40
|
+
const CANVAS_NAMES = new Set(CANVAS_SLASH_COMMANDS.map((c) => c.name));
|
|
26
41
|
/** Builtins with no Phase-4 engine method — scoped out (review m2). */
|
|
27
|
-
const SCOPED_OUT = new Set(['
|
|
42
|
+
const SCOPED_OUT = new Set(['trust']);
|
|
28
43
|
/** Valid `/settings thinking` levels — the `SetThinkingLevelFrame['level']`
|
|
29
44
|
* union (= AgentSession['thinkingLevel'], pi-agent-core ThinkingLevel),
|
|
30
45
|
* mirrored as a runtime set so an invalid arg is rejected with the options
|
|
@@ -60,6 +75,10 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
60
75
|
ctx.notify(`/${name} is not supported in attach (Phase 4)`);
|
|
61
76
|
return true;
|
|
62
77
|
}
|
|
78
|
+
// Canvas commands (reimplemented native in the viewer) — handled before the
|
|
79
|
+
// builtin gate since they are neither pi builtins nor engine-extension commands.
|
|
80
|
+
if (CANVAS_NAMES.has(name))
|
|
81
|
+
return dispatchCanvasCommand(name, arg, ctx);
|
|
63
82
|
// Unknown to us AND not a builtin → let the engine's extension runner have it.
|
|
64
83
|
if (!BUILTIN_NAMES.has(name))
|
|
65
84
|
return false;
|
|
@@ -76,6 +95,13 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
76
95
|
return true;
|
|
77
96
|
case 'quit':
|
|
78
97
|
// A viewer "quit" DETACHES — the shared engine keeps running (one-writer).
|
|
98
|
+
// onQuit tears the viewer down locally (which sends `bye` itself). Falling
|
|
99
|
+
// back to a bare `bye` would just make the broker close the socket, which
|
|
100
|
+
// the viewer reconnects to — so the detach MUST go through onQuit.
|
|
101
|
+
if (ctx.onQuit) {
|
|
102
|
+
ctx.onQuit();
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
79
105
|
ctx.send({ type: 'bye' });
|
|
80
106
|
ctx.notify('Detaching — the engine keeps running');
|
|
81
107
|
return true;
|
|
@@ -97,27 +123,34 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
97
123
|
return usage(ctx, '/import <path-to-session.jsonl>');
|
|
98
124
|
ctx.send({ type: 'switch_session', path: arg });
|
|
99
125
|
return true;
|
|
100
|
-
// --- value-picker builtins:
|
|
126
|
+
// --- value-picker builtins: native picker (no arg) OR direct value (arg) ---
|
|
101
127
|
case 'model':
|
|
102
128
|
if (!arg)
|
|
103
|
-
return
|
|
129
|
+
return openOrHint(ctx.openModelPicker, ctx, 'model', '/model <provider/id>');
|
|
104
130
|
ctx.send({ type: 'set_model', model: arg });
|
|
105
131
|
return true;
|
|
106
132
|
case 'resume':
|
|
107
133
|
if (!arg)
|
|
108
|
-
return
|
|
134
|
+
return openOrHint(ctx.openSessionPicker, ctx, 'session', '/resume <session-path>');
|
|
109
135
|
ctx.send({ type: 'switch_session', path: arg });
|
|
110
136
|
return true;
|
|
111
137
|
case 'fork':
|
|
112
138
|
if (!arg)
|
|
113
|
-
return
|
|
139
|
+
return openOrHint(ctx.openForkPicker, ctx, 'fork point', '/fork <entryId>');
|
|
114
140
|
ctx.send({ type: 'fork', entryId: arg });
|
|
115
141
|
return true;
|
|
116
142
|
case 'tree':
|
|
117
143
|
if (!arg)
|
|
118
|
-
return
|
|
144
|
+
return openOrHint(ctx.openTreePicker, ctx, 'tree', '/tree <entryId>');
|
|
119
145
|
ctx.send({ type: 'navigate_tree', targetId: arg });
|
|
120
146
|
return true;
|
|
147
|
+
case 'scoped-models':
|
|
148
|
+
if (ctx.openScopedModelsPicker) {
|
|
149
|
+
ctx.openScopedModelsPicker();
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
ctx.notify('The scoped-models view needs the read-op channel, which this viewer has not wired');
|
|
153
|
+
return true;
|
|
121
154
|
// --- settings (sub-command form; interactive menu needs read ops) ------
|
|
122
155
|
case 'settings':
|
|
123
156
|
return handleSettings(arg, ctx);
|
|
@@ -125,12 +158,44 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
125
158
|
case 'session':
|
|
126
159
|
ctx.notify(describeSession(ctx.state));
|
|
127
160
|
return true;
|
|
128
|
-
// --- recognized builtins with no Phase-4 wire path --------------------
|
|
129
161
|
case 'copy':
|
|
162
|
+
// Copy the last assistant message to the clipboard — viewer-owned (the text
|
|
163
|
+
// is in the ChatView; no engine round-trip). attach-cmd does the actual copy.
|
|
164
|
+
if (ctx.onCopy) {
|
|
165
|
+
ctx.onCopy();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
ctx.notify('/copy is not available in this viewer');
|
|
169
|
+
return true;
|
|
170
|
+
// --- auth commands — viewer-local OAuth/API-key flows ------------------
|
|
171
|
+
case 'login':
|
|
172
|
+
if (ctx.openLoginPicker) {
|
|
173
|
+
ctx.openLoginPicker();
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
ctx.notify('/login is not available in this viewer');
|
|
177
|
+
return true;
|
|
178
|
+
case 'logout':
|
|
179
|
+
if (ctx.openLogoutPicker) {
|
|
180
|
+
ctx.openLogoutPicker();
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
ctx.notify('/logout is not available in this viewer');
|
|
184
|
+
return true;
|
|
185
|
+
// --- share / clone / changelog -----------------------------------------
|
|
186
|
+
case 'share':
|
|
187
|
+
ctx.send({ type: 'share' });
|
|
188
|
+
ctx.notify('Sharing session…');
|
|
189
|
+
return true;
|
|
190
|
+
case 'clone':
|
|
191
|
+
ctx.send({ type: 'clone' });
|
|
192
|
+
ctx.notify('Cloning session…');
|
|
193
|
+
return true;
|
|
130
194
|
case 'changelog':
|
|
195
|
+
ctx.notify('/changelog: see https://github.com/earendil-works/pi for release notes (no bundled changelog in this build)');
|
|
196
|
+
return true;
|
|
197
|
+
// --- recognized builtins with no Phase-4 wire path --------------------
|
|
131
198
|
case 'hotkeys':
|
|
132
|
-
case 'scoped-models':
|
|
133
|
-
case 'clone':
|
|
134
199
|
ctx.notify(`/${name} is not available in attach (Phase 4)`);
|
|
135
200
|
return true;
|
|
136
201
|
default:
|
|
@@ -140,15 +205,153 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
140
205
|
return true;
|
|
141
206
|
}
|
|
142
207
|
}
|
|
208
|
+
/** Append the native canvas commands to a command list, skipping any already
|
|
209
|
+
* present (so they survive whatever list Unit Q feeds in — builtins, or the
|
|
210
|
+
* broker's `get_commands` result). The four canvas commands are viewer-owned,
|
|
211
|
+
* so they are always surfaced in autocomplete regardless of the source list. */
|
|
212
|
+
function withCanvasCommands(commands) {
|
|
213
|
+
const seen = new Set(commands.map((c) => c.name));
|
|
214
|
+
return [...commands, ...CANVAS_SLASH_COMMANDS.filter((c) => !seen.has(c.name))];
|
|
215
|
+
}
|
|
143
216
|
/** Build slash-command autocomplete entries from the merged command list (the
|
|
144
217
|
* broker's `get_commands` result, which T7 may inject) — defaults to the
|
|
145
|
-
* vendored builtins.
|
|
218
|
+
* vendored builtins. The native canvas commands are always appended. Shaped for
|
|
219
|
+
* pi-tui's `CombinedAutocompleteProvider`. */
|
|
146
220
|
export function slashCommandList(commands = BUILTIN_SLASH_COMMANDS) {
|
|
147
|
-
return commands.map((c) => ({ name: c.name, description: c.description }));
|
|
221
|
+
return withCanvasCommands(commands).map((c) => ({ name: c.name, description: c.description }));
|
|
148
222
|
}
|
|
149
223
|
/** Same list as flat autocomplete items (value/label/description). */
|
|
150
224
|
export function commandAutocompleteItems(commands = BUILTIN_SLASH_COMMANDS) {
|
|
151
|
-
return commands.map((c) => ({ value: `/${c.name}`, label: `/${c.name}`, description: c.description }));
|
|
225
|
+
return withCanvasCommands(commands).map((c) => ({ value: `/${c.name}`, label: `/${c.name}`, description: c.description }));
|
|
226
|
+
}
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
// Canvas commands — native reimplementations of the canvas chrome extensions.
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
/** Dispatch one of the four native canvas commands. Always returns `true` (the
|
|
231
|
+
* command is viewer-owned — never forwarded to the engine as a prompt). */
|
|
232
|
+
function dispatchCanvasCommand(name, arg, ctx) {
|
|
233
|
+
switch (name) {
|
|
234
|
+
case 'graph':
|
|
235
|
+
// `/graph` is inherently the in-process GRAPH overlay (canvas-nav.ts
|
|
236
|
+
// `toggleGraph`), NOT a tmux popup. The overlay is Unit C/Q's surface; this
|
|
237
|
+
// unit registers the command + autocomplete entry and triggers the overlay
|
|
238
|
+
// via the `onGraph` hook the integration unit wires in.
|
|
239
|
+
if (ctx.onGraph)
|
|
240
|
+
ctx.onGraph();
|
|
241
|
+
else
|
|
242
|
+
ctx.notify('The GRAPH overlay is not wired into the viewer yet');
|
|
243
|
+
return true;
|
|
244
|
+
case 'promote':
|
|
245
|
+
promoteNode(arg, ctx);
|
|
246
|
+
return true;
|
|
247
|
+
case 'resume-node':
|
|
248
|
+
// ← canvas-resume.ts: open the full-screen canvas navigator as a popup; on
|
|
249
|
+
// Enter it focuses the chosen node back into THIS pane via `crtr node focus`.
|
|
250
|
+
return resumeNode(ctx);
|
|
251
|
+
case 'view':
|
|
252
|
+
// ← canvas-view.ts: bare `/view` opens the picker, `/view <name>` opens that
|
|
253
|
+
// view directly — each as a self-contained popup (no node-focus, no return-pane).
|
|
254
|
+
return openView(arg, ctx);
|
|
255
|
+
default:
|
|
256
|
+
ctx.notify(`/${name} is not available in attach`);
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/** `/promote [kind]` ← canvas-commands.ts. Shells `crtr node promote --json` for
|
|
261
|
+
* this node (out-of-process), notifies the result, then triggers a turn so the
|
|
262
|
+
* persona injector steers in the new-role guidance at the turn boundary — exactly
|
|
263
|
+
* as the node would by running the command itself. */
|
|
264
|
+
function promoteNode(arg, ctx) {
|
|
265
|
+
const nodeId = (ctx.nodeId ?? process.env['CRTR_NODE_ID'] ?? '').trim();
|
|
266
|
+
if (nodeId === '') {
|
|
267
|
+
ctx.notify('/promote: no node to promote (viewer has no node id)');
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const kind = arg.trim().toLowerCase();
|
|
271
|
+
ctx.notify(kind ? `Promoting → ${kind}…` : 'Promoting…');
|
|
272
|
+
const argv = ['node', 'promote', '--node', nodeId, '--json'];
|
|
273
|
+
if (kind !== '')
|
|
274
|
+
argv.push('--kind', kind);
|
|
275
|
+
execFile('crtr', argv, { timeout: 15_000, maxBuffer: 4 * 1024 * 1024 }, (err, stdout) => {
|
|
276
|
+
// On a non-zero exit crtr still prints the structured error to stdout, so
|
|
277
|
+
// prefer its `message` over the raw throw.
|
|
278
|
+
let result = null;
|
|
279
|
+
let errMsg = null;
|
|
280
|
+
try {
|
|
281
|
+
result = JSON.parse(stdout);
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
result = null;
|
|
285
|
+
}
|
|
286
|
+
if (err && result === null) {
|
|
287
|
+
const out = typeof err.stdout === 'string' ? err.stdout : '';
|
|
288
|
+
try {
|
|
289
|
+
const payload = JSON.parse(out);
|
|
290
|
+
errMsg = typeof payload.message === 'string' ? payload.message : null;
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
errMsg = null;
|
|
294
|
+
}
|
|
295
|
+
if (errMsg === null)
|
|
296
|
+
errMsg = err.message;
|
|
297
|
+
}
|
|
298
|
+
if (result === null) {
|
|
299
|
+
ctx.notify(`promote failed: ${errMsg ?? 'unknown error'}`);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const rmPath = (result.roadmap_path ?? '').trim();
|
|
303
|
+
ctx.notify(`Promoted to ${result.kind ?? 'orchestrator'} orchestrator — authoring roadmap${rmPath !== '' ? ` (${rmPath})` : ''}.`);
|
|
304
|
+
// Trigger a turn so the persona injector (canvas-stophook turn_end) fires and
|
|
305
|
+
// delivers the new-role guidance — the viewer's only turn-trigger is `prompt`.
|
|
306
|
+
ctx.send({
|
|
307
|
+
type: 'prompt',
|
|
308
|
+
text: 'You have just been promoted to an orchestrator. Your new-role guidance is arriving — read it, author your roadmap, and start delegating.',
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
/** `/resume-node` ← canvas-resume.ts. Opens `crtr canvas browse` as a tmux popup,
|
|
313
|
+
* scoped to this node's cwd; on Enter it focuses the chosen node back into this
|
|
314
|
+
* pane via `crtr node focus --pane` (which `canvas browse` shells). */
|
|
315
|
+
function resumeNode(ctx) {
|
|
316
|
+
const origPane = process.env['TMUX_PANE'];
|
|
317
|
+
if (process.env['TMUX'] === undefined || origPane === undefined || origPane === '') {
|
|
318
|
+
ctx.notify('/resume-node needs tmux');
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
const cwd = shellQuote(ctx.cwd ?? process.cwd());
|
|
322
|
+
const cmd = `crtr canvas browse --return-pane ${origPane} --cwd ${cwd}`;
|
|
323
|
+
popup(cmd);
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
/** `/view [name]` ← canvas-view.ts. Bare opens the picker (`crtr view pick`);
|
|
327
|
+
* `/view <name>` opens that view directly (`crtr view run <name>`). Self-contained
|
|
328
|
+
* popup — no node-focus, no return-pane. */
|
|
329
|
+
function openView(arg, ctx) {
|
|
330
|
+
if (process.env['TMUX'] === undefined) {
|
|
331
|
+
ctx.notify('/view needs tmux');
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
const name = arg.trim();
|
|
335
|
+
const cmd = name === '' ? 'crtr view pick' : `crtr view run ${shellQuote(name)}`;
|
|
336
|
+
popup(cmd);
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
/** Open `cmd` as a fire-and-forget tmux display-popup (same geometry as the canvas
|
|
340
|
+
* extensions). tmux runs the trailing string through `sh -c`; the popup owns its
|
|
341
|
+
* screen and closes itself when the command exits, dropping back to this pane. */
|
|
342
|
+
function popup(cmd) {
|
|
343
|
+
try {
|
|
344
|
+
execFile('tmux', ['display-popup', '-E', '-w', '90%', '-h', '85%', ...surfaceTmuxStyleArgs(), cmd], () => {
|
|
345
|
+
/* best-effort: popup is self-contained */
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
/* best-effort */
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/** Single-quote a string for safe interpolation into a `sh -c` command line. */
|
|
353
|
+
function shellQuote(s) {
|
|
354
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
152
355
|
}
|
|
153
356
|
// ---------------------------------------------------------------------------
|
|
154
357
|
function usage(ctx, form) {
|
|
@@ -159,6 +362,16 @@ function pickerHint(ctx, what, form) {
|
|
|
159
362
|
ctx.notify(`Interactive ${what} picker isn't available over view.sock yet — use ${form}`);
|
|
160
363
|
return true;
|
|
161
364
|
}
|
|
365
|
+
/** Open a native picker if its opener is wired; otherwise fall back to the
|
|
366
|
+
* text-arg hint. Keeps every value-picker builtin working even when the
|
|
367
|
+
* read-op request channel is absent. */
|
|
368
|
+
function openOrHint(open, ctx, what, form) {
|
|
369
|
+
if (open) {
|
|
370
|
+
open();
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
return pickerHint(ctx, what, form);
|
|
374
|
+
}
|
|
162
375
|
function handleSettings(arg, ctx) {
|
|
163
376
|
const [sub, ...rest] = arg.split(/\s+/);
|
|
164
377
|
const value = rest.join(' ').trim();
|
|
@@ -178,12 +391,22 @@ function handleSettings(arg, ctx) {
|
|
|
178
391
|
case 'auto-compaction':
|
|
179
392
|
ctx.send({ type: 'set_auto_compaction', enabled: parseBool(value) });
|
|
180
393
|
return true;
|
|
394
|
+
case '':
|
|
395
|
+
// Bare `/settings` opens the native settings menu (falls back to the
|
|
396
|
+
// sub-command summary when the picker channel is unwired).
|
|
397
|
+
if (ctx.openSettingsPicker) {
|
|
398
|
+
ctx.openSettingsPicker();
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
return settingsSummary(ctx);
|
|
181
402
|
default:
|
|
182
|
-
ctx
|
|
183
|
-
'(the interactive settings menu needs data not exposed over view.sock in Phase 4)');
|
|
184
|
-
return true;
|
|
403
|
+
return settingsSummary(ctx);
|
|
185
404
|
}
|
|
186
405
|
}
|
|
406
|
+
function settingsSummary(ctx) {
|
|
407
|
+
ctx.notify('Settings: /settings thinking <level> | auto-retry on|off | auto-compaction on|off');
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
187
410
|
function parseBool(value) {
|
|
188
411
|
return /^(on|true|yes|1|enabled?)$/i.test(value.trim());
|
|
189
412
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { CustomEditor } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
/** Thinking levels pi cycles through (shift+tab), lowest → highest budget. */
|
|
3
|
+
export type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
4
|
+
/** The default (thinking `off`) title chip: reverse video + a space of padding
|
|
5
|
+
* each side, so the name reads as a label sitting on the border rule. Used as
|
|
6
|
+
* the fallback when no thinking color applies. */
|
|
7
|
+
export declare const defaultTitleStyle: (s: string) => string;
|
|
8
|
+
/** Resolve the editor border colorizer for a thinking level. Unknown / `off` →
|
|
9
|
+
* the supplied `fallback` (the theme's default border color). */
|
|
10
|
+
export declare function thinkingBorderColor(level: string | undefined, fallback: (s: string) => string): (s: string) => string;
|
|
11
|
+
/** Resolve the title-chip styler for a thinking level: the level's color as the
|
|
12
|
+
* chip BACKGROUND with bold white text (a space of padding each side), so the
|
|
13
|
+
* session name reads as a solid label in the same hue as the border. Unknown /
|
|
14
|
+
* `off` → the supplied `fallback` (the reverse-video default chip). */
|
|
15
|
+
export declare function thinkingTitleStyle(level: string | undefined, fallback: (s: string) => string): (s: string) => string;
|
|
16
|
+
/** Compose the replacement top-border line: solid title chip + border rule +
|
|
17
|
+
* info chip, never wider than `width` (pi-tui hard-crashes on an over-wide
|
|
18
|
+
* line). The info chip yields entirely when the chip leaves it almost no room,
|
|
19
|
+
* and truncates when it only partially fits. Exported pure for the overflow
|
|
20
|
+
* regression test. */
|
|
21
|
+
export declare function composeTopBorder(width: number, title: string, info: string, titleStyle: (s: string) => string, borderColor: (s: string) => string): string;
|
|
22
|
+
export declare class TitledEditor extends CustomEditor {
|
|
23
|
+
/** Session-name chip painted into the LEFT of the top border. Empty → plain. */
|
|
24
|
+
title: string;
|
|
25
|
+
/** Pre-styled context string painted into the RIGHT of the top border (cwd /
|
|
26
|
+
* branch / git status). Already colorized by the caller; empty → omitted. */
|
|
27
|
+
info: string;
|
|
28
|
+
/** Paint the chip solid so the name reads as a label sitting on the border
|
|
29
|
+
* rule. Defaults to the reverse-video chip; attach-cmd swaps in a
|
|
30
|
+
* thinking-colored background (bold white text) on each state update. */
|
|
31
|
+
titleStyle: (s: string) => string;
|
|
32
|
+
render(width: number): string[];
|
|
33
|
+
}
|