@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
|
@@ -20,12 +20,28 @@ export { detectColorCaps };
|
|
|
20
20
|
// Fixed chrome heights, shared with app.ts so its viewport math never drifts
|
|
21
21
|
// from what renderFrame actually draws.
|
|
22
22
|
// header = title + tab bar + status line + separator (+ search input when searching)
|
|
23
|
-
// preview = separator + meta line +
|
|
24
|
-
export const
|
|
23
|
+
// preview = separator + meta line + PROMPT_LINES (spawn/match) + REPLY_LINES (last reply)
|
|
24
|
+
export const PROMPT_LINES = 3;
|
|
25
|
+
export const REPLY_LINES = 3;
|
|
26
|
+
export const PREVIEW_BODY = PROMPT_LINES + REPLY_LINES;
|
|
25
27
|
export const PREVIEW_HEIGHT = PREVIEW_BODY + 2;
|
|
28
|
+
// header = title + tab bar + status line + column header + separator (+ search input)
|
|
26
29
|
export function headerHeight(search) {
|
|
27
|
-
return
|
|
30
|
+
return 5 + (search ? 1 : 0);
|
|
28
31
|
}
|
|
32
|
+
// ── Row table layout ──────────────────────────────────────────────────────────
|
|
33
|
+
// A row is three zones: a fixed-width STATUS rail (glyph + word) on the left, the
|
|
34
|
+
// flexible tree-indented NAME in the middle, and a flush-right METADATA cluster
|
|
35
|
+
// (kind/mode · ctx · age · project) whose fixed-width segments align into clean
|
|
36
|
+
// vertical columns down the right edge. Wide terminals widen the name; narrow ones
|
|
37
|
+
// clip the name (and drop the cluster) first. The status WORD — not a lone glyph —
|
|
38
|
+
// is the legible, NO_COLOR-safe state signal.
|
|
39
|
+
const STATUS_W = 10; // glyph + ' ' + longest word ('active'/'cancel') + trailing gap
|
|
40
|
+
const KM_W = 22; // kind/mode column
|
|
41
|
+
const CTX_W = 6; // ctx tokens column
|
|
42
|
+
const AGE_W = 5; // age column
|
|
43
|
+
const PROJ_W = 16; // project (~basename) column, only across All dirs
|
|
44
|
+
const COL_GAP = ' ';
|
|
29
45
|
// ── ANSI ────────────────────────────────────────────────────────────────────
|
|
30
46
|
const ESC = '\x1b[';
|
|
31
47
|
const RESET = `${ESC}0m`;
|
|
@@ -40,6 +56,7 @@ const FG_RED = '31';
|
|
|
40
56
|
const FG_CYAN = '36';
|
|
41
57
|
const FG_GRAY = '90'; // bright-black
|
|
42
58
|
const FG_BRIGHT_YELLOW = '93';
|
|
59
|
+
const FG_BRIGHT_GREEN = '92'; // streaming pulse (brighter than the active-status green)
|
|
43
60
|
const FG_BRIGHT_CYAN = '96'; // query-match highlight (ties to the cyan search accent)
|
|
44
61
|
const STATUS_GLYPH = {
|
|
45
62
|
active: '●',
|
|
@@ -165,8 +182,67 @@ function nameSpans(name, query, style) {
|
|
|
165
182
|
flush();
|
|
166
183
|
return out;
|
|
167
184
|
}
|
|
168
|
-
/**
|
|
169
|
-
|
|
185
|
+
/** Visible (cell) width of a span group — ANSI-free, surrogate-safe. */
|
|
186
|
+
function spansWidth(spans) {
|
|
187
|
+
let n = 0;
|
|
188
|
+
for (const s of spans)
|
|
189
|
+
n += [...s.text].length;
|
|
190
|
+
return n;
|
|
191
|
+
}
|
|
192
|
+
/** The status rail: a status WORD next to its glyph, both in the status hue, padded
|
|
193
|
+
* to STATUS_W so the rail forms a clean left column. `live` (bright green) when the
|
|
194
|
+
* node is genuinely mid-turn; otherwise the lifecycle word. The word is what makes
|
|
195
|
+
* state legible at a glance and survives NO_COLOR (the glyph is the second cue). */
|
|
196
|
+
function statusRail(r) {
|
|
197
|
+
const streaming = r.streaming === true;
|
|
198
|
+
const word = streaming
|
|
199
|
+
? 'live'
|
|
200
|
+
: ({ active: 'active', idle: 'idle', done: 'done', dead: 'dead', canceled: 'cancel' }[r.status] ?? r.status);
|
|
201
|
+
const fg = streaming ? FG_BRIGHT_GREEN : STATUS_COLOR[r.status];
|
|
202
|
+
const glyph = streaming ? '⟳' : (STATUS_GLYPH[r.status] ?? '?');
|
|
203
|
+
const text = `${glyph} ${word}`;
|
|
204
|
+
const pad = Math.max(1, STATUS_W - [...text].length);
|
|
205
|
+
return [
|
|
206
|
+
{ text, style: { fg, bold: streaming } },
|
|
207
|
+
{ text: ' '.repeat(pad) },
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
/** The flush-right metadata cluster as fixed-width segments, so kind/mode · ctx ·
|
|
211
|
+
* age · project align into vertical columns down the right edge. `showCwd` adds the
|
|
212
|
+
* project column (only meaningful across All dirs). */
|
|
213
|
+
function metaCluster(r, now, showCwd) {
|
|
214
|
+
const ctxFg = ctxColorCode(r.ctx_tokens);
|
|
215
|
+
const out = [
|
|
216
|
+
{ text: clipPad(`${r.kind}/${r.mode}`, KM_W), style: { fg: FG_GRAY } },
|
|
217
|
+
{ text: COL_GAP },
|
|
218
|
+
{ text: fmtCtx(r.ctx_tokens).padStart(CTX_W), style: { fg: ctxFg, dim: ctxFg === undefined } },
|
|
219
|
+
{ text: COL_GAP },
|
|
220
|
+
{ text: relAge(r.created, now).padStart(AGE_W), style: { dim: true } },
|
|
221
|
+
];
|
|
222
|
+
if (showCwd) {
|
|
223
|
+
out.push({ text: COL_GAP }, { text: clipPad(`~${baseDir(r.cwd)}`, PROJ_W), style: { fg: FG_GRAY } });
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
/** Left-clip-pad: clip `s` to `w` cells then pad-end with spaces to exactly `w`. */
|
|
228
|
+
function clipPad(s, w) {
|
|
229
|
+
return clip(s, w).padEnd(w);
|
|
230
|
+
}
|
|
231
|
+
/** Compose a row from a flexible LEFT zone and a fixed flush-right RIGHT cluster:
|
|
232
|
+
* pad fills the gap so RIGHT hugs the edge; when the terminal is too narrow the
|
|
233
|
+
* cluster is dropped rather than colliding with the name. The caller has already
|
|
234
|
+
* clipped the name to fit. */
|
|
235
|
+
function assembleRow(left, right, width, color, lineBase, fill) {
|
|
236
|
+
const leftW = spansWidth(left);
|
|
237
|
+
const rightW = spansWidth(right);
|
|
238
|
+
if (right.length === 0 || leftW + rightW + 1 > width) {
|
|
239
|
+
return assemble(left, width, color, lineBase, fill);
|
|
240
|
+
}
|
|
241
|
+
const pad = width - leftW - rightW;
|
|
242
|
+
return assemble([...left, { text: ' '.repeat(pad) }, ...right], width, color, lineBase, fill);
|
|
243
|
+
}
|
|
244
|
+
/** One table row: `<status rail> <indent><collapse> <name> <flags>` ........ `<meta>`.
|
|
245
|
+
* `showCwd` adds the project column (All-dirs view); `now` drives the age. */
|
|
170
246
|
function rowLine(row, tree, width, isCursor, query, caps, showCwd, now) {
|
|
171
247
|
const node = tree.nodes.get(row.id);
|
|
172
248
|
if (node === undefined)
|
|
@@ -174,30 +250,35 @@ function rowLine(row, tree, width, isCursor, query, caps, showCwd, now) {
|
|
|
174
250
|
const r = node.row;
|
|
175
251
|
const indent = ' '.repeat(row.depth);
|
|
176
252
|
const collapse = !row.hasChildren ? ' ' : row.collapsed ? '▸' : '▾';
|
|
177
|
-
const glyph = STATUS_GLYPH[r.status] ?? '?';
|
|
178
253
|
const terminal = r.status === 'done' || r.status === 'dead' || r.status === 'canceled';
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
{ text: ' ' },
|
|
188
|
-
...nameSpans(r.name, query, nameStyle),
|
|
189
|
-
{ text: ` [${r.kind}/${r.mode}]`, style: { fg: FG_GRAY } }, // recedes
|
|
190
|
-
{ text: ' ctx ', style: { dim: true } },
|
|
191
|
-
{ text: ctxStr, style: { fg: ctxFg, dim: ctxFg === undefined } }, // tiered budget cue
|
|
192
|
-
];
|
|
193
|
-
if (age !== '')
|
|
194
|
-
spans.push({ text: ` ${age}`, style: { dim: true } }); // recency cue
|
|
195
|
-
if (showCwd)
|
|
196
|
-
spans.push({ text: ` ~${baseDir(r.cwd)}`, style: { fg: FG_GRAY } }); // project cue (All dirs)
|
|
254
|
+
// Left gutter (fixed slot): an eye when a viewer is ATTACHED — a scannable rail
|
|
255
|
+
// down the left edge of "what's on a screen right now". Blank otherwise.
|
|
256
|
+
const gutter = r.viewed === true
|
|
257
|
+
? { text: '◉ ', style: { fg: FG_BRIGHT_CYAN, bold: true } }
|
|
258
|
+
: { text: ' ' };
|
|
259
|
+
// Inline attention flag that travels with the name (streaming lives in the rail,
|
|
260
|
+
// attached lives in the gutter).
|
|
261
|
+
const flags = [];
|
|
197
262
|
if (r.asks > 0)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
263
|
+
flags.push({ text: ` ⚑${r.asks}`, style: { fg: FG_BRIGHT_YELLOW, bold: true } }); // pending asks
|
|
264
|
+
const status = statusRail(r);
|
|
265
|
+
const right = metaCluster(r, now, showCwd);
|
|
266
|
+
const treeLead = `${indent}${collapse} `;
|
|
267
|
+
// Clip the NAME (only) so gutter + status + tree + name + flags + meta all fit;
|
|
268
|
+
// the cluster is dropped first (assembleRow) when even a 4-col name won't fit it.
|
|
269
|
+
const fixed = spansWidth([gutter]) + spansWidth(status) + [...treeLead].length + spansWidth(flags);
|
|
270
|
+
const rightW = spansWidth(right);
|
|
271
|
+
const nameMax = Math.max(4, width - fixed - rightW - 1);
|
|
272
|
+
const nameStyle = { dim: !isCursor && terminal, bold: isCursor }; // dim terminal names; bold the cursor row
|
|
273
|
+
const left = [
|
|
274
|
+
gutter,
|
|
275
|
+
...status,
|
|
276
|
+
{ text: treeLead, style: { dim: true } },
|
|
277
|
+
...nameSpans(clip(r.name, nameMax), query, nameStyle),
|
|
278
|
+
...flags,
|
|
279
|
+
];
|
|
280
|
+
// Row base: cursor → subtle bg (256) or reverse fallback (also !color); non-matched
|
|
281
|
+
// ancestor → whole-row dim for tree context.
|
|
201
282
|
let lineBase = '';
|
|
202
283
|
let fill = false;
|
|
203
284
|
if (isCursor) {
|
|
@@ -207,7 +288,26 @@ function rowLine(row, tree, width, isCursor, query, caps, showCwd, now) {
|
|
|
207
288
|
else if (!row.matched) {
|
|
208
289
|
lineBase = DIM;
|
|
209
290
|
}
|
|
210
|
-
return
|
|
291
|
+
return assembleRow(left, right, width, caps.color, lineBase, fill);
|
|
292
|
+
}
|
|
293
|
+
/** The column-header row — dim labels aligned to the same zones the rows use, so
|
|
294
|
+
* the metadata columns read as a table. */
|
|
295
|
+
function columnHeaderLine(width, showCwd, caps) {
|
|
296
|
+
const left = [
|
|
297
|
+
{ text: ' ' }, // viewer gutter (no label)
|
|
298
|
+
{ text: 'STATUS'.padEnd(STATUS_W), style: { dim: true, bold: true } },
|
|
299
|
+
{ text: 'NAME', style: { dim: true, bold: true } },
|
|
300
|
+
];
|
|
301
|
+
const right = [
|
|
302
|
+
{ text: 'KIND/MODE'.padEnd(KM_W), style: { dim: true, bold: true } },
|
|
303
|
+
{ text: COL_GAP },
|
|
304
|
+
{ text: 'CTX'.padStart(CTX_W), style: { dim: true, bold: true } },
|
|
305
|
+
{ text: COL_GAP },
|
|
306
|
+
{ text: 'AGE'.padStart(AGE_W), style: { dim: true, bold: true } },
|
|
307
|
+
];
|
|
308
|
+
if (showCwd)
|
|
309
|
+
right.push({ text: COL_GAP }, { text: 'PROJECT'.padEnd(PROJ_W), style: { dim: true, bold: true } });
|
|
310
|
+
return assembleRow(left, right, width, caps.color, '', false);
|
|
211
311
|
}
|
|
212
312
|
/** The status line (always present): active cwd scope · sort mode · committed
|
|
213
313
|
* filter. The committed-filter indicator lives here now (not its own line) so a
|
|
@@ -248,11 +348,13 @@ function snippetLine(ln, width, caps) {
|
|
|
248
348
|
return assemble(spans, width, caps.color, '', false);
|
|
249
349
|
}
|
|
250
350
|
/** The bottom preview panel — exactly PREVIEW_HEIGHT lines: a separator, a meta
|
|
251
|
-
* line (status · kind/mode · project · age · ctx · asks), then
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
351
|
+
* line (status · kind/mode · project · age · ctx · asks · streaming/viewing), then
|
|
352
|
+
* TWO blocks — the spawn prompt / query-match (PROMPT_LINES) and the node's LAST
|
|
353
|
+
* assistant reply (REPLY_LINES, prefixed `↩`). Under a live query the prompt block
|
|
354
|
+
* is WINDOWED to the matching prompt (anywhere in the conversation) with the match
|
|
355
|
+
* highlighted; with no query it shows the spawn prompt from the start. Seeing both
|
|
356
|
+
* ends — what it was asked and where it left off — answers "which one was this?".
|
|
357
|
+
* Always full height so viewport math holds. */
|
|
256
358
|
function previewPanel(r, width, caps, now, query) {
|
|
257
359
|
const out = [`${DIM}${'─'.repeat(width)}${RESET}`];
|
|
258
360
|
if (r === undefined) {
|
|
@@ -266,23 +368,43 @@ function previewPanel(r, width, caps, now, query) {
|
|
|
266
368
|
const metaPieces = [`${r.status} ${r.kind}/${r.mode}`, baseDir(r.cwd), relAge(r.created, now), `ctx ${fmtCtx(r.ctx_tokens)}`];
|
|
267
369
|
if (r.asks > 0)
|
|
268
370
|
metaPieces.push(`⚑${r.asks}`);
|
|
371
|
+
if (r.streaming === true)
|
|
372
|
+
metaPieces.push('⟳ streaming');
|
|
373
|
+
if (r.viewed === true)
|
|
374
|
+
metaPieces.push('◉ viewing');
|
|
269
375
|
const metaText = clip(metaPieces.filter((p) => p !== '').join(' · '), Math.max(0, width - 2));
|
|
270
376
|
out.push(caps.color ? `${glyph} ${DIM}${metaText}${RESET}` : `${glyph} ${metaText}`);
|
|
271
|
-
// With a query, window+highlight the
|
|
272
|
-
//
|
|
377
|
+
// Block 1 — spawn prompt / query-match. With a query, window+highlight the
|
|
378
|
+
// matching prompt from the WHOLE conversation; otherwise the spawn prompt.
|
|
273
379
|
const sourceText = query !== '' ? promptText(r) : (r.goal ?? '');
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
380
|
+
const promptSnippet = previewSnippet(query, sourceText, width, PROMPT_LINES);
|
|
381
|
+
if (promptSnippet.length === 0) {
|
|
276
382
|
out.push(`${DIM}(no spawn prompt)${RESET}`);
|
|
277
|
-
for (let i = 1; i <
|
|
383
|
+
for (let i = 1; i < PROMPT_LINES; i++)
|
|
278
384
|
out.push('');
|
|
279
385
|
}
|
|
280
386
|
else {
|
|
281
|
-
for (let i = 0; i <
|
|
282
|
-
const ln =
|
|
387
|
+
for (let i = 0; i < PROMPT_LINES; i++) {
|
|
388
|
+
const ln = promptSnippet[i];
|
|
283
389
|
out.push(ln === undefined ? '' : snippetLine(ln, width, caps));
|
|
284
390
|
}
|
|
285
391
|
}
|
|
392
|
+
// Block 2 — the node's LAST assistant reply, prefixed `↩` on its first line so
|
|
393
|
+
// the two blocks read apart. Wrapped to width-2 to leave room for the marker.
|
|
394
|
+
const replyMark = caps.color ? `${DIM}↩ ${RESET}` : '↩ ';
|
|
395
|
+
const replySnippet = previewSnippet('', r.lastAssistant ?? '', Math.max(1, width - 2), REPLY_LINES);
|
|
396
|
+
if (replySnippet.length === 0) {
|
|
397
|
+
out.push(`${replyMark}${DIM}(no reply yet)${RESET}`);
|
|
398
|
+
for (let i = 1; i < REPLY_LINES; i++)
|
|
399
|
+
out.push('');
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
for (let i = 0; i < REPLY_LINES; i++) {
|
|
403
|
+
const ln = replySnippet[i];
|
|
404
|
+
const prefix = i === 0 ? replyMark : ' ';
|
|
405
|
+
out.push(ln === undefined ? (i === 0 ? `${replyMark}` : '') : `${prefix}${snippetLine(ln, Math.max(1, width - 2), caps)}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
286
408
|
return out;
|
|
287
409
|
}
|
|
288
410
|
/**
|
|
@@ -315,11 +437,12 @@ export function renderFrame(state, size, caps = { color: false, color256: false
|
|
|
315
437
|
const slash = caps.color ? `${ESC}${FG_CYAN}m/${RESET}` : '/';
|
|
316
438
|
lines.push(`${slash} ${state.query}▎`);
|
|
317
439
|
}
|
|
318
|
-
// separator.
|
|
440
|
+
// column header (table labels) + separator.
|
|
441
|
+
const showCwd = state.cwdScope === null; // project column only matters across dirs
|
|
442
|
+
lines.push(columnHeaderLine(width, showCwd, caps));
|
|
319
443
|
lines.push(`${DIM}${'─'.repeat(width)}${RESET}`);
|
|
320
444
|
// body — windowed visible rows, leaving room for the (optional) preview panel.
|
|
321
445
|
const head = lines.length; // === headerHeight(state.search)
|
|
322
|
-
const showCwd = state.cwdScope === null; // project cue only matters across dirs
|
|
323
446
|
const previewOn = state.preview && state.visible.length > 0;
|
|
324
447
|
const previewH = previewOn ? PREVIEW_HEIGHT : 0;
|
|
325
448
|
const viewport = Math.max(1, rows - head - 1 /* footer */ - previewH);
|
|
@@ -344,11 +467,19 @@ export function renderFrame(state, size, caps = { color: false, color256: false
|
|
|
344
467
|
for (const l of previewPanel(r, width, caps, now, state.query))
|
|
345
468
|
lines.push(l);
|
|
346
469
|
}
|
|
347
|
-
// footer.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
470
|
+
// footer — the y/n close-out confirm takes over when pending; else search / nav.
|
|
471
|
+
if (state.pendingClose !== null && state.pendingClose !== undefined) {
|
|
472
|
+
const node = state.tree.nodes.get(state.pendingClose);
|
|
473
|
+
const who = node !== undefined ? node.row.name : state.pendingClose;
|
|
474
|
+
const warn = `⚠ ${who} is actively streaming — close it (and its subtree) anyway? y / n`;
|
|
475
|
+
lines.push(caps.color ? `${BOLD}${ESC}${FG_BRIGHT_YELLOW}m${clip(warn, width)}${RESET}` : `${REVERSE}${clip(warn, width)}${RESET}`);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
const footer = state.search
|
|
479
|
+
? '⏎ commit Esc cancel ⌫ delete'
|
|
480
|
+
: '↑↓ move →/← tree ⏎ resume x close Tab tabs / search s sort c cwd r residents p preview q quit';
|
|
481
|
+
lines.push(`${DIM}${clip(footer, width)}${RESET}`);
|
|
482
|
+
}
|
|
352
483
|
// Assemble: home, each line cleared to EOL, then clear below.
|
|
353
484
|
const body = lines.map((l) => `${l}${ESC}K`).join('\r\n');
|
|
354
485
|
return `${ESC}H${body}${ESC}J`;
|
|
@@ -22,7 +22,7 @@ import { ensureHome, ensureNodeDirs, nodeMetaPath, nodeDir, nodesRoot, } from '.
|
|
|
22
22
|
const IDENTITY_KEYS = [
|
|
23
23
|
'node_id', 'name', 'description', 'cycles', 'created', 'cwd', 'host_kind', 'kind', 'mode',
|
|
24
24
|
'lifecycle', 'persona_ack', 'parent', 'spawned_by', 'fork_from', 'passive_default',
|
|
25
|
-
'
|
|
25
|
+
'pi_session_id', 'pi_session_file', 'model_override', 'launch',
|
|
26
26
|
];
|
|
27
27
|
/** Project any node object down to its durable-identity subset. */
|
|
28
28
|
function toIdentity(m) {
|
|
@@ -4,10 +4,10 @@ import type { FocusRow } from './types.js';
|
|
|
4
4
|
export declare function openFocusRow(focus_id: string, pane: string | null, session: string | null, node_id: string): void;
|
|
5
5
|
/** Hot-swap a focus's occupant — single-statement UPDATE. Respects
|
|
6
6
|
* UNIQUE(node_id): if `node_id` already occupies ANOTHER focus this throws
|
|
7
|
-
* (correct — vacate-first is
|
|
7
|
+
* (correct — vacate-first is placement.focus()'s job, not this setter's). */
|
|
8
8
|
export declare function setFocusOccupant(focus_id: string, node_id: string): void;
|
|
9
|
-
/** Re-point a
|
|
10
|
-
*
|
|
9
|
+
/** Re-point a viewer row's durable pane + its derived session cache after the
|
|
10
|
+
* pane moves. Single-statement UPDATE. */
|
|
11
11
|
export declare function setFocusPane(focus_id: string, pane: string | null, session: string | null): void;
|
|
12
12
|
/** DELETE a viewport. */
|
|
13
13
|
export declare function closeFocusRow(focus_id: string): void;
|
|
@@ -16,7 +16,7 @@ export declare function getFocusByNode(node_id: string): FocusRow | null;
|
|
|
16
16
|
/** The focus realized by a given pane (`%id`), or null. */
|
|
17
17
|
export declare function getFocusByPane(pane: string): FocusRow | null;
|
|
18
18
|
/** A focus by its stable id, or null. Used by placement to read a row back by id
|
|
19
|
-
*
|
|
19
|
+
* after openFocusRow registers a viewer. */
|
|
20
20
|
export declare function getFocusById(focus_id: string): FocusRow | null;
|
|
21
21
|
/** Every focus row, ordered by id. */
|
|
22
22
|
export declare function listFocuses(): FocusRow[];
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
// setPresence) — it never runs raw focus SQL itself.
|
|
12
12
|
//
|
|
13
13
|
// Each setter is a single atomic statement. UNIQUE(node_id) upholds "a node
|
|
14
|
-
// occupies at most one
|
|
15
|
-
// for an already-
|
|
16
|
-
// orchestration is
|
|
14
|
+
// occupies at most one viewer" (Q5): a second focus row (or an occupant UPDATE)
|
|
15
|
+
// for an already-viewed node throws — that is correct, the vacate-first
|
|
16
|
+
// orchestration is placement.focus()'s job, not these setters'.
|
|
17
17
|
import { openDb } from './db.js';
|
|
18
18
|
function focusFrom(r) {
|
|
19
19
|
return {
|
|
@@ -35,12 +35,12 @@ export function openFocusRow(focus_id, pane, session, node_id) {
|
|
|
35
35
|
}
|
|
36
36
|
/** Hot-swap a focus's occupant — single-statement UPDATE. Respects
|
|
37
37
|
* UNIQUE(node_id): if `node_id` already occupies ANOTHER focus this throws
|
|
38
|
-
* (correct — vacate-first is
|
|
38
|
+
* (correct — vacate-first is placement.focus()'s job, not this setter's). */
|
|
39
39
|
export function setFocusOccupant(focus_id, node_id) {
|
|
40
40
|
openDb().prepare('UPDATE focuses SET node_id = ? WHERE focus_id = ?').run(node_id, focus_id);
|
|
41
41
|
}
|
|
42
|
-
/** Re-point a
|
|
43
|
-
*
|
|
42
|
+
/** Re-point a viewer row's durable pane + its derived session cache after the
|
|
43
|
+
* pane moves. Single-statement UPDATE. */
|
|
44
44
|
export function setFocusPane(focus_id, pane, session) {
|
|
45
45
|
openDb()
|
|
46
46
|
.prepare('UPDATE focuses SET pane = ?, session = ? WHERE focus_id = ?')
|
|
@@ -68,7 +68,7 @@ export function getFocusByPane(pane) {
|
|
|
68
68
|
return r ? focusFrom(r) : null;
|
|
69
69
|
}
|
|
70
70
|
/** A focus by its stable id, or null. Used by placement to read a row back by id
|
|
71
|
-
*
|
|
71
|
+
* after openFocusRow registers a viewer. */
|
|
72
72
|
export function getFocusById(focus_id) {
|
|
73
73
|
const r = openDb()
|
|
74
74
|
.prepare('SELECT * FROM focuses WHERE focus_id = ?')
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { NodeMeta, SubscriptionRef } from './index.js';
|
|
2
|
+
/** pi's InteractiveMode.MAX_WIDGET_LINES — the hard cap on lines in a string
|
|
3
|
+
* array widget; anything beyond it pi truncates with its own "... (widget
|
|
4
|
+
* truncated)". Our GRAPH viewport stays at/under this and scrolls internally. */
|
|
5
|
+
export declare const PI_MAX_WIDGET_LINES = 10;
|
|
6
|
+
export declare const VIEWPORT_FALLBACK_ROWS = 30;
|
|
7
|
+
export declare const ESC = "\u001B[";
|
|
8
|
+
export declare const RESET = "\u001B[0m";
|
|
9
|
+
export declare const BOLD = "\u001B[1m";
|
|
10
|
+
export declare const DIM = "\u001B[2m";
|
|
11
|
+
export declare const REVERSE = "\u001B[7m";
|
|
12
|
+
/** Dark-green background bar marking an ATTACHED node (a human is currently
|
|
13
|
+
* watching it) — distinct from the cursor's reverse-video bar; chosen so
|
|
14
|
+
* default-fg text stays readable. */
|
|
15
|
+
export declare const BG_ATTACHED = "\u001B[48;5;22m";
|
|
16
|
+
export declare const GREEN = "\u001B[32m";
|
|
17
|
+
export declare const RED = "\u001B[31m";
|
|
18
|
+
export declare const YELLOW = "\u001B[33m";
|
|
19
|
+
export declare const CYAN = "\u001B[36m";
|
|
20
|
+
export declare const GRAY = "\u001B[90m";
|
|
21
|
+
/** Status glyph colored by state: active green, idle dim, done cyan, dead red. */
|
|
22
|
+
export declare function coloredGlyph(node: NodeMeta | null): string;
|
|
23
|
+
/** Visible width, ignoring ANSI escapes. */
|
|
24
|
+
export declare function visibleWidth(s: string): number;
|
|
25
|
+
/** Truncate to `max` VISIBLE columns: escape sequences are copied through
|
|
26
|
+
* verbatim (so a cut never lands mid-escape) and the result always ends in
|
|
27
|
+
* RESET, so a clipped style can't bleed into the editor below. */
|
|
28
|
+
export declare function truncate(s: string, max?: number): string;
|
|
29
|
+
/** Visible columns available to ONE widget line — the cap for every line we
|
|
30
|
+
* emit, and the width a full-width reverse-video SELF bar fills to.
|
|
31
|
+
*
|
|
32
|
+
* pi does NOT clip widget lines; it WRAPS them. Each string line is wrapped in
|
|
33
|
+
* a `Text(paddingX = 1)` inside a full-terminal-width container, so the usable
|
|
34
|
+
* content width is `columns - 2` (a 1-col margin on each side). A line wider
|
|
35
|
+
* than that wraps, and the overflow spills onto a second row as a stray
|
|
36
|
+
* reverse-video block (the bug this guards against). Clamp to `columns - 2`. */
|
|
37
|
+
export declare function fillWidth(): number;
|
|
38
|
+
/** Wrap `content` in a full-width background bar opened by `open` (REVERSE for
|
|
39
|
+
* the cursor, BG_ATTACHED for a human-watched node). `open` is re-asserted after every
|
|
40
|
+
* embedded RESET so a coloured cell (the status dot) can't punch a hole in the
|
|
41
|
+
* bar; the visible width is padded out to `width`; the line closes with a real
|
|
42
|
+
* RESET so the style never bleeds into the editor below. */
|
|
43
|
+
export declare function fillBar(content: string, width: number, open: string): string;
|
|
44
|
+
export declare function beginFrame(): void;
|
|
45
|
+
/** getNode memoized for the current frame (meta.json read + sqlite row). */
|
|
46
|
+
export declare function cNode(id: string): NodeMeta | null;
|
|
47
|
+
/** subscriptionsOf (a node's reports) memoized for the current frame. */
|
|
48
|
+
export declare function cSubscriptions(id: string): SubscriptionRef[];
|
|
49
|
+
/** subscribersOf (a node's managers) memoized for the current frame. */
|
|
50
|
+
export declare function cSubscribers(id: string): SubscriptionRef[];
|
|
51
|
+
export interface Telemetry {
|
|
52
|
+
/** Live context-window size (pi's getContextUsage) — the figure pi's own footer
|
|
53
|
+
* shows. This is the node's window fill, NOT a per-turn delta. */
|
|
54
|
+
context_tokens?: number;
|
|
55
|
+
/** One-line "what is it doing" cue (`tool: detail`), written on every tool start. */
|
|
56
|
+
last_activity?: string;
|
|
57
|
+
}
|
|
58
|
+
export declare function readTelemetry(nodeId: string): Telemetry;
|
|
59
|
+
export declare function fmtTokens(n: number): string;
|
|
60
|
+
/** The context-window cell — live window fill rounded to the nearest 1k (the same
|
|
61
|
+
* figure pi's footer shows), NOT a per-turn token delta. */
|
|
62
|
+
export declare function tokensCell(id: string): string;
|
|
63
|
+
/** Dimmed live "what is it doing" cue for an ACTIVE node — the last tool it ran.
|
|
64
|
+
* Empty for non-active rows (stale once a node parks) and when none is recorded. */
|
|
65
|
+
export declare function activityCell(id: string, node: NodeMeta | null): string;
|
|
66
|
+
/** Revive-count badge (meta.cycles). Hidden on the first cycle (0) to cut noise. */
|
|
67
|
+
export declare function cycleBadge(node: NodeMeta | null): string;
|
|
68
|
+
/** Short on-screen label: the explicit handle when set, else the pi-generated
|
|
69
|
+
* description, else the bare name. fullName joins BOTH (`handle description`);
|
|
70
|
+
* the nav chrome shows just the first so rows stay compact. */
|
|
71
|
+
export declare function navLabel(node: NodeMeta | null, id: string): string;
|
|
72
|
+
export declare function shortId(id: string): string;
|
|
73
|
+
/** Node ids currently shown in a tmux focus viewport. Built once per render. */
|
|
74
|
+
export declare function focusedNodeIds(): Set<string>;
|
|
75
|
+
/** True when a human is watching `id` right now (tmux focus or broker viewer). */
|
|
76
|
+
export declare function isAttached(id: string, node: NodeMeta | null, focused: ReadonlySet<string>): boolean;
|
|
77
|
+
export declare function fetchAsksMap(rootId: string): Record<string, number>;
|
|
78
|
+
/** First manager (by created) — the UP step for the ancestry spine. */
|
|
79
|
+
export declare function managerOf(id: string): string | undefined;
|
|
80
|
+
/** A kind:'human' node is a control-plane ASK (a humanloop deck on the human's
|
|
81
|
+
* screen), NOT a pi conversation — it has no session, so focusing/reviving it
|
|
82
|
+
* boots a confused blank "you have been revived" pi. Its pending-ask signal
|
|
83
|
+
* already rides the ⚑ badge on the ASKING node (attention.ts attributes asks by
|
|
84
|
+
* source.nodeId, never to the human node), so the row carries no signal of its
|
|
85
|
+
* own. Drop it from every navigable list (the tree, BASE reports, child counts,
|
|
86
|
+
* subtree expansion) so it can never be selected. */
|
|
87
|
+
export declare function isHumanAsk(id: string): boolean;
|
|
88
|
+
/** A node's direct children that are navigable conversations — human-ask nodes
|
|
89
|
+
* dropped. The one place the nav chrome enumerates children. */
|
|
90
|
+
export declare function convoChildIds(id: string): string[];
|
|
91
|
+
/** Live reports (active|idle) of a node — the DOWN set in BASE. */
|
|
92
|
+
export declare function liveReports(id: string): string[];
|
|
93
|
+
/** Direct navigable children — used for the ⤳ badge and fold counts (human-ask
|
|
94
|
+
* nodes excluded, so the count matches what the tree actually shows). */
|
|
95
|
+
export declare function childCount(id: string): number;
|
|
96
|
+
/** Climb first-manager edges from `self` to the ancestry root (cycle-guarded). */
|
|
97
|
+
export declare function climbRoot(self: string): string;
|
|
98
|
+
/** Space-joined ids of a node's subtree (cursor-relative {subtree} var). */
|
|
99
|
+
export declare function subtreeIds(root: string): string[];
|
|
100
|
+
/** ⤳M direct-children badge — only on orchestrator rows. */
|
|
101
|
+
export declare function childBadge(node: NodeMeta | null): string;
|
|
102
|
+
/** ⚑K pending-asks badge for a node, read from the supplied per-node ask map. */
|
|
103
|
+
export declare function askBadge(id: string, asks: Record<string, number>): string;
|
|
104
|
+
/** "Live work below" badge — green ⇣N when this node is NOT itself active but has
|
|
105
|
+
* N active descendants. The one signal that an idle/parked node isn't dead: real
|
|
106
|
+
* work is still running beneath it. Count comes from the single subtree-activity
|
|
107
|
+
* pass (no per-row walk). Suppressed on active rows (their own ● already says so). */
|
|
108
|
+
export declare function liveBelowBadge(node: NodeMeta | null, activeBelow: ReadonlyMap<string, number>): string;
|
|
109
|
+
/** Sort rank for sibling ordering — live nodes (active, then idle) ahead of
|
|
110
|
+
* terminal ones, so sessions still running surface at the TOP of each child
|
|
111
|
+
* group instead of being buried under finished/failed ones. */
|
|
112
|
+
export declare function statusRank(id: string): number;
|
|
113
|
+
/** Direct children, live-first — the sibling order used both when flattening
|
|
114
|
+
* the tree and when stepping into a subtree (`l`). Array.sort is stable, so
|
|
115
|
+
* equal-status siblings keep their creation order. */
|
|
116
|
+
export declare function sortedChildIds(id: string): string[];
|
|
117
|
+
export interface FlatRow {
|
|
118
|
+
id: string;
|
|
119
|
+
hasKids: boolean;
|
|
120
|
+
isSelf: boolean;
|
|
121
|
+
branch: string;
|
|
122
|
+
cycle: boolean;
|
|
123
|
+
collapsed: boolean;
|
|
124
|
+
}
|
|
125
|
+
/** Manual fold OVERRIDES, keyed by id. `userCollapsed` forces a fold, `userExpanded`
|
|
126
|
+
* forces an unfold; both override the default activity-driven policy. Held by the
|
|
127
|
+
* consumer (the extension / viewer) and threaded into buildGraphModel so this layer
|
|
128
|
+
* carries no mutable singletons. */
|
|
129
|
+
export interface FoldState {
|
|
130
|
+
userExpanded: ReadonlySet<string>;
|
|
131
|
+
userCollapsed: ReadonlySet<string>;
|
|
132
|
+
}
|
|
133
|
+
/** One bottom-up O(N) pass over the local graph, computing TWO things at once so
|
|
134
|
+
* the render path never walks twice:
|
|
135
|
+
* - `expand`: which nodes auto-UNFOLD — a node expands when a child subtree holds
|
|
136
|
+
* a running ('active') agent or self, so the path to any live agent (and to
|
|
137
|
+
* you) is revealed while quiescent branches stay folded.
|
|
138
|
+
* - `activeBelow`: per node, the count of ACTIVE descendants (excluding itself) —
|
|
139
|
+
* drives the ⇣N "live work below" badge on idle/parked nodes.
|
|
140
|
+
* Cycle-guarded (the graph is declared acyclic; a re-seen id contributes only
|
|
141
|
+
* its own status, never its subtree again, so counts can't double or loop). */
|
|
142
|
+
export interface SubtreeActivity {
|
|
143
|
+
expand: Set<string>;
|
|
144
|
+
activeBelow: Map<string, number>;
|
|
145
|
+
}
|
|
146
|
+
export declare function computeSubtreeActivity(root: string, self: string): SubtreeActivity;
|
|
147
|
+
/** Fold policy alone — thin wrapper over computeSubtreeActivity for the keypress
|
|
148
|
+
* path (buildGraphModel without a precomputed set). The render path computes the
|
|
149
|
+
* activity once and threads `expand` in, so it never recomputes here. */
|
|
150
|
+
export declare function computeDefaultExpanded(root: string, self: string): Set<string>;
|
|
151
|
+
export declare function buildGraphModel(self: string, folds: FoldState, expand?: ReadonlySet<string>): FlatRow[];
|
|
152
|
+
/** Render one GRAPH row. CURSOR (selected) → reverse-video bar; an ATTACHED
|
|
153
|
+
* (human-watched) node → a coloured background bar; SELF → bold name. The
|
|
154
|
+
* cursor outranks the attached tint when both land on the same row. Running
|
|
155
|
+
* is signaled by the dot glyph alone (● green = active engine). */
|
|
156
|
+
export declare function renderGraphRow(r: FlatRow, isCursor: boolean, focused: ReadonlySet<string>, activeBelow: ReadonlyMap<string, number>, asks: Record<string, number>): string;
|
|
157
|
+
/** Total lines the GRAPH widget may emit. pi hard-caps extension widgets at
|
|
158
|
+
* MAX_WIDGET_LINES — anything past that pi truncates itself, eating our own
|
|
159
|
+
* scroll chrome — so never exceed it (and shrink on a very short terminal).
|
|
160
|
+
* The viewport scrolls WITHIN this cap as the cursor moves. */
|
|
161
|
+
export declare function graphWidgetBudget(): number;
|
|
162
|
+
export declare const GRAPH_HINT = "\u001B[2mjk move \u00B7 hl fold \u00B7 \u21B5 focus \u00B7 e expand \u00B7 x kill \u00B7 m mgr \u00B7 esc\u001B[0m";
|