@crouton-kit/crouter 0.3.28 → 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 +5 -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__/slash-quit-copy.test.js +42 -0
- package/dist/clients/attach/__tests__/titled-editor.test.js +39 -0
- package/dist/clients/attach/attach-cmd.js +282 -46
- 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 +4 -2
- package/dist/clients/attach/canvas-panels.js +45 -17
- 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 +13 -4
- package/dist/clients/attach/graph-overlay.js +59 -19
- package/dist/clients/attach/input-controller.d.ts +75 -13
- package/dist/clients/attach/input-controller.js +305 -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 +22 -0
- package/dist/clients/attach/slash-commands.js +91 -25
- 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 +18 -1
- package/dist/clients/attach/view-socket.js +70 -1
- 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/memory/__tests__/lint-schema.test.js +6 -6
- package/dist/commands/memory/lint.js +14 -2
- 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 +8 -4
- 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/sync.d.ts +1 -0
- package/dist/commands/sys/sync.js +187 -0
- package/dist/commands/sys.js +3 -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__/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 +17 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +22 -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__/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.js +2 -2
- 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.js +8 -7
- 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.d.ts +4 -1
- package/dist/core/runtime/broker.js +494 -58
- package/dist/core/runtime/close.d.ts +12 -2
- package/dist/core/runtime/close.js +37 -16
- package/dist/core/runtime/front-door.js +6 -14
- 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 -335
- package/dist/core/runtime/placement.js +262 -840
- 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 +40 -139
- package/dist/core/runtime/spawn.d.ts +15 -16
- package/dist/core/runtime/spawn.js +165 -194
- 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 +13 -50
- package/dist/core/runtime/tmux.js +33 -87
- 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/substrate/index.d.ts +1 -1
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.js +3 -3
- 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 +10 -3
- package/dist/core/substrate/schema.js +6 -3
- 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 +16 -16
- package/dist/pi-extensions/canvas-nav.js +5 -0
- 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 +14 -12
- package/dist/pi-extensions/canvas-stophook.js +87 -101
- 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 -297
- 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__/slash-quit-copy.test.d.ts} +0 -0
- /package/dist/{core/__tests__/relaunch.test.d.ts → clients/attach/__tests__/titled-editor.test.d.ts} +0 -0
|
@@ -46,6 +46,12 @@ export interface SplitWindowOpts {
|
|
|
46
46
|
command: string;
|
|
47
47
|
/** Stack the new pane below instead of beside (default: beside, `-h`). */
|
|
48
48
|
vertical?: boolean;
|
|
49
|
+
/** Place the new pane BEFORE the target (left for `-h`, above for `-v`) via
|
|
50
|
+
* `-b`, instead of after it (default: after — right/below). */
|
|
51
|
+
before?: boolean;
|
|
52
|
+
/** Fixed size of the new pane in the split axis' cells (columns for `-h`, rows
|
|
53
|
+
* for `-v`) via `-l`. Omit ⇒ tmux's default even split. */
|
|
54
|
+
size?: number;
|
|
49
55
|
}
|
|
50
56
|
/** Split `targetPane`'s window, opening a NEW pane beside it running `command`,
|
|
51
57
|
* and return the new pane id (the durable `%id`). The ONLY new-pane-beside verb
|
|
@@ -62,6 +68,10 @@ export declare function splitWindow(targetPane: string, opts: SplitWindowOpts):
|
|
|
62
68
|
export declare function focusWindow(session: string, window: string): boolean;
|
|
63
69
|
/** Close a node's window (drop it from the UI). */
|
|
64
70
|
export declare function closeWindow(window: string): boolean;
|
|
71
|
+
/** Rename a window (`tmux rename-window -t <window> <name>`). Used by the
|
|
72
|
+
* root relaunch to re-title the viewer window when its pane is re-pointed at
|
|
73
|
+
* the freshly-minted node. Best-effort; false if tmux fails. */
|
|
74
|
+
export declare function renameWindow(window: string, name: string): boolean;
|
|
65
75
|
/** Close a single PANE. Its window closes automatically once this was the last
|
|
66
76
|
* pane, but sibling panes survive — so co-located nodes (several agents sharing
|
|
67
77
|
* one window via swap-pane focus) are torn down one at a time instead of all
|
|
@@ -97,13 +107,9 @@ export declare function paneLocation(pane: string): {
|
|
|
97
107
|
* pane: a live pane echoes its own id, a gone/bogus one yields empty. */
|
|
98
108
|
export declare function paneExists(pane: string): boolean;
|
|
99
109
|
/** Does this pane exist AND have its command still RUNNING (`#{pane_dead}` = 0)?
|
|
100
|
-
* Distinguishes a pane genuinely hosting a
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* (openNodeWindow / respawn-pane), never a shell, so pane-running ⟹ a pi (or
|
|
104
|
-
* its respawn) occupies it. The probe handFocusToManager's live-swap gates on:
|
|
105
|
-
* the recorded `pi_pid` is a stale proxy in the just-revived window (the new pi
|
|
106
|
-
* records its pid only at session_start), but the pane itself never lies. */
|
|
110
|
+
* Distinguishes a pane genuinely hosting a live process from a remain-on-exit
|
|
111
|
+
* corpse frozen after exit (`pane_dead` = 1). Node viewer panes run `crtr attach`
|
|
112
|
+
* as the pane command, so pane-running ⟹ a live viewer occupies it. */
|
|
107
113
|
export declare function paneRunning(pane: string): boolean;
|
|
108
114
|
/** Every live pane id on the server (`list-panes -a`), as a Set for membership
|
|
109
115
|
* probes. Returns null when tmux is unreachable (no server / transient failure)
|
|
@@ -125,21 +131,6 @@ export declare function setPaneOption(pane: string, name: string, value: string)
|
|
|
125
131
|
* <name>`): `-v` prints only the value, `-q` suppresses the unknown-option
|
|
126
132
|
* error so an unset option yields an empty string. undefined if tmux fails. */
|
|
127
133
|
export declare function getPaneOption(pane: string, name: string): string | undefined;
|
|
128
|
-
/** Relocate a pane into another session as its own window WITHOUT killing the
|
|
129
|
-
* process in it — `break-pane -d` moves the pane out of its current window (the
|
|
130
|
-
* pi keeps generating) into a fresh window in `session`; `-d` leaves the caller's
|
|
131
|
-
* client where it is rather than following the pane to the background, and `-a`
|
|
132
|
-
* allocates the next free window index (same dodge as openNodeWindow). The
|
|
133
|
-
* "detach to background" driver behind `node lifecycle --detach`. Best-effort;
|
|
134
|
-
* false if tmux refuses (e.g. the pane is gone). The caller reconciles presence
|
|
135
|
-
* so the canvas follows the move. */
|
|
136
|
-
export declare function breakPaneToSession(pane: string, session: string): boolean;
|
|
137
|
-
/** Swap `targetPane` into `callerPane`'s layout slot, IN PLACE. `-d` keeps the
|
|
138
|
-
* caller's window active, so the target's pane appears where the caller is
|
|
139
|
-
* rather than navigating the client off to the target's window. The caller's
|
|
140
|
-
* old pane lives on in the target's former window — the move is reversible
|
|
141
|
-
* (focusing back swaps it in again). Best-effort; never throws. */
|
|
142
|
-
export declare function swapPaneInPlace(targetPane: string, callerPane: string): boolean;
|
|
143
134
|
export interface RespawnPaneOpts {
|
|
144
135
|
/** Target pane id (e.g. `%3`) — the pane to re-exec in place. */
|
|
145
136
|
pane: string;
|
|
@@ -148,21 +139,11 @@ export interface RespawnPaneOpts {
|
|
|
148
139
|
/** The full command to run in the pane (already a shell string). */
|
|
149
140
|
command: string;
|
|
150
141
|
}
|
|
151
|
-
/** Re-exec a command in an EXISTING pane, in place — DETACHED. Spawned in its own
|
|
152
|
-
* process group (unref'd) so the request reaches the tmux server even though
|
|
153
|
-
* `-k` tears down the caller's own pi mid-flight. Used when a node respawns ITS
|
|
154
|
-
* OWN pane (refresh-yield): the dispatch can't be awaited because it kills the
|
|
155
|
-
* awaiter. Returns true once the request was dispatched. */
|
|
156
|
-
export declare function respawnPaneDetached(opts: RespawnPaneOpts): boolean;
|
|
157
142
|
/** Re-exec a command in an EXISTING pane, in place — SYNCHRONOUS. Runs the
|
|
158
143
|
* `respawn-pane` to completion and reports the real exit status. Used when the
|
|
159
144
|
* caller is NOT the pane being respawned (e.g. the daemon resuming a frozen
|
|
160
145
|
* focus pane), so it can confirm the respawn landed. Returns true on success. */
|
|
161
146
|
export declare function respawnPaneSync(opts: RespawnPaneOpts): boolean;
|
|
162
|
-
/** @deprecated Use respawnPaneDetached. Retained so existing refresh-yield
|
|
163
|
-
* callers stay green while the placement layer migrates onto the explicit
|
|
164
|
-
* sync/detached split. */
|
|
165
|
-
export declare function respawnPane(opts: RespawnPaneOpts): boolean;
|
|
166
147
|
/** Turn a pi argv array into a single shell command string.
|
|
167
148
|
*
|
|
168
149
|
* The binary defaults to `CRTR_PI_BINARY` when that env var is set, else the
|
|
@@ -192,24 +173,6 @@ export declare function selectWindow(session: string, window: string): boolean;
|
|
|
192
173
|
* `tmux switch-client -t <session>`. Best-effort; never throws. The caller is
|
|
193
174
|
* responsible for following up with selectWindow to land on the right window. */
|
|
194
175
|
export declare function switchClient(session: string): boolean;
|
|
195
|
-
/** Move a source pane into a destination window (`tmux join-pane`). The source
|
|
196
|
-
* pane's running process (e.g. a child's live pi) is preserved; its now-empty
|
|
197
|
-
* source window auto-closes. Best-effort; false if tmux fails. */
|
|
198
|
-
export declare function joinPane(srcPane: string, dstWindow: string): boolean;
|
|
199
|
-
/** Apply a named tmux layout to a window (`tmux select-layout`). Use
|
|
200
|
-
* `main-vertical` for one wide pane on the left + the rest stacked right.
|
|
201
|
-
* Best-effort; never throws. */
|
|
202
|
-
export declare function selectLayout(window: string, layout: string): boolean;
|
|
203
|
-
/** Set a tmux window option (`tmux set-window-option`). Used to size the main
|
|
204
|
-
* pane (`main-pane-width`) before a main-vertical layout. Best-effort. */
|
|
205
|
-
export declare function setWindowOption(window: string, name: string, value: string): boolean;
|
|
206
|
-
/** Toggle `remain-on-exit` on a window (§1.5 F3). `on` keeps a focus pane on
|
|
207
|
-
* screen after its pi exits — the viewport survives (F1), the final transcript
|
|
208
|
-
* is preserved, and `respawn-pane -k` can resurrect the node into the SAME pane
|
|
209
|
-
* id. NOTE (§1.5/§2.5, spike-confirmed): a dead/frozen pane is reaped only by
|
|
210
|
-
* `kill-pane`/`respawn-pane`, NEVER by toggling this off — the toggle does not
|
|
211
|
-
* reap an already-dead pane. Best-effort; never throws. */
|
|
212
|
-
export declare function setRemainOnExit(window: string, on: boolean): boolean;
|
|
213
176
|
/** Type a literal (e.g. a `/graph` slash command) into a pane and press Enter
|
|
214
177
|
* (`tmux send-keys -t <pane> '<text>' Enter`). Requires the pane's editor be
|
|
215
178
|
* empty, same limitation as the menu's `/promote` item. Best-effort. */
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
// UI unless you switch to it. Bring one forefront with select-window (within a
|
|
8
8
|
// root) or switch-client + select-window (across roots). done/dead nodes close
|
|
9
9
|
// their window; reviving opens a fresh one.
|
|
10
|
-
import {
|
|
10
|
+
import { spawnSync } from 'node:child_process';
|
|
11
11
|
import { readConfig } from '../config.js';
|
|
12
|
-
import {
|
|
12
|
+
import { surfaceTmuxStyleArgs } from './surface-bg.js';
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// Shell quoting + tmux invocation
|
|
15
15
|
// ---------------------------------------------------------------------------
|
|
@@ -115,6 +115,8 @@ export function splitWindow(targetPane, opts) {
|
|
|
115
115
|
'split-window',
|
|
116
116
|
'-d',
|
|
117
117
|
...(opts.vertical === true ? [] : ['-h']),
|
|
118
|
+
...(opts.before === true ? ['-b'] : []),
|
|
119
|
+
...(opts.size !== undefined ? ['-l', String(opts.size)] : []),
|
|
118
120
|
'-P',
|
|
119
121
|
'-F',
|
|
120
122
|
'#{pane_id}',
|
|
@@ -141,6 +143,12 @@ export function focusWindow(session, window) {
|
|
|
141
143
|
export function closeWindow(window) {
|
|
142
144
|
return tmux(['kill-window', '-t', window]).ok;
|
|
143
145
|
}
|
|
146
|
+
/** Rename a window (`tmux rename-window -t <window> <name>`). Used by the
|
|
147
|
+
* root relaunch to re-title the viewer window when its pane is re-pointed at
|
|
148
|
+
* the freshly-minted node. Best-effort; false if tmux fails. */
|
|
149
|
+
export function renameWindow(window, name) {
|
|
150
|
+
return tmux(['rename-window', '-t', window, name]).ok;
|
|
151
|
+
}
|
|
144
152
|
/** Close a single PANE. Its window closes automatically once this was the last
|
|
145
153
|
* pane, but sibling panes survive — so co-located nodes (several agents sharing
|
|
146
154
|
* one window via swap-pane focus) are torn down one at a time instead of all
|
|
@@ -192,13 +200,9 @@ export function paneExists(pane) {
|
|
|
192
200
|
return r.ok && r.stdout === pane;
|
|
193
201
|
}
|
|
194
202
|
/** Does this pane exist AND have its command still RUNNING (`#{pane_dead}` = 0)?
|
|
195
|
-
* Distinguishes a pane genuinely hosting a
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* (openNodeWindow / respawn-pane), never a shell, so pane-running ⟹ a pi (or
|
|
199
|
-
* its respawn) occupies it. The probe handFocusToManager's live-swap gates on:
|
|
200
|
-
* the recorded `pi_pid` is a stale proxy in the just-revived window (the new pi
|
|
201
|
-
* records its pid only at session_start), but the pane itself never lies. */
|
|
203
|
+
* Distinguishes a pane genuinely hosting a live process from a remain-on-exit
|
|
204
|
+
* corpse frozen after exit (`pane_dead` = 1). Node viewer panes run `crtr attach`
|
|
205
|
+
* as the pane command, so pane-running ⟹ a live viewer occupies it. */
|
|
202
206
|
export function paneRunning(pane) {
|
|
203
207
|
const r = tmux(['display-message', '-p', '-t', pane, '#{pane_id}\t#{pane_dead}']);
|
|
204
208
|
return r.ok && r.stdout === `${pane}\t0`;
|
|
@@ -236,27 +240,6 @@ export function getPaneOption(pane, name) {
|
|
|
236
240
|
const r = tmux(['show-options', '-p', '-t', pane, '-q', '-v', name]);
|
|
237
241
|
return r.ok ? r.stdout : undefined;
|
|
238
242
|
}
|
|
239
|
-
/** Relocate a pane into another session as its own window WITHOUT killing the
|
|
240
|
-
* process in it — `break-pane -d` moves the pane out of its current window (the
|
|
241
|
-
* pi keeps generating) into a fresh window in `session`; `-d` leaves the caller's
|
|
242
|
-
* client where it is rather than following the pane to the background, and `-a`
|
|
243
|
-
* allocates the next free window index (same dodge as openNodeWindow). The
|
|
244
|
-
* "detach to background" driver behind `node lifecycle --detach`. Best-effort;
|
|
245
|
-
* false if tmux refuses (e.g. the pane is gone). The caller reconciles presence
|
|
246
|
-
* so the canvas follows the move. */
|
|
247
|
-
export function breakPaneToSession(pane, session) {
|
|
248
|
-
return tmux(['break-pane', '-d', '-a', '-s', pane, '-t', `${session}:`]).ok;
|
|
249
|
-
}
|
|
250
|
-
/** Swap `targetPane` into `callerPane`'s layout slot, IN PLACE. `-d` keeps the
|
|
251
|
-
* caller's window active, so the target's pane appears where the caller is
|
|
252
|
-
* rather than navigating the client off to the target's window. The caller's
|
|
253
|
-
* old pane lives on in the target's former window — the move is reversible
|
|
254
|
-
* (focusing back swaps it in again). Best-effort; never throws. */
|
|
255
|
-
export function swapPaneInPlace(targetPane, callerPane) {
|
|
256
|
-
if (targetPane === callerPane)
|
|
257
|
-
return true;
|
|
258
|
-
return tmux(['swap-pane', '-d', '-s', targetPane, '-t', callerPane]).ok;
|
|
259
|
-
}
|
|
260
243
|
/** The `respawn-pane -k` argv for `opts`. `-k` kills the pane's current process
|
|
261
244
|
* (e.g. a yielding pi) and re-execs `command` in the SAME pane, preserving its
|
|
262
245
|
* `%id` (§1.5 F3: a frozen focus pane resumes in place, no new window). The
|
|
@@ -274,24 +257,6 @@ function respawnPaneArgs(opts) {
|
|
|
274
257
|
opts.command,
|
|
275
258
|
];
|
|
276
259
|
}
|
|
277
|
-
/** Re-exec a command in an EXISTING pane, in place — DETACHED. Spawned in its own
|
|
278
|
-
* process group (unref'd) so the request reaches the tmux server even though
|
|
279
|
-
* `-k` tears down the caller's own pi mid-flight. Used when a node respawns ITS
|
|
280
|
-
* OWN pane (refresh-yield): the dispatch can't be awaited because it kills the
|
|
281
|
-
* awaiter. Returns true once the request was dispatched. */
|
|
282
|
-
export function respawnPaneDetached(opts) {
|
|
283
|
-
try {
|
|
284
|
-
const child = spawn('tmux', respawnPaneArgs(opts), {
|
|
285
|
-
detached: true,
|
|
286
|
-
stdio: 'ignore',
|
|
287
|
-
});
|
|
288
|
-
child.unref();
|
|
289
|
-
return true;
|
|
290
|
-
}
|
|
291
|
-
catch {
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
260
|
/** Re-exec a command in an EXISTING pane, in place — SYNCHRONOUS. Runs the
|
|
296
261
|
* `respawn-pane` to completion and reports the real exit status. Used when the
|
|
297
262
|
* caller is NOT the pane being respawned (e.g. the daemon resuming a frozen
|
|
@@ -299,12 +264,6 @@ export function respawnPaneDetached(opts) {
|
|
|
299
264
|
export function respawnPaneSync(opts) {
|
|
300
265
|
return tmux(respawnPaneArgs(opts)).ok;
|
|
301
266
|
}
|
|
302
|
-
/** @deprecated Use respawnPaneDetached. Retained so existing refresh-yield
|
|
303
|
-
* callers stay green while the placement layer migrates onto the explicit
|
|
304
|
-
* sync/detached split. */
|
|
305
|
-
export function respawnPane(opts) {
|
|
306
|
-
return respawnPaneDetached(opts);
|
|
307
|
-
}
|
|
308
267
|
// ---------------------------------------------------------------------------
|
|
309
268
|
// pi command assembly
|
|
310
269
|
// ---------------------------------------------------------------------------
|
|
@@ -359,34 +318,8 @@ export function switchClient(session) {
|
|
|
359
318
|
return tmux(['switch-client', '-t', session]).ok;
|
|
360
319
|
}
|
|
361
320
|
// ---------------------------------------------------------------------------
|
|
362
|
-
//
|
|
321
|
+
// send-keys (chrome — used by the prefix menu + nav bindings)
|
|
363
322
|
// ---------------------------------------------------------------------------
|
|
364
|
-
/** Move a source pane into a destination window (`tmux join-pane`). The source
|
|
365
|
-
* pane's running process (e.g. a child's live pi) is preserved; its now-empty
|
|
366
|
-
* source window auto-closes. Best-effort; false if tmux fails. */
|
|
367
|
-
export function joinPane(srcPane, dstWindow) {
|
|
368
|
-
return tmux(['join-pane', '-s', srcPane, '-t', dstWindow]).ok;
|
|
369
|
-
}
|
|
370
|
-
/** Apply a named tmux layout to a window (`tmux select-layout`). Use
|
|
371
|
-
* `main-vertical` for one wide pane on the left + the rest stacked right.
|
|
372
|
-
* Best-effort; never throws. */
|
|
373
|
-
export function selectLayout(window, layout) {
|
|
374
|
-
return tmux(['select-layout', '-t', window, layout]).ok;
|
|
375
|
-
}
|
|
376
|
-
/** Set a tmux window option (`tmux set-window-option`). Used to size the main
|
|
377
|
-
* pane (`main-pane-width`) before a main-vertical layout. Best-effort. */
|
|
378
|
-
export function setWindowOption(window, name, value) {
|
|
379
|
-
return tmux(['set-window-option', '-t', window, name, value]).ok;
|
|
380
|
-
}
|
|
381
|
-
/** Toggle `remain-on-exit` on a window (§1.5 F3). `on` keeps a focus pane on
|
|
382
|
-
* screen after its pi exits — the viewport survives (F1), the final transcript
|
|
383
|
-
* is preserved, and `respawn-pane -k` can resurrect the node into the SAME pane
|
|
384
|
-
* id. NOTE (§1.5/§2.5, spike-confirmed): a dead/frozen pane is reaped only by
|
|
385
|
-
* `kill-pane`/`respawn-pane`, NEVER by toggling this off — the toggle does not
|
|
386
|
-
* reap an already-dead pane. Best-effort; never throws. */
|
|
387
|
-
export function setRemainOnExit(window, on) {
|
|
388
|
-
return tmux(['set-window-option', '-t', window, 'remain-on-exit', on ? 'on' : 'off']).ok;
|
|
389
|
-
}
|
|
390
323
|
/** Type a literal (e.g. a `/graph` slash command) into a pane and press Enter
|
|
391
324
|
* (`tmux send-keys -t <pane> '<text>' Enter`). Requires the pane's editor be
|
|
392
325
|
* empty, same limitation as the menu's `/promote` item. Best-effort. */
|
|
@@ -401,7 +334,7 @@ export function sendKeysEnter(pane, text) {
|
|
|
401
334
|
// ---------------------------------------------------------------------------
|
|
402
335
|
/** Reserved mnemonic keys owned by the built-in menu items below — a custom
|
|
403
336
|
* `prefixBind` may not claim these (the built-in item wins). */
|
|
404
|
-
const RESERVED_MENU_KEYS = new Set(['o', 'r', 'd', 'D', 'x'
|
|
337
|
+
const RESERVED_MENU_KEYS = new Set(['o', 'r', 'd', 'D', 'x']);
|
|
405
338
|
/** Bind Alt+C to the crouter action menu. Best-effort; false if tmux fails.
|
|
406
339
|
* The built-in items (promote/demote/detach/close/browse) are static; the canvas-nav
|
|
407
340
|
* chords (graph/manager/expand/report-N + any custom prefixBind) are appended
|
|
@@ -409,7 +342,6 @@ const RESERVED_MENU_KEYS = new Set(['o', 'r', 'd', 'D', 'x', 'b']);
|
|
|
409
342
|
* the `__graph__` sentinel, a `send-keys '/graph'`) so the menu stays static
|
|
410
343
|
* while behaviour is config-driven. */
|
|
411
344
|
export function installMenuBinding() {
|
|
412
|
-
const sess = nodeSession();
|
|
413
345
|
const title = ' crtr ';
|
|
414
346
|
const items = [
|
|
415
347
|
// Promote types `/promote` into the agent's pane rather than shelling out:
|
|
@@ -423,15 +355,13 @@ export function installMenuBinding() {
|
|
|
423
355
|
// `d` runs `node demote`: flip the agent to TERMINAL in place — no finalize,
|
|
424
356
|
// no kill — it keeps running where it is, and because it is now terminal it
|
|
425
357
|
// is forced to push a final up the spine when it finishes. `D` runs `node
|
|
426
|
-
// demote --detach`, which ALSO
|
|
427
|
-
//
|
|
358
|
+
// demote --detach`, which ALSO closes the agent's viewer pane (frees the
|
|
359
|
+
// pane; the detached broker keeps generating). Neither ends it.
|
|
428
360
|
{ name: 'demote to terminal', key: 'd', cmd: `run-shell "crtr node demote --pane '#{pane_id}' >/dev/null 2>&1"` },
|
|
429
361
|
{ name: 'detach to background', key: 'D', cmd: `run-shell "crtr node demote --pane '#{pane_id}' --detach >/dev/null 2>&1"` },
|
|
430
362
|
// Close cascades down the subscribes_to spine (kills the subtree's windows,
|
|
431
363
|
// marks them canceled); revivable. Output discarded — the keypress just acts.
|
|
432
364
|
{ name: 'close agent + subtree', key: 'x', cmd: `run-shell "crtr node close --pane '#{pane_id}' >/dev/null 2>&1"` },
|
|
433
|
-
// Re-keyed g→b so `g` is free for the canvas-nav GRAPH toggle (below).
|
|
434
|
-
{ name: 'browse background agents', key: 'b', cmd: `switch-client -t ${sess}` },
|
|
435
365
|
];
|
|
436
366
|
// Canvas-nav chords from config (default: g→graph, m→manager, e→expand). The
|
|
437
367
|
// `__graph__` sentinel toggles the in-pi GRAPH modal via send-keys; every
|
|
@@ -461,6 +391,17 @@ export function installMenuBinding() {
|
|
|
461
391
|
cmd: `run-shell "crtr canvas chord --pane '#{pane_id}' --key ${n} >/dev/null 2>&1"`,
|
|
462
392
|
});
|
|
463
393
|
}
|
|
394
|
+
// Alt+C TOGGLE: re-pressing Alt+C while the menu is open should DISMISS it.
|
|
395
|
+
// display-menu is modal, so the root `M-c` binding is shadowed while the menu
|
|
396
|
+
// is up; tmux instead assembles the re-pressed Esc+c into the meta key `M-c`
|
|
397
|
+
// and delivers it to the menu's own key handler. An unhandled key is ignored
|
|
398
|
+
// (the menu just stays open) and the trailing `c` never leaks to the editor
|
|
399
|
+
// (verified empirically in tmux 3.6b: no native ESC-cancel + c-leak occurs for
|
|
400
|
+
// an atomic keypress). We catch that `M-c` with a menu item keyed to it whose
|
|
401
|
+
// command is empty — selecting it runs nothing and closes the menu. The row
|
|
402
|
+
// doubles as the menu's self-documenting dismiss hint. Placed last so it reads
|
|
403
|
+
// as chrome below the actions; `M-c` collides with no mnemonic above.
|
|
404
|
+
items.push({ name: 'close menu', key: 'M-c', cmd: '' });
|
|
464
405
|
// tmux's -x sets the menu's LEFT edge. To sit the box INSIDE the pane's
|
|
465
406
|
// top-right corner, shift x left by the box width (longest line + tmux chrome:
|
|
466
407
|
// borders + padding + the right-aligned mnemonic-key column) via format math.
|
|
@@ -472,6 +413,11 @@ export function installMenuBinding() {
|
|
|
472
413
|
const args = [
|
|
473
414
|
'bind-key', '-n', 'M-c', 'display-menu',
|
|
474
415
|
'-T', `#[align=centre]${title}`,
|
|
416
|
+
// Frame the menu on the theme's distinct-surface background so it doesn't
|
|
417
|
+
// blend into the pane behind it (CTO ruling). Empty in the front door (theme
|
|
418
|
+
// not loaded there); the attach viewer re-installs this with the style once
|
|
419
|
+
// it has themed — see attach-cmd.ts.
|
|
420
|
+
...surfaceTmuxStyleArgs(),
|
|
475
421
|
'-x', `#{e|-:#{pane_right},${boxW + nudgeX}}`,
|
|
476
422
|
'-y', `#{e|+:#{pane_top},${nudgeY}}`,
|
|
477
423
|
];
|
package/dist/core/scope.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export declare function requireScopeRoot(scope: Scope): string;
|
|
|
9
9
|
export declare function ensureProjectScopeRoot(startDir?: string): string;
|
|
10
10
|
export declare function pluginsDir(scope: Scope): string | null;
|
|
11
11
|
export declare function marketplacesDir(scope: Scope): string | null;
|
|
12
|
-
export declare function scopeSkillsDir(scope: Scope): string | null;
|
|
13
12
|
/** Where substrate memory documents live per scope. Builtin memory is the
|
|
14
13
|
* special case — its own package dir (builtinMemoryRoot), since the builtin
|
|
15
14
|
* scope has no scopeRoot — mirroring viewsDir's builtin handling. User and
|
|
@@ -20,7 +19,7 @@ export declare function scopeMemoryDir(scope: Scope): string | null;
|
|
|
20
19
|
export declare function pluginMemoryDir(plugin: InstalledPlugin): string;
|
|
21
20
|
/** Where view definition dirs live per scope. Builtin views sit directly under
|
|
22
21
|
* builtinViewsRoot() (no `views/` segment — they ARE the builtin views dir),
|
|
23
|
-
* matching the loader's `<root>/<name>/
|
|
22
|
+
* matching the loader's `<root>/<name>/core.mjs` resolution. */
|
|
24
23
|
export declare function viewsDir(scope: Scope): string | null;
|
|
25
24
|
export declare function resolveScopeArg(scopeArg: string | undefined): Scope | 'all';
|
|
26
25
|
export declare function listScopes(scopeArg: string | undefined): Scope[];
|
package/dist/core/scope.js
CHANGED
|
@@ -87,10 +87,6 @@ export function marketplacesDir(scope) {
|
|
|
87
87
|
const root = scopeRoot(scope);
|
|
88
88
|
return root ? join(root, 'marketplaces') : null;
|
|
89
89
|
}
|
|
90
|
-
export function scopeSkillsDir(scope) {
|
|
91
|
-
const root = scopeRoot(scope);
|
|
92
|
-
return root ? join(root, 'skills') : null;
|
|
93
|
-
}
|
|
94
90
|
/** Where substrate memory documents live per scope. Builtin memory is the
|
|
95
91
|
* special case — its own package dir (builtinMemoryRoot), since the builtin
|
|
96
92
|
* scope has no scopeRoot — mirroring viewsDir's builtin handling. User and
|
|
@@ -108,7 +104,7 @@ export function pluginMemoryDir(plugin) {
|
|
|
108
104
|
}
|
|
109
105
|
/** Where view definition dirs live per scope. Builtin views sit directly under
|
|
110
106
|
* builtinViewsRoot() (no `views/` segment — they ARE the builtin views dir),
|
|
111
|
-
* matching the loader's `<root>/<name>/
|
|
107
|
+
* matching the loader's `<root>/<name>/core.mjs` resolution. */
|
|
112
108
|
export function viewsDir(scope) {
|
|
113
109
|
if (scope === 'builtin')
|
|
114
110
|
return builtinViewsRoot();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Regression test for the `--dry-run` `wrote` count (found by P8 integration).
|
|
2
|
+
//
|
|
3
|
+
// Bug: under `--dry-run`, reconcilePair returned `wrote: counts.writes` — the
|
|
4
|
+
// WOULD-BE endpoint write total — even though nothing is written to disk. That
|
|
5
|
+
// contradicts the `sys sync -h` contract ("wrote … 0 under --dry-run") and the
|
|
6
|
+
// rendered "DRY RUN (nothing written)" header, which together promise 0. The
|
|
7
|
+
// fix makes `wrote` reflect ACTUAL writes (0 under dry-run) while still using
|
|
8
|
+
// the would-be counts internally to decide synced-vs-noop.
|
|
9
|
+
//
|
|
10
|
+
// This test FAILS on the pre-fix code (wrote === number of files that WOULD be
|
|
11
|
+
// written, e.g. 2 for a one-sided bootstrap) and PASSES on the current code.
|
|
12
|
+
//
|
|
13
|
+
// Run: node --import tsx/esm --test src/core/skill-sync/__tests__/dry-run-wrote-count.test.ts
|
|
14
|
+
import { test, before, after } from 'node:test';
|
|
15
|
+
import assert from 'node:assert/strict';
|
|
16
|
+
import { mkdtempSync, rmSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
17
|
+
import { tmpdir, homedir } from 'node:os';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { reconcilePair } from '../engine.js';
|
|
20
|
+
import { DEFAULT_PROFILE } from '../profile.js';
|
|
21
|
+
// The engine resolves user-scope endpoints + the snapshot store off homedir(),
|
|
22
|
+
// which reads $HOME at call time — so isolating $HOME isolates everything.
|
|
23
|
+
let home;
|
|
24
|
+
let prevHome;
|
|
25
|
+
const PAIR = {
|
|
26
|
+
id: 'dryrun-regression',
|
|
27
|
+
crtr: { scope: 'user', name: 'dr' },
|
|
28
|
+
claude: { scope: 'user', name: 'dr' },
|
|
29
|
+
};
|
|
30
|
+
before(() => {
|
|
31
|
+
prevHome = process.env.HOME;
|
|
32
|
+
home = mkdtempSync(join(tmpdir(), 'crtr-sync-dryrun-'));
|
|
33
|
+
process.env.HOME = home;
|
|
34
|
+
// One-sided bootstrap: only the crtr side exists. A real sync here would
|
|
35
|
+
// materialize the Claude side + write the snapshot (2 endpoint files written).
|
|
36
|
+
const crtrDir = join(home, '.crouter', 'memory', 'dr');
|
|
37
|
+
mkdirSync(crtrDir, { recursive: true });
|
|
38
|
+
writeFileSync(join(crtrDir, 'SKILL.md'), '---\nkind: knowledge\nwhen-and-why-to-read: "When the regression applies, this doc should be read."\n---\n# DR\nbody\n', 'utf8');
|
|
39
|
+
});
|
|
40
|
+
after(() => {
|
|
41
|
+
if (prevHome === undefined)
|
|
42
|
+
delete process.env.HOME;
|
|
43
|
+
else
|
|
44
|
+
process.env.HOME = prevHome;
|
|
45
|
+
rmSync(home, { recursive: true, force: true });
|
|
46
|
+
assert.equal(homedir(), prevHome ?? homedir()); // sanity: $HOME restored
|
|
47
|
+
});
|
|
48
|
+
test('--dry-run reports wrote === 0 and writes nothing to disk', () => {
|
|
49
|
+
const res = reconcilePair(PAIR, DEFAULT_PROFILE, { dryRun: true });
|
|
50
|
+
// Status still reflects that a real run WOULD sync this pair...
|
|
51
|
+
assert.equal(res.status, 'synced', 'dry-run should still classify the pending change as synced');
|
|
52
|
+
// ...but the `wrote` count is ACTUAL writes — zero under --dry-run.
|
|
53
|
+
assert.equal(res.wrote, 0, 'dry-run must report wrote === 0 (contract: 0 under --dry-run)');
|
|
54
|
+
// And nothing was actually written: no Claude bundle, no snapshot.
|
|
55
|
+
assert.ok(!existsSync(join(home, '.claude', 'skills', 'dr', 'SKILL.md')), 'dry-run must not materialize the Claude endpoint');
|
|
56
|
+
assert.ok(!existsSync(join(home, '.crouter', 'skill-sync', 'snapshots', PAIR.id)), 'dry-run must not write the snapshot base');
|
|
57
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in export registry for the skill-sync subsystem.
|
|
3
|
+
*
|
|
4
|
+
* These are the crtr-*generated* host artifacts — the `crtr-skills` boot skill
|
|
5
|
+
* and any CLI slash-commands. Unlike the user-authored bidirectional manifest
|
|
6
|
+
* pairs (engine.ts), an export pair has a single source of truth (code), so it
|
|
7
|
+
* is one-way: render → marker-guarded write (see export.ts). The generators
|
|
8
|
+
* (`bootSkillBody`, `renderSlashTemplate`) and their version markers were moved
|
|
9
|
+
* here verbatim from bootstrap.ts when skill-sync became the sole host writer —
|
|
10
|
+
* marker strings are byte-identical so already-deployed artifacts roll forward.
|
|
11
|
+
*/
|
|
12
|
+
import type { RootDef } from '../command.js';
|
|
13
|
+
/** A host runtime crtr exports generated artifacts into. */
|
|
14
|
+
export type Host = 'claude' | 'pi';
|
|
15
|
+
/** One destination for an export artifact. Only targets whose host root exists
|
|
16
|
+
* are written — crtr never creates `~/.claude` or `~/.pi` itself (export.ts). */
|
|
17
|
+
export interface HostTarget {
|
|
18
|
+
host: Host;
|
|
19
|
+
/** The leaf dir the artifact is written into (never the host root). */
|
|
20
|
+
dir: () => string;
|
|
21
|
+
/** `bundle` → `<dir>/<name>/SKILL.md` ; `file` → `<dir>/<name>.md`. */
|
|
22
|
+
layout: 'bundle' | 'file';
|
|
23
|
+
}
|
|
24
|
+
/** One crtr-generated export artifact and every host it reaches. */
|
|
25
|
+
export interface ExportPair {
|
|
26
|
+
/** Stable id for logging, e.g. `@boot/crtr-skills` or `@cmd/<name>`. */
|
|
27
|
+
id: string;
|
|
28
|
+
/** On-disk artifact name (bundle dir name or file stem). */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Marker prefix identifying a crtr-owned file (the clobber guard). */
|
|
31
|
+
markerPrefix: string;
|
|
32
|
+
/** Full file content incl. frontmatter + version marker. */
|
|
33
|
+
render: () => string;
|
|
34
|
+
/** Every host destination. Targets with an absent host root are skipped. */
|
|
35
|
+
targets: HostTarget[];
|
|
36
|
+
}
|
|
37
|
+
export declare const BOOT_SKILL_MARKER_PREFIX = "<!-- crtr-boot-skill v";
|
|
38
|
+
export declare const SLASH_CMD_MARKER_PREFIX = "<!-- crtr-mode-cmd v";
|
|
39
|
+
/** The crtr-generated export artifacts for this command tree: the boot skill
|
|
40
|
+
* (both hosts, bundle layout) plus one pair per opted-in slash command (both
|
|
41
|
+
* hosts, file layout). */
|
|
42
|
+
export declare function builtinExportPairs(root: RootDef): ExportPair[];
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in export registry for the skill-sync subsystem.
|
|
3
|
+
*
|
|
4
|
+
* These are the crtr-*generated* host artifacts — the `crtr-skills` boot skill
|
|
5
|
+
* and any CLI slash-commands. Unlike the user-authored bidirectional manifest
|
|
6
|
+
* pairs (engine.ts), an export pair has a single source of truth (code), so it
|
|
7
|
+
* is one-way: render → marker-guarded write (see export.ts). The generators
|
|
8
|
+
* (`bootSkillBody`, `renderSlashTemplate`) and their version markers were moved
|
|
9
|
+
* here verbatim from bootstrap.ts when skill-sync became the sole host writer —
|
|
10
|
+
* marker strings are byte-identical so already-deployed artifacts roll forward.
|
|
11
|
+
*/
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
import { collectSlashSpecs } from '../command.js';
|
|
15
|
+
// ── Boot skill ───────────────────────────────────────────────────────────────
|
|
16
|
+
const BOOT_SKILL_NAME = 'crtr-skills';
|
|
17
|
+
const BOOT_SKILL_MARKER = '<!-- crtr-boot-skill v2 -->';
|
|
18
|
+
export const BOOT_SKILL_MARKER_PREFIX = '<!-- crtr-boot-skill v';
|
|
19
|
+
function bootSkillBody() {
|
|
20
|
+
return `---
|
|
21
|
+
name: crtr-skills
|
|
22
|
+
description: 'Author, list, search, and load crtr memory documents (knowledge, preferences) via the crtr CLI. These are durable agent memory — markdown future LLM sessions load on demand. Use when the user wants to remember/save knowledge, build a context primer, or recall a previously saved doc. Triggers: "save", "remember", "build context for", "what skills do we have", "skill for X".'
|
|
23
|
+
argument-hint: [topic or verb]
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
${BOOT_SKILL_MARKER}
|
|
27
|
+
|
|
28
|
+
# /crtr-skills — crtr memory router
|
|
29
|
+
|
|
30
|
+
crtr memory documents = durable agent memory (kind: knowledge or preference).
|
|
31
|
+
Written for **future LLM sessions**, not the user. \`crtr memory\` is the
|
|
32
|
+
surface — author, discover, and load them.
|
|
33
|
+
|
|
34
|
+
## Route by intent
|
|
35
|
+
|
|
36
|
+
- **Capture** ("save", "remember", "build context for", "make a skill"):
|
|
37
|
+
\`crtr memory write <name> --kind <knowledge|preference>\` with the
|
|
38
|
+
body piped on stdin — e.g.
|
|
39
|
+
\`crtr memory write $ARGUMENTS --kind knowledge <<'EOF' … EOF\`. Run
|
|
40
|
+
\`crtr memory write -h\` for the full frontmatter schema
|
|
41
|
+
(--when-and-why-to-read, --short-form, --scope, visibility rungs), then
|
|
42
|
+
\`crtr memory lint\` to validate what you wrote.
|
|
43
|
+
- **Find** ("what do we have on X"): \`crtr memory find "$ARGUMENTS"\` →
|
|
44
|
+
\`crtr memory read <name>\` on the best hit (add --body/--grep to search
|
|
45
|
+
bodies).
|
|
46
|
+
- **Load by name**: \`crtr memory read <name>\`.
|
|
47
|
+
- **List all**: \`crtr memory list\`.
|
|
48
|
+
|
|
49
|
+
If \`$ARGUMENTS\` is empty, ask the user what they want before running.
|
|
50
|
+
|
|
51
|
+
## Rules
|
|
52
|
+
|
|
53
|
+
- CLI stdout is the prompt — act on it, don't paraphrase to the user.
|
|
54
|
+
- Append \`-h\` at any leaf (\`crtr memory write -h\`) for its full schema
|
|
55
|
+
before authoring.
|
|
56
|
+
- If \`crtr\` is not on PATH, tell the user and stop.
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
// ── Slash commands ───────────────────────────────────────────────────────────
|
|
60
|
+
const SLASH_CMD_MARKER = '<!-- crtr-mode-cmd v1 -->';
|
|
61
|
+
export const SLASH_CMD_MARKER_PREFIX = '<!-- crtr-mode-cmd v';
|
|
62
|
+
/** Render a SlashSpec to a full template file (frontmatter + marker + body). */
|
|
63
|
+
function renderSlashTemplate(spec) {
|
|
64
|
+
const hint = spec.argumentHint !== undefined
|
|
65
|
+
? `argument-hint: ${JSON.stringify(spec.argumentHint)}\n`
|
|
66
|
+
: '';
|
|
67
|
+
return `---\ndescription: ${spec.description}\n${hint}---\n\n${SLASH_CMD_MARKER}\n\n${spec.body}\n`;
|
|
68
|
+
}
|
|
69
|
+
// ── Host roots ───────────────────────────────────────────────────────────────
|
|
70
|
+
function claudeSkillsRoot() {
|
|
71
|
+
return join(homedir(), '.claude', 'skills');
|
|
72
|
+
}
|
|
73
|
+
function piSkillsRoot() {
|
|
74
|
+
return join(homedir(), '.pi', 'agent', 'skills');
|
|
75
|
+
}
|
|
76
|
+
function claudeCommandsRoot() {
|
|
77
|
+
return join(homedir(), '.claude', 'commands');
|
|
78
|
+
}
|
|
79
|
+
function piPromptsRoot() {
|
|
80
|
+
return join(homedir(), '.pi', 'agent', 'prompts');
|
|
81
|
+
}
|
|
82
|
+
// ── Registry ─────────────────────────────────────────────────────────────────
|
|
83
|
+
/** The crtr-generated export artifacts for this command tree: the boot skill
|
|
84
|
+
* (both hosts, bundle layout) plus one pair per opted-in slash command (both
|
|
85
|
+
* hosts, file layout). */
|
|
86
|
+
export function builtinExportPairs(root) {
|
|
87
|
+
const pairs = [
|
|
88
|
+
{
|
|
89
|
+
id: '@boot/crtr-skills',
|
|
90
|
+
name: BOOT_SKILL_NAME,
|
|
91
|
+
markerPrefix: BOOT_SKILL_MARKER_PREFIX,
|
|
92
|
+
render: bootSkillBody,
|
|
93
|
+
targets: [
|
|
94
|
+
{ host: 'claude', dir: claudeSkillsRoot, layout: 'bundle' },
|
|
95
|
+
{ host: 'pi', dir: piSkillsRoot, layout: 'bundle' },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
for (const spec of collectSlashSpecs(root)) {
|
|
100
|
+
pairs.push({
|
|
101
|
+
id: `@cmd/${spec.name}`,
|
|
102
|
+
name: spec.name,
|
|
103
|
+
markerPrefix: SLASH_CMD_MARKER_PREFIX,
|
|
104
|
+
render: () => renderSlashTemplate(spec),
|
|
105
|
+
targets: [
|
|
106
|
+
{ host: 'claude', dir: claudeCommandsRoot, layout: 'file' },
|
|
107
|
+
{ host: 'pi', dir: piPromptsRoot, layout: 'file' },
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return pairs;
|
|
112
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude-plugin endpoint resolution for skill-sync.
|
|
3
|
+
*
|
|
4
|
+
* The `claude-plugin` endpoint scope addresses a skill IN PLACE at the path
|
|
5
|
+
* where Claude actually installs the owning plugin — `<installPath>/skills/<name>`
|
|
6
|
+
* — instead of forcing a copy under `~/.claude/skills/`. The install path is read
|
|
7
|
+
* from Claude's OWN registry (`~/.claude/plugins/installed_plugins.json`), keyed
|
|
8
|
+
* by the marketplace-qualified plugin key (`<plugin>@<marketplace>`), gated on the
|
|
9
|
+
* enable state in `~/.claude/settings.json` → `enabledPlugins`.
|
|
10
|
+
*
|
|
11
|
+
* This is Claude's registry, deliberately NOT crtr's plugin registry
|
|
12
|
+
* (`resolver.ts` / `findPluginByName`): the two are independent installs.
|
|
13
|
+
*
|
|
14
|
+
* Strictness (no lenient fallback): a key that is unknown, disabled, or whose
|
|
15
|
+
* recorded install path is missing on disk is a HARD error naming it.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a Claude plugin's on-disk install path from its marketplace-qualified
|
|
19
|
+
* key (`<plugin>@<marketplace>`, e.g. `devcore@crouton-kit`). Throws naming the
|
|
20
|
+
* key when it is not installed, not enabled, or has no install path present on
|
|
21
|
+
* disk.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveClaudePluginInstallPath(key: string): string;
|