@crouton-kit/crouter 0.3.78 → 0.3.79
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.d.ts +12 -4
- package/dist/build-root.js +25 -6
- package/dist/builtin-memory/crouter-development/plugins.md +82 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +1228 -12
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +3 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +2 -787
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +8 -3
- package/dist/clients/attach/__tests__/attach-keybindings.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +113 -0
- package/dist/clients/attach/__tests__/mermaid-render.test.js +9 -1
- package/dist/clients/attach/attach-cmd.d.ts +9 -1
- package/dist/clients/attach/attach-cmd.js +849 -803
- package/dist/clients/attach/auth-pickers.d.ts +0 -12
- package/dist/clients/attach/auth-pickers.js +64 -15
- package/dist/clients/attach/graph-overlay.d.ts +12 -2
- package/dist/clients/attach/graph-overlay.js +83 -33
- package/dist/commands/human/queue.js +3 -4
- package/dist/commands/pkg/plugin-inspect.js +22 -1
- package/dist/commands/pkg/plugin-manage.js +31 -9
- package/dist/commands/sys/__tests__/setup-core.test.js +158 -1
- package/dist/commands/sys/doctor.js +42 -4
- package/dist/commands/sys/setup-core.d.ts +37 -0
- package/dist/commands/sys/setup-core.js +138 -1
- package/dist/commands/sys/setup.d.ts +88 -0
- package/dist/commands/sys/setup.js +915 -171
- package/dist/commands/view-pick.d.ts +4 -0
- package/dist/commands/view-pick.js +17 -7
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +34 -9
- package/dist/core/__tests__/command-plugins-surfaces.test.d.ts +1 -0
- package/dist/core/__tests__/command-plugins-surfaces.test.js +298 -0
- package/dist/core/__tests__/command-plugins.test.d.ts +1 -0
- package/dist/core/__tests__/command-plugins.test.js +444 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +6 -0
- package/dist/core/__tests__/fixtures/fake-engine.js +9 -1
- package/dist/core/__tests__/preview-registry-sync.test.js +30 -1
- package/dist/core/__tests__/scope-crouter-home-fence.test.d.ts +1 -0
- package/dist/core/__tests__/scope-crouter-home-fence.test.js +55 -0
- package/dist/core/canvas/browse/app.d.ts +6 -0
- package/dist/core/canvas/browse/app.js +71 -41
- package/dist/core/command-plugins/adapter.d.ts +15 -0
- package/dist/core/command-plugins/adapter.js +145 -0
- package/dist/core/command-plugins/compose.d.ts +5 -0
- package/dist/core/command-plugins/compose.js +56 -0
- package/dist/core/command-plugins/discovery.d.ts +104 -0
- package/dist/core/command-plugins/discovery.js +565 -0
- package/dist/core/config.d.ts +2 -5
- package/dist/core/keybindings/__tests__/bespoke-consumers.test.d.ts +1 -0
- package/dist/core/keybindings/__tests__/bespoke-consumers.test.js +40 -0
- package/dist/core/keybindings/__tests__/resolve.test.js +2 -2
- package/dist/core/keybindings/catalog.d.ts +3 -3
- package/dist/core/keybindings/catalog.js +2 -1
- package/dist/core/profiles/select.d.ts +6 -0
- package/dist/core/profiles/select.js +86 -52
- package/dist/core/provider-management.d.ts +12 -0
- package/dist/core/provider-management.js +24 -0
- package/dist/core/runtime/broker.js +7 -6
- package/dist/core/runtime/pi-vendored.d.ts +8 -0
- package/dist/core/runtime/pi-vendored.js +14 -0
- package/dist/core/runtime/recap.d.ts +1 -1
- package/dist/core/runtime/recap.js +50 -25
- package/dist/core/runtime/session-list-cache.d.ts +10 -0
- package/dist/core/runtime/session-list-cache.js +94 -26
- package/dist/core/runtime/spawn.js +1 -13
- package/dist/core/runtime/tmux.js +2 -1
- package/dist/core/scope.js +27 -4
- package/dist/core/subscription-state.d.ts +90 -0
- package/dist/core/subscription-state.js +762 -0
- package/dist/daemon/crtrd.js +253 -12
- package/dist/pi-extensions/canvas-recap.js +43 -17
- package/dist/types.d.ts +6 -13
- package/dist/types.js +0 -3
- package/package.json +7 -3
|
@@ -29,6 +29,7 @@ import { openFocusRow } from '../../../core/canvas/focuses.js';
|
|
|
29
29
|
import { jobDir } from '../../../core/canvas/paths.js';
|
|
30
30
|
import { recordFault } from '../../../core/runtime/fault.js';
|
|
31
31
|
import { BG_ATTACHED } from '../../../core/canvas/nav-model.js';
|
|
32
|
+
import { resolveKeybindings } from '../../../core/keybindings/index.js';
|
|
32
33
|
import { dispatchSlashCommand, slashCommandList } from '../slash-commands.js';
|
|
33
34
|
import { resolveInitialMode, requestModeSwitch, tagAttachPane, untagAttachPane, reapOnDetach, } from '../attach-cmd.js';
|
|
34
35
|
import { buildCanvasPanelLines } from '../canvas-panels.js';
|
|
@@ -198,7 +199,7 @@ test('GraphOverlay suppresses every local-read adornment for a RemoteCanvasSourc
|
|
|
198
199
|
t.after(() => server.close());
|
|
199
200
|
const source = new RemoteCanvasSource(server.url, 'tok');
|
|
200
201
|
const { tui } = fakeTui();
|
|
201
|
-
const overlay = new GraphOverlay(tui, 'root', () => ({}), IDENTITY_PALETTE, source);
|
|
202
|
+
const overlay = new GraphOverlay(tui, 'root', () => ({}), IDENTITY_PALETTE, resolveKeybindings(), source);
|
|
202
203
|
overlay.open();
|
|
203
204
|
// buildSnapshot() is async (chased through the mock server); poll for it to land.
|
|
204
205
|
for (let i = 0; i < 100 && overlay.snapshot === undefined; i++) {
|
|
@@ -217,10 +218,14 @@ test('GraphOverlay suppresses every local-read adornment for a RemoteCanvasSourc
|
|
|
217
218
|
// effect of a real `focusTarget` dispatch) — it must no-op instead.
|
|
218
219
|
overlay.handleInput('\r');
|
|
219
220
|
assert.equal(overlay.isOpen(), true, 'Enter must not act (close) on a remote graph row');
|
|
220
|
-
// x must NOT arm the
|
|
221
|
+
// x must NOT arm the close confirm on a remote row. The confirmation UI never
|
|
222
|
+
// literally contains legacy `y/n`, so assert the real state directly:
|
|
223
|
+
// pendingConfirm stays unset and no `kill …?` accept/cancel prompt renders.
|
|
221
224
|
overlay.handleInput('x');
|
|
225
|
+
assert.equal(overlay.pendingConfirm, undefined, 'x must not arm pendingConfirm on a remote graph row');
|
|
222
226
|
const afterX = overlay.render(100).join('\n');
|
|
223
|
-
assert.ok(!afterX.includes('
|
|
227
|
+
assert.ok(!afterX.includes('kill'), `x must not surface a kill confirm prompt on a remote graph row: ${afterX}`);
|
|
228
|
+
assert.ok(!/accept|cancel/.test(afterX), `x must not surface an accept/cancel confirm on a remote graph row: ${afterX}`);
|
|
224
229
|
// m must NOT close the overlay either (it would focusTarget the manager).
|
|
225
230
|
overlay.handleInput('m');
|
|
226
231
|
assert.equal(overlay.isOpen(), true, 'm must not act (close) on a remote graph row');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { resolveKeybindings } from '../../../core/keybindings/index.js';
|
|
4
|
+
import { detachHint, dispatchAttachBinding, matchesAttachBinding, refreshAttachBindingSnapshot, } from '../attach-cmd.js';
|
|
5
|
+
import { GraphOverlay } from '../graph-overlay.js';
|
|
6
|
+
const PALETTE = {
|
|
7
|
+
accent: (s) => s,
|
|
8
|
+
active: (s) => s,
|
|
9
|
+
info: (s) => s,
|
|
10
|
+
muted: (s) => s,
|
|
11
|
+
faint: (s) => s,
|
|
12
|
+
border: (s) => s,
|
|
13
|
+
bold: (s) => s,
|
|
14
|
+
error: (s) => s,
|
|
15
|
+
warning: (s) => s,
|
|
16
|
+
bashMode: (s) => s,
|
|
17
|
+
bashModeAlt: (s) => s,
|
|
18
|
+
surface: (s) => s,
|
|
19
|
+
};
|
|
20
|
+
function fakeTui() {
|
|
21
|
+
return {
|
|
22
|
+
showOverlay: () => ({ hide: () => { } }),
|
|
23
|
+
requestRender: () => { },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
test('attach input dispatches only the current configured action', () => {
|
|
27
|
+
const bindings = resolveKeybindings({
|
|
28
|
+
'crtr.attach.detach': ['ctrl+x'],
|
|
29
|
+
'crtr.attach.clear-or-detach': [],
|
|
30
|
+
'crtr.attach.graph.toggle': [],
|
|
31
|
+
'crtr.mode.cycle': ['alt+x'],
|
|
32
|
+
'crtr.attach.model-ladder.next': ['alt+z'],
|
|
33
|
+
});
|
|
34
|
+
const calls = [];
|
|
35
|
+
const actions = {
|
|
36
|
+
'crtr.attach.detach': () => calls.push('detach'),
|
|
37
|
+
'crtr.attach.clear-or-detach': () => calls.push('clear'),
|
|
38
|
+
'crtr.attach.graph.toggle': () => calls.push('graph'),
|
|
39
|
+
'crtr.mode.cycle': () => calls.push('mode'),
|
|
40
|
+
'crtr.attach.model-ladder.next': () => calls.push('ladder'),
|
|
41
|
+
};
|
|
42
|
+
assert.equal(dispatchAttachBinding(bindings, '\x18', actions), true);
|
|
43
|
+
assert.equal(dispatchAttachBinding(bindings, '\x04', actions), false, 'old detach must not reach the listener action');
|
|
44
|
+
assert.equal(dispatchAttachBinding(bindings, '\x1bg', actions), false, 'disabled graph must not reach the listener action');
|
|
45
|
+
assert.equal(dispatchAttachBinding(bindings, '\x1bx', actions), true);
|
|
46
|
+
assert.equal(dispatchAttachBinding(bindings, '\x1bm', actions), false, 'old mode key must not reach the listener action');
|
|
47
|
+
assert.equal(dispatchAttachBinding(bindings, '\x1bz', actions), true);
|
|
48
|
+
assert.equal(dispatchAttachBinding(bindings, '\x1bM', actions), false, 'old ladder key must not reach the listener action');
|
|
49
|
+
assert.deepEqual(calls, ['detach', 'mode', 'ladder']);
|
|
50
|
+
assert.equal(detachHint(bindings), 'Ctrl+X', 'disabled clear-or-detach must fall back to enabled detach');
|
|
51
|
+
const noDetach = resolveKeybindings({ 'crtr.attach.detach': [], 'crtr.attach.clear-or-detach': [] });
|
|
52
|
+
assert.equal(detachHint(noDetach), undefined, 'a disabled pair must not render "Disabled to detach"');
|
|
53
|
+
});
|
|
54
|
+
test('attach startup and reload replace the dispatch/GRAPH snapshots and install each tmux snapshot', () => {
|
|
55
|
+
const first = resolveKeybindings({ 'crtr.attach.detach': ['ctrl+x'], 'crtr.graph.focus': ['f'] });
|
|
56
|
+
const second = resolveKeybindings({ 'crtr.attach.detach': ['ctrl+z'], 'crtr.graph.focus': ['r'] });
|
|
57
|
+
const snapshots = [first, second];
|
|
58
|
+
const graphSnapshots = [];
|
|
59
|
+
const installed = [];
|
|
60
|
+
let active = refreshAttachBindingSnapshot(() => snapshots.shift(), (bindings) => graphSnapshots.push(bindings), () => true, (bindings) => installed.push(bindings));
|
|
61
|
+
assert.equal(matchesAttachBinding(active, 'crtr.attach.detach', '\x18'), true, 'startup snapshot drives attach dispatch');
|
|
62
|
+
assert.equal(active.gestures('crtr.graph.focus')[0], 'f', 'startup snapshot reaches GRAPH');
|
|
63
|
+
active = refreshAttachBindingSnapshot(() => snapshots.shift(), (bindings) => graphSnapshots.push(bindings), () => true, (bindings) => installed.push(bindings));
|
|
64
|
+
assert.equal(matchesAttachBinding(active, 'crtr.attach.detach', '\x18'), false, 'reload replaces the old attach snapshot');
|
|
65
|
+
assert.equal(matchesAttachBinding(active, 'crtr.attach.detach', '\x1a'), true, 'reload dispatches through the new attach snapshot');
|
|
66
|
+
assert.equal(active.gestures('crtr.graph.focus')[0], 'r', 'reload replaces GRAPH bindings too');
|
|
67
|
+
assert.deepEqual(graphSnapshots, [first, second]);
|
|
68
|
+
assert.deepEqual(installed, [first, second], 'the unified tmux installer receives startup and reload snapshots');
|
|
69
|
+
});
|
|
70
|
+
test('GRAPH navigation and hints use effective bindings rather than default literals', () => {
|
|
71
|
+
const bindings = resolveKeybindings({
|
|
72
|
+
'crtr.graph.down': ['z'],
|
|
73
|
+
'crtr.graph.up': [],
|
|
74
|
+
'crtr.graph.focus': ['f'],
|
|
75
|
+
'crtr.graph.close-subtree': [],
|
|
76
|
+
'crtr.graph.dismiss': ['q'],
|
|
77
|
+
'crtr.graph.confirm.accept': ['t'],
|
|
78
|
+
'crtr.graph.confirm.cancel': ['c'],
|
|
79
|
+
});
|
|
80
|
+
const overlay = new GraphOverlay(fakeTui(), 'a', () => ({}), PALETTE, bindings);
|
|
81
|
+
const internal = overlay;
|
|
82
|
+
internal.cursorId = 'a';
|
|
83
|
+
internal.snapshot = {
|
|
84
|
+
rows: [
|
|
85
|
+
{ id: 'a', hasKids: false, isSelf: true, branch: '', cycle: false, collapsed: false },
|
|
86
|
+
{ id: 'b', hasKids: false, isSelf: false, branch: '', cycle: false, collapsed: false },
|
|
87
|
+
],
|
|
88
|
+
activeBelow: new Map(),
|
|
89
|
+
nodes: new Map([['a', null], ['b', null]]),
|
|
90
|
+
childIds: new Map(),
|
|
91
|
+
sortedChildIds: new Map(),
|
|
92
|
+
managerOf: new Map(),
|
|
93
|
+
focused: new Set(),
|
|
94
|
+
};
|
|
95
|
+
overlay.handleInput('j');
|
|
96
|
+
assert.equal(internal.cursorId, 'a', 'replaced default must stop matching');
|
|
97
|
+
overlay.handleInput('z');
|
|
98
|
+
assert.equal(internal.cursorId, 'b', 'replacement must drive navigation');
|
|
99
|
+
const rendered = overlay.render(58).join('\n');
|
|
100
|
+
assert.match(rendered, /F focus/, 'narrow GRAPH footer must prioritize remapped local focus');
|
|
101
|
+
assert.match(rendered, /Q dismiss/, 'narrow GRAPH footer must prioritize remapped dismiss');
|
|
102
|
+
assert.doesNotMatch(rendered, /close/);
|
|
103
|
+
assert.doesNotMatch(rendered, /J down/);
|
|
104
|
+
assert.doesNotMatch(rendered, /Disabled/);
|
|
105
|
+
let closed = 0;
|
|
106
|
+
internal.pendingConfirm = { label: 'close b?', action: () => { closed++; } };
|
|
107
|
+
assert.match(overlay.render(80).join('\n'), /T accept · C cancel/);
|
|
108
|
+
overlay.handleInput('y');
|
|
109
|
+
assert.equal(closed, 0, 'replaced accept default must stop confirming');
|
|
110
|
+
internal.pendingConfirm = { label: 'close b?', action: () => { closed++; } };
|
|
111
|
+
overlay.handleInput('t');
|
|
112
|
+
assert.equal(closed, 1, 'replacement must confirm close');
|
|
113
|
+
});
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import test from 'node:test';
|
|
3
|
+
import { ensureRenderer, isRendererReady } from '@crouton-kit/humanloop';
|
|
3
4
|
import { transformMermaidFences } from '../mermaid-render.js';
|
|
5
|
+
// This asserts a REAL termrender render (box-drawing glyphs), so it only holds
|
|
6
|
+
// once the managed renderer is provisioned — true in the full tier and on a dev
|
|
7
|
+
// box, but the fast tier is deliberately termrender-free (no uv to provision the
|
|
8
|
+
// venv). Skip cleanly when the renderer is unavailable, exactly as the tmux
|
|
9
|
+
// tests skip when tmux is absent, rather than failing on the environment.
|
|
10
|
+
ensureRenderer();
|
|
11
|
+
const SKIP = !isRendererReady() ? 'termrender renderer unavailable' : false;
|
|
4
12
|
const APPLET_CONCIERGE_FLOW = `\`\`\`mermaid
|
|
5
13
|
flowchart LR
|
|
6
14
|
U[User] --> C[Applet concierge]
|
|
@@ -10,7 +18,7 @@ flowchart LR
|
|
|
10
18
|
B --> C
|
|
11
19
|
C --> U
|
|
12
20
|
\`\`\``;
|
|
13
|
-
test('renders the applet concierge flowchart after provisioning termrender', () => {
|
|
21
|
+
test('renders the applet concierge flowchart after provisioning termrender', { skip: SKIP }, () => {
|
|
14
22
|
const rendered = transformMermaidFences(APPLET_CONCIERGE_FLOW, 200);
|
|
15
23
|
assert.match(rendered, /Applet-builder/);
|
|
16
24
|
assert.match(rendered, /cwd = app repo/);
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { CombinedAutocompleteProvider } from '@earendil-works/pi-tui';
|
|
2
2
|
import type { BranchDef } from '../../core/command.js';
|
|
3
3
|
import type { BrokerSnapshot, ClientRole } from '../../core/runtime/broker-protocol.js';
|
|
4
|
+
import { type BindingId, type BindingResolution } from '../../core/keybindings/index.js';
|
|
4
5
|
import { type AttachPalette } from './config-load.js';
|
|
5
6
|
export type Mode = 'normal' | 'spec' | 'plan';
|
|
7
|
+
type AttachBindingId = Extract<BindingId, `crtr.attach.${string}` | 'crtr.mode.cycle'>;
|
|
8
|
+
type AttachBindings = BindingResolution<BindingId>;
|
|
9
|
+
export declare function matchesAttachBinding(bindings: AttachBindings, id: AttachBindingId, data: string): boolean;
|
|
10
|
+
export declare function dispatchAttachBinding(bindings: AttachBindings, data: string, actions: Record<AttachBindingId, () => void>): boolean;
|
|
11
|
+
export declare function detachHint(bindings: AttachBindings): string | undefined;
|
|
12
|
+
export declare function refreshAttachBindingSnapshot(resolve: () => AttachBindings, setGraphBindings: (bindings: AttachBindings) => void, inTmuxNow: () => boolean, install: (bindings: AttachBindings) => void): AttachBindings;
|
|
6
13
|
/** Resolve the mode badge's initial state. Remote never reads the local
|
|
7
14
|
* mode-switch `current` file for `nodeId` — always starts 'normal'. */
|
|
8
15
|
export declare function resolveInitialMode(cwd: string, nodeId: string, remote: boolean, readMode?: (cwd: string, nodeId: string) => Mode | undefined): Mode;
|
|
9
|
-
/** Handle
|
|
16
|
+
/** Handle a configured mode-cycle request. Remote no-ops entirely (returns `false`,
|
|
10
17
|
* so the caller leaves `modeState`/the badge untouched) — there is no
|
|
11
18
|
* remote-safe mode-switch protocol, and writing the local `request.json` for a
|
|
12
19
|
* remote node id would target the wrong (or a colliding local) node. */
|
|
@@ -46,3 +53,4 @@ export declare function buildAttachAutocompleteProvider(cwd: string, fdPath: str
|
|
|
46
53
|
description?: string;
|
|
47
54
|
}>, cwd: string, fdPath: string | null) => CombinedAutocompleteProvider): CombinedAutocompleteProvider;
|
|
48
55
|
export declare function registerAttach(): BranchDef;
|
|
56
|
+
export {};
|