@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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const bridgeBranch: import("../../core/command.js").BranchDef;
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
// `crtr pkg bridge` — generate crouter substrate docs from a Claude skills root
|
|
2
|
-
// and file-level reconcile the bridge plugin (design §2). The mapping lives in
|
|
3
|
-
// core/bridge-map.ts (one testable source of truth); the extension shells to
|
|
4
|
-
// this command rather than duplicating the transform.
|
|
5
|
-
import { join, basename, dirname, isAbsolute } from 'node:path';
|
|
6
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
7
|
-
import { defineBranch, defineLeaf } from '../../core/command.js';
|
|
8
|
-
import { usage } from '../../core/errors.js';
|
|
9
|
-
import { userScopeRoot } from '../../core/scope.js';
|
|
10
|
-
import { ensureDir, writeJson, writeText, readText, removePath, walkFiles, } from '../../core/fs-utils.js';
|
|
11
|
-
import { parseFrontmatter, parseFrontmatterGeneric } from '../../core/frontmatter.js';
|
|
12
|
-
import { buildBridgeDoc } from '../../core/bridge-map.js';
|
|
13
|
-
import { PLUGIN_MANIFEST_DIR, PLUGIN_MANIFEST_FILE } from '../../types.js';
|
|
14
|
-
/** Enumerate `<source>/**\/SKILL.md`, stopping recursion at the first SKILL.md
|
|
15
|
-
* on any branch — a dir that holds SKILL.md is a skill; its subdirs are assets,
|
|
16
|
-
* not nested skills. */
|
|
17
|
-
function enumerateSkillFiles(root) {
|
|
18
|
-
const out = [];
|
|
19
|
-
if (!existsSync(root))
|
|
20
|
-
return out;
|
|
21
|
-
const stack = [root];
|
|
22
|
-
while (stack.length) {
|
|
23
|
-
const dir = stack.pop();
|
|
24
|
-
let entries;
|
|
25
|
-
try {
|
|
26
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
const hasSkill = entries.some((e) => e.isFile() && e.name === 'SKILL.md');
|
|
32
|
-
if (hasSkill) {
|
|
33
|
-
out.push(join(dir, 'SKILL.md'));
|
|
34
|
-
continue; // stop descent — deeper dirs are assets
|
|
35
|
-
}
|
|
36
|
-
for (const e of entries)
|
|
37
|
-
if (e.isDirectory())
|
|
38
|
-
stack.push(join(dir, e.name));
|
|
39
|
-
}
|
|
40
|
-
return out;
|
|
41
|
-
}
|
|
42
|
-
const bridgeSync = defineLeaf({
|
|
43
|
-
name: 'sync',
|
|
44
|
-
description: 'generate substrate docs from a Claude skills root',
|
|
45
|
-
whenToUse: 'mirroring a Claude Code skills directory into a crtr bridge plugin: maps each SKILL.md to a substrate memory doc and file-level reconciles the plugin (regenerate, prune stale, preserve hand-authored leaves + INDEX)',
|
|
46
|
-
help: {
|
|
47
|
-
name: 'pkg bridge sync',
|
|
48
|
-
summary: 'mirror a Claude skills root into a crtr bridge plugin (idempotent reconcile)',
|
|
49
|
-
guide: 'Each <source>/skills/<skillname>/SKILL.md becomes ~/.crouter/plugins/<name>/memory/<skillname>.md, stamped generator:claude-bridge. Stamped leaves whose source skill is gone are pruned; hand-authored leaves and any INDEX.md are never touched.',
|
|
50
|
-
params: [
|
|
51
|
-
{ kind: 'flag', name: 'source', type: 'path', required: true, constraint: 'Absolute path to a Claude skills root (a dir whose <skillname>/SKILL.md leaves are bridged; recursion stops at the first SKILL.md).' },
|
|
52
|
-
{ kind: 'flag', name: 'name', type: 'string', required: true, constraint: 'Bridge plugin name. Dest plugin dir is ~/.crouter/plugins/<name>/.' },
|
|
53
|
-
{ kind: 'flag', name: 'source-desc', type: 'string', required: false, constraint: 'Optional plugin description written into the generated plugin.json.' },
|
|
54
|
-
],
|
|
55
|
-
output: [
|
|
56
|
-
{ name: 'plugin', type: 'string', required: true, constraint: 'Bridge plugin name.' },
|
|
57
|
-
{ name: 'path', type: 'string', required: true, constraint: 'Absolute path to the bridge plugin directory.' },
|
|
58
|
-
{ name: 'written', type: 'number', required: true, constraint: 'Count of substrate leaves generated from source SKILL.md files.' },
|
|
59
|
-
{ name: 'pruned', type: 'number', required: true, constraint: 'Count of stale generator:claude-bridge leaves deleted (source skill gone).' },
|
|
60
|
-
{ name: 'preserved', type: 'number', required: true, constraint: 'Count of untouched protected leaves (INDEX.md or unstamped hand-authored docs).' },
|
|
61
|
-
],
|
|
62
|
-
outputKind: 'object',
|
|
63
|
-
effects: ['Writes ~/.crouter/plugins/<name>/.crouter-plugin/plugin.json and memory/*.md. Deletes stale generator:claude-bridge leaves. Never writes or deletes INDEX.md or unstamped docs.'],
|
|
64
|
-
},
|
|
65
|
-
run: async (input) => {
|
|
66
|
-
const source = input['source'];
|
|
67
|
-
const name = input['name'];
|
|
68
|
-
const sourceDesc = input['sourceDesc'];
|
|
69
|
-
if (!isAbsolute(source)) {
|
|
70
|
-
throw usage(`--source must be an absolute path: ${source}`);
|
|
71
|
-
}
|
|
72
|
-
if (!existsSync(source)) {
|
|
73
|
-
throw usage(`--source does not exist: ${source}`);
|
|
74
|
-
}
|
|
75
|
-
const destRoot = join(userScopeRoot(), 'plugins', name);
|
|
76
|
-
const memoryDir = join(destRoot, 'memory');
|
|
77
|
-
// 1. Ensure the bridge plugin manifest.
|
|
78
|
-
const manifestDir = join(destRoot, PLUGIN_MANIFEST_DIR);
|
|
79
|
-
ensureDir(manifestDir);
|
|
80
|
-
const manifest = {
|
|
81
|
-
name,
|
|
82
|
-
version: '0.0.0',
|
|
83
|
-
...(sourceDesc !== undefined ? { description: sourceDesc } : {}),
|
|
84
|
-
source,
|
|
85
|
-
generator: 'claude-bridge',
|
|
86
|
-
};
|
|
87
|
-
writeJson(join(manifestDir, PLUGIN_MANIFEST_FILE), manifest);
|
|
88
|
-
// 2 + 3. Regenerate one stamped leaf per source skill.
|
|
89
|
-
ensureDir(memoryDir);
|
|
90
|
-
const writtenPaths = new Set();
|
|
91
|
-
for (const file of enumerateSkillFiles(source)) {
|
|
92
|
-
const skillName = basename(dirname(file));
|
|
93
|
-
const parsed = parseFrontmatter(readText(file));
|
|
94
|
-
const doc = buildBridgeDoc({
|
|
95
|
-
name: parsed.data?.name ?? skillName,
|
|
96
|
-
description: parsed.data?.description ?? '',
|
|
97
|
-
body: parsed.body,
|
|
98
|
-
});
|
|
99
|
-
const dest = join(memoryDir, `${skillName}.md`);
|
|
100
|
-
writeText(dest, doc);
|
|
101
|
-
writtenPaths.add(dest);
|
|
102
|
-
}
|
|
103
|
-
const written = writtenPaths.size;
|
|
104
|
-
// 4. Reconcile: prune stale stamped leaves; never touch INDEX.md or
|
|
105
|
-
// unstamped (hand-authored) leaves.
|
|
106
|
-
let pruned = 0;
|
|
107
|
-
let preserved = 0;
|
|
108
|
-
for (const f of walkFiles(memoryDir, (n) => n.endsWith('.md'))) {
|
|
109
|
-
if (writtenPaths.has(f))
|
|
110
|
-
continue; // just regenerated → counted in `written`
|
|
111
|
-
if (basename(f) === 'INDEX.md') {
|
|
112
|
-
preserved++;
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
const generator = parseFrontmatterGeneric(readText(f)).data?.['generator'];
|
|
116
|
-
if (generator === 'claude-bridge') {
|
|
117
|
-
removePath(f);
|
|
118
|
-
pruned++;
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
preserved++;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return { plugin: name, path: destRoot, written, pruned, preserved };
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
export const bridgeBranch = defineBranch({
|
|
128
|
-
name: 'bridge',
|
|
129
|
-
description: 'mirror Claude skills into crtr bridge plugins',
|
|
130
|
-
whenToUse: 'generating a crtr bridge plugin from a Claude Code skills root — the substrate-doc mapping and file-level reconcile invoked by the pi bridge extension',
|
|
131
|
-
help: {
|
|
132
|
-
name: 'pkg bridge',
|
|
133
|
-
summary: 'mirror Claude skills roots into crtr bridge plugins',
|
|
134
|
-
model: 'A bridge plugin is a generator:claude-bridge plugin whose memory/ docs are generated from a Claude skills root. Hand-authored leaves and INDEX ceilings are preserved across syncs.',
|
|
135
|
-
},
|
|
136
|
-
children: [bridgeSync],
|
|
137
|
-
});
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { dirname } from 'node:path';
|
|
2
|
-
import { writeFileSync } from 'node:fs';
|
|
3
|
-
import { defineLeaf, defineBranch } from '../../core/command.js';
|
|
4
|
-
import { usage, general, notFound } from '../../core/errors.js';
|
|
5
|
-
import { SKILL_TYPES, isSkillType } from '../../types.js';
|
|
6
|
-
import { parseSkillQualifier, findPluginByName, } from '../../core/resolver.js';
|
|
7
|
-
import { resolveScopeArg, requireScopeRoot, scopeMemoryDir, pluginMemoryDir, } from '../../core/scope.js';
|
|
8
|
-
import { ensureScopeInitialized } from '../../core/config.js';
|
|
9
|
-
import { ensureDir, pathExists } from '../../core/fs-utils.js';
|
|
10
|
-
import { skillCreatePrompt, skillTemplatePrompt } from '../../prompts/skill.js';
|
|
11
|
-
import { memoryFilePath, serializeMemoryDoc } from '../memory/shared.js';
|
|
12
|
-
import { VALID_TYPES, resolveWriteScope } from './shared.js';
|
|
13
|
-
export const authorGuide = defineLeaf({
|
|
14
|
-
name: 'guide',
|
|
15
|
-
description: 'load authoring workflow + skeleton for a type',
|
|
16
|
-
whenToUse: 'REQUIRED reading before you author a new skill OR edit an existing one — it carries the SKILL.md format, the description-drives-discovery rule (when-to-use lives in the frontmatter description, never the body), the voice constraints, and the per-type workflow. Editing an existing skill counts: read this first, because the format and voice rules govern every change, not just new files.',
|
|
17
|
-
help: {
|
|
18
|
-
name: 'skill author guide',
|
|
19
|
-
summary: 'load the skill authoring workflow — two stages: omit type to pick one, pass type for its full skeleton',
|
|
20
|
-
params: [
|
|
21
|
-
{ kind: 'flag', name: 'type', type: 'enum', choices: [...VALID_TYPES], required: false, constraint: 'OMIT to receive the template-picker guide first; pass on the second call for that type\'s full workflow + skeleton.' },
|
|
22
|
-
{ kind: 'flag', name: 'topic', type: 'string', required: false, constraint: 'Optional topic context injected into the guide.' },
|
|
23
|
-
],
|
|
24
|
-
output: [
|
|
25
|
-
{ name: 'guide', type: 'string', required: true, constraint: 'Stage 1 (no type): the template-picker workflow. Stage 2 (type given): that type\'s authoring workflow + skeleton.' },
|
|
26
|
-
{ name: 'type', type: 'string | null', required: true, constraint: 'Echo of the requested type, or null on the picker stage.' },
|
|
27
|
-
],
|
|
28
|
-
outputKind: 'object',
|
|
29
|
-
effects: ['None. Read-only.'],
|
|
30
|
-
},
|
|
31
|
-
run: async (input) => {
|
|
32
|
-
const type = input['type'];
|
|
33
|
-
const topic = input['topic'];
|
|
34
|
-
const topicArg = topic !== undefined ? topic : '';
|
|
35
|
-
// Progressive disclosure: no type → template picker (stage 1);
|
|
36
|
-
// type given → that type's full workflow + skeleton (stage 2).
|
|
37
|
-
if (type === undefined) {
|
|
38
|
-
return { guide: skillCreatePrompt(topicArg), type: null };
|
|
39
|
-
}
|
|
40
|
-
return { guide: skillTemplatePrompt(type, topicArg), type };
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
export const authorScaffold = defineLeaf({
|
|
44
|
-
name: 'scaffold',
|
|
45
|
-
description: 'create an empty skill substrate doc stub',
|
|
46
|
-
whenToUse: 'creating the empty `kind: skill` substrate doc at a `<plugin>/<skill>` qualifier (or a bare `<skill>` name for a scope-owned doc) before you fill in content — it writes the frontmatter and file, then points you at the authoring guide.',
|
|
47
|
-
help: {
|
|
48
|
-
name: 'skill author scaffold',
|
|
49
|
-
summary: 'create an empty kind:skill substrate doc stub at the given qualifier',
|
|
50
|
-
params: [
|
|
51
|
-
{ kind: 'positional', name: 'qualifier', required: true, constraint: 'Skill identifier: <plugin>/<skill> to scaffold into a plugin\'s memory/, or a bare <skill> for the scope-owned memory/.' },
|
|
52
|
-
{ kind: 'flag', name: 'type', type: 'enum', choices: [...VALID_TYPES], required: false, constraint: 'One of: playbook, primer, reference, runbook, freeform. Drives the follow-up authoring guide only — not stored on the substrate doc.' },
|
|
53
|
-
{ kind: 'flag', name: 'description', type: 'string', required: false, constraint: 'Read-routing line written to the doc\'s when-and-why-to-read frontmatter.' },
|
|
54
|
-
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Default: project if available, else user.' },
|
|
55
|
-
],
|
|
56
|
-
output: [
|
|
57
|
-
{ name: 'path', type: 'string', required: true, constraint: 'Absolute path to the scaffolded substrate doc.' },
|
|
58
|
-
{ name: 'follow_up', type: 'string', required: true, constraint: 'Recommended next call to load the authoring guide.' },
|
|
59
|
-
],
|
|
60
|
-
outputKind: 'object',
|
|
61
|
-
effects: [
|
|
62
|
-
'Creates a `kind: skill` substrate doc stub under the resolved memory/ dir (scope-owned, or the named plugin\'s).',
|
|
63
|
-
'Writes substrate frontmatter: kind:skill, when-and-why-to-read (from --description if given), system-prompt-visibility:name, file-read-visibility:none.',
|
|
64
|
-
],
|
|
65
|
-
},
|
|
66
|
-
run: async (input) => {
|
|
67
|
-
const qualifier = input['qualifier'];
|
|
68
|
-
const typeStr = input['type'];
|
|
69
|
-
const description = input['description'];
|
|
70
|
-
const scopeStr = input['scope'];
|
|
71
|
-
const parsed = parseSkillQualifier(qualifier);
|
|
72
|
-
if (parsed.segments.length === 0) {
|
|
73
|
-
throw usage('skill name required in qualifier');
|
|
74
|
-
}
|
|
75
|
-
// For scaffold, the qualifier is always <plugin>/<skill>. If it's a single segment,
|
|
76
|
-
// treat it as a scope-direct skill name. Otherwise first segment is the plugin.
|
|
77
|
-
const pluginName = parsed.segments.length > 1 ? parsed.segments[0] : undefined;
|
|
78
|
-
const skillName = parsed.segments.length > 1
|
|
79
|
-
? parsed.segments.slice(1).join('/')
|
|
80
|
-
: parsed.segments[0];
|
|
81
|
-
if (typeStr !== undefined && !isSkillType(typeStr)) {
|
|
82
|
-
throw usage(`unknown skill type: ${typeStr} / valid: ${SKILL_TYPES.join(' | ')}`);
|
|
83
|
-
}
|
|
84
|
-
const skillType = typeStr !== undefined && isSkillType(typeStr) ? typeStr : undefined;
|
|
85
|
-
// Resolve the memory/ dir to scaffold into: the named plugin's, or the
|
|
86
|
-
// scope-owned one. One substrate, one author path.
|
|
87
|
-
let memoryDir;
|
|
88
|
-
if (pluginName === undefined) {
|
|
89
|
-
const scope = resolveWriteScope(scopeStr);
|
|
90
|
-
const scopeRootPath = requireScopeRoot(scope);
|
|
91
|
-
ensureScopeInitialized(scope, scopeRootPath);
|
|
92
|
-
const dir = scopeMemoryDir(scope);
|
|
93
|
-
if (!dir) {
|
|
94
|
-
throw general(`no memory dir for scope ${scope}`);
|
|
95
|
-
}
|
|
96
|
-
memoryDir = dir;
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
const scopeForLookup = scopeStr !== undefined
|
|
100
|
-
? (() => {
|
|
101
|
-
const r = resolveScopeArg(scopeStr);
|
|
102
|
-
return r !== 'all' ? r : undefined;
|
|
103
|
-
})()
|
|
104
|
-
: undefined;
|
|
105
|
-
const plugin = scopeForLookup !== undefined
|
|
106
|
-
? findPluginByName(pluginName, scopeForLookup)
|
|
107
|
-
: findPluginByName(pluginName);
|
|
108
|
-
if (!plugin) {
|
|
109
|
-
throw notFound(`plugin not found: ${pluginName}`);
|
|
110
|
-
}
|
|
111
|
-
memoryDir = pluginMemoryDir(plugin);
|
|
112
|
-
}
|
|
113
|
-
const skillFile = memoryFilePath(memoryDir, skillName);
|
|
114
|
-
if (pathExists(skillFile)) {
|
|
115
|
-
throw general(`skill already exists: ${skillFile}`);
|
|
116
|
-
}
|
|
117
|
-
ensureDir(dirname(skillFile));
|
|
118
|
-
// Catalog-default substrate frontmatter (design §1.5): a name-rung,
|
|
119
|
-
// file-read-none skill doc. --description becomes the read-routing line.
|
|
120
|
-
const frontmatter = { kind: 'skill' };
|
|
121
|
-
if (description !== undefined)
|
|
122
|
-
frontmatter['when-and-why-to-read'] = description;
|
|
123
|
-
frontmatter['system-prompt-visibility'] = 'name';
|
|
124
|
-
frontmatter['file-read-visibility'] = 'none';
|
|
125
|
-
writeFileSync(skillFile, serializeMemoryDoc(frontmatter, ''), 'utf8');
|
|
126
|
-
const typeHint = skillType !== undefined ? `--type ${skillType} ` : '';
|
|
127
|
-
const follow_up = `crtr skill author guide ${typeHint}--topic "${skillName}"`;
|
|
128
|
-
return { path: skillFile, follow_up };
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
export const authorBranch = defineBranch({
|
|
132
|
-
name: 'author',
|
|
133
|
-
description: 'create and scaffold skills',
|
|
134
|
-
whenToUse: 'you have a reusable workflow, methodology, or hard-won convention worth capturing so future agents adopt it instead of re-deriving it — author carries you from picking a template through scaffolding the file. Reach for it when a task just taught you a repeatable procedure, when the same guidance keeps getting re-explained across sessions, or when the house conventions for a tool deserve to be written down once. Always start with `crtr skill author guide` — required reading before you author OR edit any skill (the SKILL.md format, the description-vs-body rule, and the voice constraints all live there) — then use `crtr skill author scaffold` to stub the SKILL.md file.',
|
|
135
|
-
help: {
|
|
136
|
-
name: 'skill author',
|
|
137
|
-
summary: 'create and scaffold new skills',
|
|
138
|
-
},
|
|
139
|
-
children: [authorGuide, authorScaffold],
|
|
140
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { usage } from '../../core/errors.js';
|
|
2
|
-
import { resolveScopeArg, projectScopeRoot } from '../../core/scope.js';
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// Resolve scope for scaffold
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
export function resolveWriteScope(scopeStr) {
|
|
7
|
-
if (scopeStr !== undefined) {
|
|
8
|
-
const resolved = resolveScopeArg(scopeStr);
|
|
9
|
-
if (resolved === 'all') {
|
|
10
|
-
throw usage('scope must be user or project, not all');
|
|
11
|
-
}
|
|
12
|
-
return resolved;
|
|
13
|
-
}
|
|
14
|
-
return projectScopeRoot() !== null ? 'project' : 'user';
|
|
15
|
-
}
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Valid skill types (used by author sub-branch)
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
export const VALID_TYPES = ['playbook', 'primer', 'reference', 'runbook', 'freeform'];
|
package/dist/commands/skill.d.ts
DELETED
package/dist/commands/skill.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// `crtr skill` subtree handlers.
|
|
2
|
-
// Sub-branch: author {guide, scaffold}.
|
|
3
|
-
// Discovery and reading are now `crtr memory` verbs.
|
|
4
|
-
import { defineBranch } from '../core/command.js';
|
|
5
|
-
import { authorBranch } from './skill/author.js';
|
|
6
|
-
export function registerSkill() {
|
|
7
|
-
return defineBranch({
|
|
8
|
-
name: 'skill',
|
|
9
|
-
rootEntry: {
|
|
10
|
-
concept: 'a SKILL.md you author and manage',
|
|
11
|
-
desc: 'author and manage skills',
|
|
12
|
-
useWhen: 'authoring a new skill (`crtr skill author`). To discover and read skills use `crtr memory` — `crtr memory read <name>`, `crtr memory find`, `crtr memory list`.',
|
|
13
|
-
},
|
|
14
|
-
help: {
|
|
15
|
-
name: 'skill',
|
|
16
|
-
summary: 'author skills — use `crtr memory` to discover and read them',
|
|
17
|
-
model: '`author` when you are writing a new skill — it carries the template workflow and the scaffolder. Discovery and reading are `crtr memory` verbs: `crtr memory list`, `crtr memory find`, `crtr memory read`. Visibility is governed by the substrate (INDEX/rung/gate), not a per-skill on/off. Append `-h` at any branch or leaf for its full schema.',
|
|
18
|
-
},
|
|
19
|
-
children: [authorBranch],
|
|
20
|
-
});
|
|
21
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// `crtr canvas tmux-spread <id>` — expand a node + its live children into one
|
|
2
|
-
// tiled tmux window: the target wide in the LEFT (main) pane, its live children
|
|
3
|
-
// stacked as panes on the RIGHT, then grab focus.
|
|
4
|
-
//
|
|
5
|
-
// Reuses the presence/tmux machinery: revive a dormant target/child so it has a
|
|
6
|
-
// live pane, `join-pane` each child's existing pane into the target's window
|
|
7
|
-
// (preserving its running pi), `select-layout main-vertical`, then focus.
|
|
8
|
-
//
|
|
9
|
-
// CRITICAL fix-up: a joined child physically changes windows, so its
|
|
10
|
-
// meta.{tmux_session,window} goes stale — `windowAlive` would then report it
|
|
11
|
-
// dormant and the daemon would spuriously revive it. After each join we
|
|
12
|
-
// re-derive the child's location from its (stable) pane id and updateNode it,
|
|
13
|
-
// mirroring the swap fix-up in placement.ts.
|
|
14
|
-
import { defineLeaf } from '../core/command.js';
|
|
15
|
-
import { InputError } from '../core/io.js';
|
|
16
|
-
import { readConfig } from '../core/config.js';
|
|
17
|
-
import { reviveNode } from '../core/runtime/revive.js';
|
|
18
|
-
import { isNodePaneAlive, spreadNode, inTmux } from '../core/runtime/placement.js';
|
|
19
|
-
import { nodeInPane } from './node.js';
|
|
20
|
-
import { getNode, subscriptionsOf } from '../core/canvas/index.js';
|
|
21
|
-
export const tmuxSpreadLeaf = defineLeaf({
|
|
22
|
-
name: 'tmux-spread',
|
|
23
|
-
description: 'tile a node + its live children into one window and focus it',
|
|
24
|
-
whenToUse: 'tiling a node and its live children into one window — the node wide on the left, its workers stacked on the right — to watch an orchestrator and its team together (alt+c → e / GRAPH e)',
|
|
25
|
-
help: {
|
|
26
|
-
name: 'canvas tmux-spread',
|
|
27
|
-
summary: 'tile a node and its live children into one window — target wide on the left, children stacked on the right — and focus it. Revives dormant nodes first; caps children by max_panes_per_window.',
|
|
28
|
-
params: [
|
|
29
|
-
{
|
|
30
|
-
kind: 'positional',
|
|
31
|
-
name: 'node',
|
|
32
|
-
required: false,
|
|
33
|
-
constraint: 'Node id to spread. Defaults to the node occupying --pane (or your current pane).',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
kind: 'flag',
|
|
37
|
-
name: 'pane',
|
|
38
|
-
type: 'string',
|
|
39
|
-
required: false,
|
|
40
|
-
constraint: 'tmux pane id to resolve the node from when no positional is given. The alt+c menu passes this for you.',
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
output: [
|
|
44
|
-
{ name: 'window', type: 'string', required: false, constraint: 'The window all panes were tiled into.' },
|
|
45
|
-
{ name: 'session', type: 'string', required: false, constraint: 'The tmux session that window lives in.' },
|
|
46
|
-
{ name: 'children_joined', type: 'string[]', required: true, constraint: 'Child node ids whose panes were joined into the window.' },
|
|
47
|
-
{ name: 'overflow', type: 'string[]', required: true, constraint: 'Live children left out because max_panes_per_window was reached.' },
|
|
48
|
-
{ name: 'focused', type: 'boolean', required: true, constraint: 'True when the window was brought forefront.' },
|
|
49
|
-
],
|
|
50
|
-
outputKind: 'object',
|
|
51
|
-
effects: [
|
|
52
|
-
'Revives the target (and joined children) if dormant — opens tmux windows running pi.',
|
|
53
|
-
'Moves each joined child\'s pane into the target window (join-pane) and re-points its canvas record to the new window.',
|
|
54
|
-
'Applies a main-vertical layout and focuses the window.',
|
|
55
|
-
],
|
|
56
|
-
},
|
|
57
|
-
run: async (input) => {
|
|
58
|
-
if (!inTmux()) {
|
|
59
|
-
throw new InputError({
|
|
60
|
-
error: 'not_in_tmux',
|
|
61
|
-
message: 'tmux-spread needs a tmux server (no $TMUX)',
|
|
62
|
-
next: 'Run from inside the shared crtr tmux session.',
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
const pane = input['pane'];
|
|
66
|
-
let id = input['node'];
|
|
67
|
-
if (id === undefined || id === '')
|
|
68
|
-
id = nodeInPane(pane);
|
|
69
|
-
if (id === undefined || id === '') {
|
|
70
|
-
throw new InputError({
|
|
71
|
-
error: 'no_node',
|
|
72
|
-
message: 'no node to spread (pass a node id, or run from inside its pane)',
|
|
73
|
-
next: 'Pass `crtr canvas tmux-spread <id>` or --pane <pane-id>.',
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
if (getNode(id) === null) {
|
|
77
|
-
throw new InputError({
|
|
78
|
-
error: 'not_found',
|
|
79
|
-
message: `no node: ${id}`,
|
|
80
|
-
next: 'List nodes with `crtr node inspect list`.',
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
// 1. Revive the target if it has no live pane (placement is pane-keyed).
|
|
84
|
-
if (!isNodePaneAlive(id)) {
|
|
85
|
-
try {
|
|
86
|
-
reviveNode(id, { resume: true });
|
|
87
|
-
}
|
|
88
|
-
catch { /* fall through */ }
|
|
89
|
-
}
|
|
90
|
-
if (!isNodePaneAlive(id)) {
|
|
91
|
-
throw new InputError({
|
|
92
|
-
error: 'no_window',
|
|
93
|
-
message: `could not open a live window for ${id}`,
|
|
94
|
-
next: 'Try `crtr canvas revive <id>` then retry.',
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
// 2. Live children, capped: the target owns one pane, so up to max-1 join.
|
|
98
|
-
const max = readConfig('user').max_panes_per_window;
|
|
99
|
-
const budget = Math.max(0, max - 1);
|
|
100
|
-
const liveChildren = subscriptionsOf(id)
|
|
101
|
-
.map((r) => r.node_id)
|
|
102
|
-
.filter((cid) => {
|
|
103
|
-
const s = getNode(cid)?.status;
|
|
104
|
-
return s === 'active' || s === 'idle';
|
|
105
|
-
});
|
|
106
|
-
const selected = liveChildren.slice(0, budget);
|
|
107
|
-
const overflow = liveChildren.slice(budget);
|
|
108
|
-
// 3. Revive any dormant selected child so it has a live pane, then hand the
|
|
109
|
-
// join + pane-fix-up (reconcile FOLLOWS each joined pane) + layout + focus
|
|
110
|
-
// to placement.
|
|
111
|
-
for (const cid of selected) {
|
|
112
|
-
if (!isNodePaneAlive(cid)) {
|
|
113
|
-
try {
|
|
114
|
-
reviveNode(cid, { resume: true });
|
|
115
|
-
}
|
|
116
|
-
catch { /* skip on failure */ }
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
const spread = spreadNode(id, selected);
|
|
120
|
-
return {
|
|
121
|
-
window: spread.window,
|
|
122
|
-
session: spread.session,
|
|
123
|
-
children_joined: spread.joined,
|
|
124
|
-
overflow,
|
|
125
|
-
focused: spread.focused,
|
|
126
|
-
};
|
|
127
|
-
},
|
|
128
|
-
render: (r) => {
|
|
129
|
-
const joined = r['children_joined'] ?? [];
|
|
130
|
-
const overflow = r['overflow'] ?? [];
|
|
131
|
-
const where = r['window'] !== undefined && r['window'] !== ''
|
|
132
|
-
? ` into window ${r['window']}${r['session'] !== undefined ? ` (session ${r['session']})` : ''}`
|
|
133
|
-
: '';
|
|
134
|
-
const lead = `Spread node${where} — ${joined.length} child pane(s) joined, ${overflow.length} overflow, ${r['focused'] === true ? 'window focused' : 'window not focused'}.`;
|
|
135
|
-
const blocks = [lead];
|
|
136
|
-
if (joined.length > 0) {
|
|
137
|
-
blocks.push(['Children joined:', ...joined.map((c) => `- ${c}`)].join('\n'));
|
|
138
|
-
}
|
|
139
|
-
if (overflow.length > 0) {
|
|
140
|
-
blocks.push(['Overflow (max_panes_per_window reached):', ...overflow.map((c) => `- ${c}`)].join('\n'));
|
|
141
|
-
}
|
|
142
|
-
return blocks.join('\n\n');
|
|
143
|
-
},
|
|
144
|
-
});
|