@north-light/crouter 0.3.162 → 0.3.164
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/builtin-memory/00-runtime-base.md +1 -0
- package/dist/builtin-memory/04-orchestration-kernel.md +1 -0
- package/dist/builtin-memory/init.md +38 -0
- package/dist/builtin-memory/internal/INDEX.md +1 -0
- package/dist/builtin-memory/internal/memory-loading.md +45 -0
- package/dist/builtin-memory/wedged-child-on-runaway-bash.md +2 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +0 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +34 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +14 -3
- package/dist/clients/attach/__tests__/crtr-output-coverage.test.js +1 -1
- package/dist/clients/attach/__tests__/crtr-output.test.js +14 -9
- package/dist/clients/attach/__tests__/edit-diff.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/edit-diff.test.js +35 -0
- package/dist/clients/attach/__tests__/editor-frame-title.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/editor-frame-title.test.js +19 -0
- package/dist/clients/attach/render/chat-view.js +54 -38
- package/dist/clients/attach/render/crtr-output.d.ts +2 -1
- package/dist/clients/attach/render/crtr-output.js +33 -4
- package/dist/clients/attach/render/edit-diff.d.ts +21 -6
- package/dist/clients/attach/render/edit-diff.js +104 -81
- package/dist/clients/attach/render/tool-calls.d.ts +21 -20
- package/dist/clients/attach/render/tool-calls.js +89 -43
- package/dist/clients/attach/session/connection.d.ts +1 -1
- package/dist/clients/attach/session/editor-frame.d.ts +5 -1
- package/dist/clients/attach/session/editor-frame.js +21 -15
- package/dist/clients/attach/session/identity.d.ts +1 -1
- package/dist/clients/attach/viewer.js +628 -624
- package/dist/commands/__tests__/api-canvas-source.test.d.ts +1 -0
- package/dist/commands/__tests__/api-canvas-source.test.js +37 -0
- package/dist/commands/__tests__/human.test.js +2 -0
- package/dist/commands/__tests__/search-contents.test.d.ts +1 -0
- package/dist/commands/__tests__/search-contents.test.js +52 -0
- package/dist/commands/api-client.js +1 -0
- package/dist/commands/human/prompts.js +2 -2
- package/dist/commands/human/shared.d.ts +1 -0
- package/dist/commands/human/shared.js +7 -4
- package/dist/commands/memory/lint.d.ts +6 -0
- package/dist/commands/memory/lint.js +177 -35
- package/dist/commands/memory/read.js +22 -1
- package/dist/commands/memory/write.js +18 -4
- package/dist/commands/memory.js +1 -1
- package/dist/commands/search/contents.js +2 -2
- package/dist/commands/sys/__tests__/sync-deps.test.js +12 -21
- package/dist/commands/sys/__tests__/sync-import.test.js +30 -29
- package/dist/commands/sys/setup-wizard.d.ts +8 -1
- package/dist/commands/sys/setup-wizard.js +209 -58
- package/dist/commands/sys/sync-deps.js +14 -18
- package/dist/commands/sys/sync-project-guidance.js +16 -19
- package/dist/core/__tests__/migration.test.js +8 -3
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +7 -10
- package/dist/core/__tests__/on-read-dedup-resume.test.js +13 -25
- package/dist/core/__tests__/on-read-identity.test.js +8 -15
- package/dist/core/__tests__/revive.test.js +2 -2
- package/dist/core/__tests__/tmux-surface.test.js +10 -4
- package/dist/core/__tests__/worktree.test.js +3 -3
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +18 -11
- package/dist/core/canvas/labels.d.ts +4 -5
- package/dist/core/canvas/labels.js +9 -9
- package/dist/core/command-manifests/schema.js +18 -5
- package/dist/core/command.js +17 -7
- package/dist/core/configured-clis/invoker.js +2 -0
- package/dist/core/help.d.ts +3 -1
- package/dist/core/help.js +5 -2
- package/dist/core/keybindings/__tests__/resolve.test.js +5 -2
- package/dist/core/keybindings/catalog.d.ts +3 -2
- package/dist/core/keybindings/catalog.js +34 -25
- package/dist/core/keybindings/index.d.ts +1 -1
- package/dist/core/keybindings/resolve.js +8 -0
- package/dist/core/keybindings/types.d.ts +6 -1
- package/dist/core/memory/doc-link-grammar.d.ts +20 -0
- package/dist/core/memory/doc-link-grammar.js +110 -0
- package/dist/core/memory-resolver.d.ts +5 -0
- package/dist/core/memory-resolver.js +12 -1
- package/dist/core/runtime/bearings.d.ts +8 -8
- package/dist/core/runtime/bearings.js +20 -16
- package/dist/core/runtime/broker.js +3 -13
- package/dist/core/runtime/canvas-extensions.d.ts +1 -0
- package/dist/core/runtime/canvas-extensions.js +2 -0
- package/dist/core/runtime/launch.d.ts +1 -1
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/tmux.js +136 -100
- package/dist/core/scope.js +4 -5
- package/dist/core/substrate/ceiling.d.ts +3 -6
- package/dist/core/substrate/ceiling.js +13 -15
- package/dist/core/substrate/index.d.ts +2 -3
- package/dist/core/substrate/index.js +2 -2
- package/dist/core/substrate/on-read.d.ts +4 -13
- package/dist/core/substrate/on-read.js +142 -262
- package/dist/core/substrate/render.js +4 -4
- package/dist/core/substrate/schema.d.ts +3 -2
- package/dist/core/substrate/schema.js +5 -5
- package/dist/pi-extensions/__tests__/canvas-tool-guide.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-tool-guide.test.js +96 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +7 -8
- package/dist/pi-extensions/canvas-tool-guide.d.ts +14 -0
- package/dist/pi-extensions/canvas-tool-guide.js +77 -0
- package/package.json +2 -2
- package/runtime.lock.json +6 -6
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crouter-help.ts +0 -95
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// conversation. That dedup set USED to live only in the pi process heap (cleared
|
|
5
|
-
// on session_start). But a node's logical session — the .jsonl transcript —
|
|
6
|
-
// spans MULTIPLE pi processes: a dormancy → revive(resume) cycle exits the old
|
|
7
|
-
// process and launches a fresh `pi --session` that REUSES the same transcript.
|
|
8
|
-
// The fresh process started with an empty set, so any doc already injected
|
|
9
|
-
// before dormancy got injected AGAIN on the next read.
|
|
10
|
-
//
|
|
11
|
-
// The fix persists the set to nodes/<id>/injected-docs.json: a resume rehydrates
|
|
12
|
-
// it (dedup holds), and only the FRESH-transcript launch paths clear it. These
|
|
13
|
-
// tests lock in both halves: the store round-trip, and the dedup surviving a
|
|
14
|
-
// simulated revive(resume) while re-surfacing after a fresh launch.
|
|
1
|
+
// On-read context surfaces at most once per conversation. The dedup set lives
|
|
2
|
+
// at nodes/<id>/injected-docs.json so a process resumed onto the same transcript
|
|
3
|
+
// rehydrates it, while a fresh transcript clears it.
|
|
15
4
|
//
|
|
16
5
|
// Run: node --import tsx/esm --test src/core/__tests__/on-read-dedup-resume.test.ts
|
|
17
6
|
import { test, before, beforeEach, after } from 'node:test';
|
|
@@ -64,7 +53,7 @@ test('injected-store round-trips and clears', () => {
|
|
|
64
53
|
assert.ok(!existsSync(injectedDocsPath(node)), 'clear removes the file');
|
|
65
54
|
assert.deepEqual([...loadInjectedDocs(node)], [], 'load after clear is empty');
|
|
66
55
|
});
|
|
67
|
-
test('user
|
|
56
|
+
test('project and user memory docs without applies-to do not fire on file reads', () => {
|
|
68
57
|
const fakeHome = mkdtempSync(join(tmpdir(), 'crtr-onread-user-home-'));
|
|
69
58
|
process.env['HOME'] = fakeHome;
|
|
70
59
|
resetScopeCache();
|
|
@@ -72,33 +61,32 @@ test('user-global memory docs do not fire positionally for every file under home
|
|
|
72
61
|
const userMemDir = join(fakeHome, '.crouter', 'memory');
|
|
73
62
|
mkdirSync(userMemDir, { recursive: true });
|
|
74
63
|
writeFileSync(join(userMemDir, 'noisy-global.md'), '---\nkind: preference\n' +
|
|
75
|
-
'when-and-why-to-read: When reading any file, this preference should be read because this fixture must not fire
|
|
64
|
+
'when-and-why-to-read: When reading any file, this preference should be read because this fixture must not fire without an explicit route\n' +
|
|
76
65
|
'file-read-visibility: content\n---\n' +
|
|
77
66
|
'NOISY USER BODY\n');
|
|
78
67
|
const projectMemDir = join(fakeHome, 'work', '.crouter', 'memory');
|
|
79
68
|
mkdirSync(projectMemDir, { recursive: true });
|
|
80
69
|
writeFileSync(join(projectMemDir, 'local.md'), '---\nkind: knowledge\n' +
|
|
81
|
-
'when-and-why-to-read: When reading project files, this reference should be read because
|
|
70
|
+
'when-and-why-to-read: When reading project files, this reference should be read because this fixture proves explicit file routing\n' +
|
|
82
71
|
'file-read-visibility: content\n---\n' +
|
|
83
72
|
'LOCAL PROJECT BODY\n');
|
|
84
73
|
const readFile = join(fakeHome, 'work', 'ideas.md');
|
|
85
74
|
writeFileSync(readFile, 'turn off xml closer\n');
|
|
86
75
|
const rendered = renderOnReadDocs(node, readFile, new Set());
|
|
87
|
-
assert.ok(!rendered.includes('NOISY USER BODY'), 'user-global doc
|
|
88
|
-
assert.ok(rendered.includes('LOCAL PROJECT BODY'), 'project doc
|
|
89
|
-
assert.
|
|
90
|
-
assert.ok(!rendered.includes('triggered-by='), 'read target is not repeated on child items');
|
|
91
|
-
assert.ok(!rendered.includes('<auto-loaded-context file='), 'read target is not repeated on the wrapper');
|
|
76
|
+
assert.ok(!rendered.includes('NOISY USER BODY'), 'user-global doc has no explicit file route');
|
|
77
|
+
assert.ok(!rendered.includes('LOCAL PROJECT BODY'), 'project doc also has no implicit file route');
|
|
78
|
+
assert.equal(rendered, '', 'no file context renders without an explicit trigger');
|
|
92
79
|
rmSync(fakeHome, { recursive: true, force: true });
|
|
93
80
|
});
|
|
94
81
|
test('on-read doc surfaces once, stays deduped across a revive(resume), re-surfaces after a fresh launch', () => {
|
|
95
82
|
const node = spawnNode({ kind: 'general', cwd: work, parent: null }).node_id;
|
|
96
|
-
// A
|
|
83
|
+
// A file-routed substrate doc in a `.crouter/memory/` ancestor.
|
|
97
84
|
const memDir = join(work, '.crouter', 'memory');
|
|
98
85
|
mkdirSync(memDir, { recursive: true });
|
|
99
86
|
writeFileSync(join(memDir, 'onread-fixture.md'), '---\nkind: knowledge\n' +
|
|
100
87
|
'when-and-why-to-read: When reading work files, this reference should be read because it is the on-read regression fixture\n' +
|
|
101
|
-
'file-read-visibility: content\n
|
|
88
|
+
'file-read-visibility: content\n' +
|
|
89
|
+
'applies-to: "src/**"\n---\n' +
|
|
102
90
|
`${FIXTURE_BODY}\n`);
|
|
103
91
|
const readFile = join(work, 'src', 'file.ts');
|
|
104
92
|
mkdirSync(join(work, 'src'), { recursive: true });
|
|
@@ -113,7 +101,7 @@ test('on-read doc surfaces once, stays deduped across a revive(resume), re-surfa
|
|
|
113
101
|
// --- dormancy → revive(resume): NEW process rehydrates the set from disk ---
|
|
114
102
|
const seenAfterRevive = loadInjectedDocs(node);
|
|
115
103
|
assert.deepEqual(seenAfterRevive, seen, 'revive(resume) rehydrates the persisted dedup set');
|
|
116
|
-
assert.ok(!renderOnReadDocs(node, readFile, seenAfterRevive).includes(FIXTURE_BODY), 'resumed process does
|
|
104
|
+
assert.ok(!renderOnReadDocs(node, readFile, seenAfterRevive).includes(FIXTURE_BODY), 'resumed process does not re-inject the doc');
|
|
117
105
|
// --- fresh launch (revive resume=false, e.g. a refresh-yield) clears the set ---
|
|
118
106
|
clearInjectedDocs(node);
|
|
119
107
|
const seenFresh = loadInjectedDocs(node);
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
// The
|
|
1
|
+
// The explicit file-context loader resolves a doc's identity with the same
|
|
2
2
|
// explicit-name-then-path-fallback rule the boot resolver uses (schema.ts's
|
|
3
3
|
// resolveDocName), never deriving `name` from the physical path alone.
|
|
4
4
|
//
|
|
5
|
-
// A migrated AGENTS.md doc (`crtr sys sync project-guidance`'s CLAUDE.md/AGENTS.md conversion)
|
|
6
|
-
// is written at `file-read-visibility: content` with an explicit frontmatter
|
|
7
|
-
// `name` (the project label, e.g. `crouter`) so sibling project roots do not
|
|
8
|
-
// collapse onto the shared `AGENTS` leaf. `loadPositionalDoc` must inject the
|
|
9
|
-
// doc under that explicit name, not the path-derived `AGENTS`.
|
|
10
|
-
//
|
|
11
5
|
// Run: node --import tsx/esm --test src/core/__tests__/on-read-identity.test.ts
|
|
12
6
|
import { test, before, beforeEach, after } from 'node:test';
|
|
13
7
|
import assert from 'node:assert/strict';
|
|
@@ -47,22 +41,21 @@ after(() => {
|
|
|
47
41
|
else
|
|
48
42
|
process.env['HOME'] = prevHomeEnv;
|
|
49
43
|
});
|
|
50
|
-
test('
|
|
44
|
+
test('an explicitly routed doc injects on-read under its frontmatter name, not the path-derived one', () => {
|
|
51
45
|
const node = spawnNode({ kind: 'general', cwd: work, parent: null }).node_id;
|
|
52
|
-
// Shape of a `crtr sys sync project-guidance` CLAUDE.md/AGENTS.md migration: physical filename
|
|
53
|
-
// stays AGENTS.md, but frontmatter carries the project's explicit identity.
|
|
54
46
|
const memDir = join(work, '.crouter', 'memory');
|
|
55
47
|
mkdirSync(memDir, { recursive: true });
|
|
56
|
-
writeFileSync(join(memDir, '
|
|
48
|
+
writeFileSync(join(memDir, 'physical-guide.md'), '---\nkind: knowledge\n' +
|
|
57
49
|
'name: acme-project\n' +
|
|
58
|
-
'when-and-why-to-read: When
|
|
59
|
-
'file-read-visibility: content\n
|
|
50
|
+
'when-and-why-to-read: When reading acme source, this knowledge should be read because it carries the project-specific implementation constraints.\n' +
|
|
51
|
+
'file-read-visibility: content\n' +
|
|
52
|
+
'applies-to: "src/**"\n---\n' +
|
|
60
53
|
'ACME PROJECT OPERATING GUIDE\n');
|
|
61
54
|
const readFile = join(work, 'src', 'file.ts');
|
|
62
55
|
mkdirSync(join(work, 'src'), { recursive: true });
|
|
63
56
|
writeFileSync(readFile, 'export const x = 1;\n');
|
|
64
57
|
const rendered = renderOnReadDocs(node, readFile, new Set());
|
|
65
|
-
assert.ok(rendered.includes('ACME PROJECT OPERATING GUIDE'), 'the
|
|
58
|
+
assert.ok(rendered.includes('ACME PROJECT OPERATING GUIDE'), 'the matching explicit route fires');
|
|
66
59
|
assert.ok(rendered.includes('<memory kind="knowledge" name="acme-project"'), `expected explicit frontmatter name "acme-project" in the injected envelope, got: ${rendered}`);
|
|
67
|
-
assert.ok(!rendered.includes('name="
|
|
60
|
+
assert.ok(!rendered.includes('name="physical-guide"'), 'must not fall back to the path-derived name when frontmatter sets one');
|
|
68
61
|
});
|
|
@@ -193,9 +193,9 @@ test('buildPiArgv restores mandatory canvas extensions when persisted launch met
|
|
|
193
193
|
}
|
|
194
194
|
assert.deepEqual(extensions.slice(0, required.size), mandatory, 'mandatory extensions are loaded first and once');
|
|
195
195
|
});
|
|
196
|
-
test('buildPiArgv replaces the
|
|
196
|
+
test('buildPiArgv replaces the previous mandatory prefix with the current canvas hooks', () => {
|
|
197
197
|
const historicalNames = [
|
|
198
|
-
'canvas-
|
|
198
|
+
'canvas-inbox-watcher', 'canvas-stophook', 'canvas-recap',
|
|
199
199
|
'canvas-goal-capture', 'canvas-passive-context', 'canvas-context-intro',
|
|
200
200
|
'canvas-doc-substrate', 'canvas-structured-output', 'canvas-bash-valve',
|
|
201
201
|
];
|
|
@@ -224,6 +224,7 @@ test('tmux installer remaps owned pairs and preserves occupied user bindings', (
|
|
|
224
224
|
const remapped = installTmuxBindings(resolveKeybindings({
|
|
225
225
|
'crtr.tmux.menu.open': ['alt+shift+z'],
|
|
226
226
|
'crtr.tmux.menu.graph': ['shift+z'],
|
|
227
|
+
'crtr.tmux.menu-global.fold-tools': ['alt+shift+f'],
|
|
227
228
|
'crtr.tmux.node.next': [],
|
|
228
229
|
'crtr.tmux.view.next': ['alt+z > n'],
|
|
229
230
|
'crtr.tmux.view.previous': ['alt+z > p'],
|
|
@@ -231,10 +232,15 @@ test('tmux installer remaps owned pairs and preserves occupied user bindings', (
|
|
|
231
232
|
assert.equal(remapped.ok, true, JSON.stringify(remapped.diagnostics));
|
|
232
233
|
assert.equal(tmuxBinding(socket, 'root', 'M-c'), null);
|
|
233
234
|
assert.equal(tmuxBinding(socket, 'root', 'M-]'), null);
|
|
234
|
-
|
|
235
|
+
assert.equal(tmuxBinding(socket, 'root', 'M-F'), null);
|
|
236
|
+
assert.equal(tmuxBinding(socket, 'root', 'M-S-f'), null);
|
|
237
|
+
const foldTools = tmuxBinding(socket, 'root', 'M-S-F') ?? '';
|
|
238
|
+
assert.match(foldTools, /fold-tools/);
|
|
239
|
+
assert.doesNotMatch(foldTools, /#\{pane_id\}/);
|
|
240
|
+
const menu = tmuxBinding(socket, 'root', 'M-S-Z') ?? '';
|
|
235
241
|
assert.match(menu, /graph view/);
|
|
236
242
|
assert.match(menu, /Z/);
|
|
237
|
-
assert.match(menu, /"Alt\+Shift\+Z to close" M-Z refresh-client/);
|
|
243
|
+
assert.match(menu, /"Alt\+Shift\+Z to close" M-S-Z refresh-client/);
|
|
238
244
|
const prefix = tmuxBinding(socket, 'root', 'M-z') ?? '';
|
|
239
245
|
assert.match(prefix, /switch-client/);
|
|
240
246
|
const again = installTmuxBindings(resolveKeybindings({
|
|
@@ -244,11 +250,11 @@ test('tmux installer remaps owned pairs and preserves occupied user bindings', (
|
|
|
244
250
|
'crtr.tmux.view.previous': ['alt+z > p'],
|
|
245
251
|
}));
|
|
246
252
|
assert.equal(again.ok, true, JSON.stringify(again.diagnostics));
|
|
247
|
-
isolatedTmux(socket, ['bind-key', '-T', 'root', 'M-Z', 'display-message', 'user-owned']);
|
|
253
|
+
isolatedTmux(socket, ['bind-key', '-T', 'root', 'M-S-Z', 'display-message', 'user-owned']);
|
|
248
254
|
const occupied = installTmuxBindings(resolveKeybindings({ 'crtr.tmux.menu.open': ['alt+shift+z'] }));
|
|
249
255
|
assert.equal(occupied.ok, true);
|
|
250
256
|
assert.ok(occupied.diagnostics.some((diagnostic) => diagnostic.code === 'occupied' && diagnostic.bindingId === 'crtr.tmux.menu.open'));
|
|
251
|
-
assert.match(tmuxBinding(socket, 'root', 'M-Z') ?? '', /user-owned/);
|
|
257
|
+
assert.match(tmuxBinding(socket, 'root', 'M-S-Z') ?? '', /user-owned/);
|
|
252
258
|
}
|
|
253
259
|
finally {
|
|
254
260
|
if (priorTmux === undefined)
|
|
@@ -386,15 +386,15 @@ test('close still blocks on real .crouter/memory/ work — a tracked-file edit a
|
|
|
386
386
|
run(['config', 'user.email', 'test@example.com'], memoryRepo);
|
|
387
387
|
run(['config', 'user.name', 'Test'], memoryRepo);
|
|
388
388
|
mkdirSync(join(memoryRepo, '.crouter', 'memory'), { recursive: true });
|
|
389
|
-
writeFileSync(join(memoryRepo, '.crouter', 'memory', '
|
|
389
|
+
writeFileSync(join(memoryRepo, '.crouter', 'memory', 'tracked.md'), 'tracked memory doc\n');
|
|
390
390
|
run(['add', '.'], memoryRepo);
|
|
391
391
|
run(['commit', '-m', 'init with tracked memory'], memoryRepo);
|
|
392
392
|
const editNodeId = 'wt-real-crouter-memory-edit';
|
|
393
393
|
const editWt = createManagedWorktree(memoryRepo, editNodeId);
|
|
394
394
|
createNode(node(editNodeId, { cwd: editWt.path, managed_worktree: editWt }));
|
|
395
|
-
writeFileSync(join(editWt.path, '.crouter', 'memory', '
|
|
395
|
+
writeFileSync(join(editWt.path, '.crouter', 'memory', 'tracked.md'), 'edited tracked memory doc\n');
|
|
396
396
|
const editStatus = gitSync(['status', '--porcelain'], editWt.path).stdout;
|
|
397
|
-
assert.ok(editStatus.includes('M .crouter/memory/
|
|
397
|
+
assert.ok(editStatus.includes('M .crouter/memory/tracked.md'), 'a tracked .crouter/memory/ edit must still show as modified — exclude can never hide a tracked-file change');
|
|
398
398
|
assert.throws(() => closeManagedWorktree(editNodeId), (error) => error instanceof Error && 'code' in error && error.code === 'dirty_worktree');
|
|
399
399
|
const newDocNodeId = 'wt-real-crouter-memory-new-doc';
|
|
400
400
|
const newDocWt = createManagedWorktree(memoryRepo, newDocNodeId);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const CANVAS_EXTENSION_NAMES: readonly ["canvas-inbox-watcher", "canvas-stophook", "canvas-recap", "canvas-goal-capture", "canvas-passive-context", "canvas-context-intro", "canvas-doc-substrate", "canvas-structured-output", "canvas-bash-valve"];
|
|
1
|
+
export declare const CANVAS_EXTENSION_NAMES: readonly ["canvas-inbox-watcher", "canvas-stophook", "canvas-recap", "canvas-goal-capture", "canvas-passive-context", "canvas-context-intro", "canvas-tool-guide", "canvas-doc-substrate", "canvas-structured-output", "canvas-bash-valve"];
|
|
2
2
|
/** Remove a complete persisted canvas prefix while retaining every genuine
|
|
3
3
|
* caller/kind extra, including a custom extension whose basename happens to
|
|
4
4
|
* resemble a canvas hook. */
|
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
// Canvas-extension identity is structural, not basename-based: old runtime roots
|
|
2
|
-
// are recognized only when they carry
|
|
2
|
+
// are recognized only when they carry a complete shipped canvas prefix.
|
|
3
3
|
// canvas-inbox-watcher must load before canvas-stophook: at agent_settled it
|
|
4
4
|
// commits the cursor the stophook then reads to decide idle release.
|
|
5
5
|
export const CANVAS_EXTENSION_NAMES = [
|
|
6
6
|
'canvas-inbox-watcher', 'canvas-stophook', 'canvas-recap',
|
|
7
7
|
'canvas-goal-capture', 'canvas-passive-context', 'canvas-context-intro',
|
|
8
|
-
'canvas-
|
|
8
|
+
'canvas-tool-guide', 'canvas-doc-substrate', 'canvas-structured-output',
|
|
9
|
+
'canvas-bash-valve',
|
|
9
10
|
];
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const HISTORICAL_CANVAS_EXTENSION_PREFIXES = [
|
|
12
|
+
[
|
|
13
|
+
'canvas-inbox-watcher', 'canvas-stophook', 'canvas-recap',
|
|
14
|
+
'canvas-goal-capture', 'canvas-passive-context', 'canvas-context-intro',
|
|
15
|
+
'canvas-doc-substrate', 'canvas-structured-output', 'canvas-bash-valve',
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
'canvas-stophook', 'canvas-inbox-watcher', 'canvas-recap',
|
|
19
|
+
'canvas-goal-capture', 'canvas-passive-context', 'canvas-context-intro',
|
|
20
|
+
'canvas-doc-substrate', 'canvas-structured-output', 'canvas-bash-valve',
|
|
21
|
+
],
|
|
14
22
|
];
|
|
15
23
|
function matchesExtensionPrefix(extensions, names) {
|
|
24
|
+
if (extensions.length < names.length)
|
|
25
|
+
return false;
|
|
16
26
|
const paths = extensions.slice(0, names.length);
|
|
17
27
|
const first = paths[0].replaceAll('\\', '/');
|
|
18
28
|
const marker = '/pi-extensions/';
|
|
@@ -24,11 +34,8 @@ function matchesExtensionPrefix(extensions, names) {
|
|
|
24
34
|
return names.every((name, index) => paths[index].replaceAll('\\', '/') === `${root}${name}${suffix}`);
|
|
25
35
|
}
|
|
26
36
|
function extensionPrefixLength(extensions) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return matchesExtensionPrefix(extensions, CANVAS_EXTENSION_NAMES) || matchesExtensionPrefix(extensions, LEGACY_CANVAS_EXTENSION_NAMES)
|
|
30
|
-
? CANVAS_EXTENSION_NAMES.length
|
|
31
|
-
: 0;
|
|
37
|
+
const prefixes = [CANVAS_EXTENSION_NAMES, ...HISTORICAL_CANVAS_EXTENSION_PREFIXES];
|
|
38
|
+
return prefixes.find((names) => matchesExtensionPrefix(extensions, names))?.length ?? 0;
|
|
32
39
|
}
|
|
33
40
|
/** Remove a complete persisted canvas prefix while retaining every genuine
|
|
34
41
|
* caller/kind extra, including a custom extension whose basename happens to
|
|
@@ -23,9 +23,8 @@ export declare function fullName(node: NamedNode): string;
|
|
|
23
23
|
* badge. */
|
|
24
24
|
export declare function personaLabel(meta: Pick<NodeMeta, 'kind' | 'mode'>): string;
|
|
25
25
|
/** The pi session display name — the editor label in the top-left. It carries
|
|
26
|
-
* ONE name:
|
|
27
|
-
*
|
|
28
|
-
* the chip doesn't read as two names side by side. Persona lives in
|
|
29
|
-
* editor's bottom-right badge. Recomputed from meta on every revive
|
|
30
|
-
* live when naming generates the first task description. */
|
|
26
|
+
* ONE name: a person's explicit handle when present, otherwise the generated
|
|
27
|
+
* description of the node's first task. Unlike `fullName` it never prints
|
|
28
|
+
* both, so the chip doesn't read as two names side by side. Persona lives in
|
|
29
|
+
* the editor's bottom-right badge. Recomputed from meta on every revive. */
|
|
31
30
|
export declare function editorLabel(meta: NodeMeta): string;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
//
|
|
3
3
|
// One source of truth for "what do we call this node on screen". `fullName`
|
|
4
4
|
// combines the node's explicit handle (meta.name) with the pi-generated
|
|
5
|
-
// description of its first task (meta.description)
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// description of its first task (meta.description) for canvas and tmux surfaces.
|
|
6
|
+
// `editorLabel` keeps the editor chip compact by choosing the explicit handle
|
|
7
|
+
// when a person supplied one, otherwise the generated description.
|
|
8
8
|
/** Nerd Font glyph for a node's mode in its persona badge. A base worker reads
|
|
9
9
|
* as a single figure (nf-fa-user, U+F007); an orchestrator reads as a fan-out
|
|
10
10
|
* hierarchy (nf-fa-sitemap, U+F0E8). */
|
|
@@ -30,14 +30,14 @@ export function personaLabel(meta) {
|
|
|
30
30
|
return `${modeIcon(meta.mode)} ${meta.kind}`;
|
|
31
31
|
}
|
|
32
32
|
/** The pi session display name — the editor label in the top-left. It carries
|
|
33
|
-
* ONE name:
|
|
34
|
-
*
|
|
35
|
-
* the chip doesn't read as two names side by side. Persona lives in
|
|
36
|
-
* editor's bottom-right badge. Recomputed from meta on every revive
|
|
37
|
-
* live when naming generates the first task description. */
|
|
33
|
+
* ONE name: a person's explicit handle when present, otherwise the generated
|
|
34
|
+
* description of the node's first task. Unlike `fullName` it never prints
|
|
35
|
+
* both, so the chip doesn't read as two names side by side. Persona lives in
|
|
36
|
+
* the editor's bottom-right badge. Recomputed from meta on every revive. */
|
|
38
37
|
export function editorLabel(meta) {
|
|
39
38
|
const icon = (meta.icon ?? '').trim();
|
|
39
|
+
const handle = meta.name && meta.name !== meta.kind ? meta.name.trim() : '';
|
|
40
40
|
const desc = (meta.description ?? '').trim();
|
|
41
|
-
const name = desc !== '' ? desc :
|
|
41
|
+
const name = handle !== '' ? handle : desc !== '' ? desc : meta.name;
|
|
42
42
|
return icon !== '' ? `${icon} ${name}` : name;
|
|
43
43
|
}
|
|
@@ -27,7 +27,7 @@ const CONFIGURED_LEAF_KEYS = new Set([...LEAF_KEYS_COMMON, 'rest']);
|
|
|
27
27
|
const ROOT_ENTRY_KEYS = new Set(['concept', 'description', 'whenToUse']);
|
|
28
28
|
const FILE_ENCODINGS = new Set(['text', 'base64']);
|
|
29
29
|
const PARAM_KEYS_BY_KIND = {
|
|
30
|
-
positional: new Set(['kind', 'name', 'type', 'required', 'constraint', 'encoding', 'defaultFromEnv']),
|
|
30
|
+
positional: new Set(['kind', 'name', 'type', 'required', 'constraint', 'repeatable', 'encoding', 'defaultFromEnv']),
|
|
31
31
|
flag: new Set([
|
|
32
32
|
'kind',
|
|
33
33
|
'name',
|
|
@@ -137,18 +137,31 @@ function validateParams(raw, path, issue) {
|
|
|
137
137
|
issue('command_node_invalid', `positional type must be string|path`, String(p['type']), 'string | path | omitted', 'Fix the positional type.', `${pp}.type`);
|
|
138
138
|
return null;
|
|
139
139
|
}
|
|
140
|
+
let repeatable = false;
|
|
141
|
+
if (p['repeatable'] !== undefined) {
|
|
142
|
+
if (typeof p['repeatable'] !== 'boolean') {
|
|
143
|
+
issue('command_node_invalid', `repeatable must be a boolean`, typeName(p['repeatable']), 'true | false | omitted', 'Fix repeatable.', `${pp}.repeatable`);
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
repeatable = p['repeatable'];
|
|
147
|
+
}
|
|
140
148
|
const encoding = validateFileEncoding(p, pp, issue);
|
|
141
149
|
if (encoding === null)
|
|
142
150
|
return null;
|
|
143
151
|
const envDefault = validateDefaultFromEnv(p, pp, issue);
|
|
144
152
|
if (envDefault === null)
|
|
145
153
|
return null;
|
|
154
|
+
if (repeatable && envDefault !== undefined) {
|
|
155
|
+
issue('command_node_invalid', `repeatable positional must not declare defaultFromEnv`, name, 'no defaultFromEnv on a repeatable positional (values accumulate into an array)', 'Remove defaultFromEnv, or drop repeatable.', `${pp}.defaultFromEnv`);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
146
158
|
out.push({
|
|
147
159
|
kind: 'positional',
|
|
148
160
|
name,
|
|
149
161
|
...(p['type'] !== undefined ? { type: p['type'] } : {}),
|
|
150
162
|
required: p['required'],
|
|
151
163
|
constraint: p['constraint'],
|
|
164
|
+
...(repeatable ? { repeatable: true } : {}),
|
|
152
165
|
...(encoding !== undefined ? { encoding } : {}),
|
|
153
166
|
...(envDefault !== undefined ? { defaultFromEnv: envDefault } : {}),
|
|
154
167
|
});
|
|
@@ -251,7 +264,7 @@ function validateFileEncoding(p, pp, issue) {
|
|
|
251
264
|
* so it is meaningful only on a string|path param, and it cannot coexist with a
|
|
252
265
|
* static `default` (two competing sources for one slot is an authoring error,
|
|
253
266
|
* not a precedence puzzle for the reader). The caller checks `repeatable`
|
|
254
|
-
* separately, where that
|
|
267
|
+
* separately, where that param is in scope: a repeatable param accumulates an
|
|
255
268
|
* ARRAY, and one env string cannot honestly fill it. Returns the env var name,
|
|
256
269
|
* undefined when absent, or null when invalid. */
|
|
257
270
|
function validateDefaultFromEnv(p, pp, issue) {
|
|
@@ -716,11 +729,11 @@ function validateRestMapping(raw, basePath, params, issue) {
|
|
|
716
729
|
return null;
|
|
717
730
|
}
|
|
718
731
|
}
|
|
719
|
-
// A repeatable
|
|
732
|
+
// A repeatable param's parsed value is an array; only JSON body placement
|
|
720
733
|
// can carry it faithfully (query/header/path would String() it into "a,b").
|
|
721
734
|
const mappedParam = paramsByName.get(paramName);
|
|
722
|
-
if (mappedParam.kind === 'flag' && mappedParam.repeatable === true && inValue !== 'body') {
|
|
723
|
-
issue('command_rest_invalid', `repeatable
|
|
735
|
+
if ((mappedParam.kind === 'flag' || mappedParam.kind === 'positional') && mappedParam.repeatable === true && inValue !== 'body') {
|
|
736
|
+
issue('command_rest_invalid', `repeatable param must use body placement`, `${paramName}: in "${inValue}"`, 'in: "body" (array values ship as a JSON array)', `Map "${paramName}" to body placement.`, `${path}.params.${paramName}.in`);
|
|
724
737
|
return null;
|
|
725
738
|
}
|
|
726
739
|
// Collect body params for coalesced source validation
|
package/dist/core/command.js
CHANGED
|
@@ -283,7 +283,8 @@ export async function parseArgv(params, tokens, options) {
|
|
|
283
283
|
result[flagNameToKey(f.name)] = f.default;
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
-
|
|
286
|
+
const positionalValues = [];
|
|
287
|
+
const repeatablePositional = positionalParam?.repeatable === true;
|
|
287
288
|
let positionalWasSupplied = false; // Track if positional came from argv (not stdin)
|
|
288
289
|
let forcedPositional = false; // true after bare --
|
|
289
290
|
let i = 0;
|
|
@@ -385,7 +386,13 @@ export async function parseArgv(params, tokens, options) {
|
|
|
385
386
|
continue;
|
|
386
387
|
}
|
|
387
388
|
// Positional (or token after --)
|
|
388
|
-
if (
|
|
389
|
+
if (repeatablePositional && positionalParam !== undefined) {
|
|
390
|
+
positionalValues.push(token);
|
|
391
|
+
positionalWasSupplied = true;
|
|
392
|
+
i++;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (positionalValues.length > 0) {
|
|
389
396
|
// A leaf that also declares a `stdin` param treats its stdin value as
|
|
390
397
|
// write-only/sensitive by convention (`profile env set`'s value, etc.).
|
|
391
398
|
// An unexpected extra positional here is the single most likely misuse
|
|
@@ -404,13 +411,15 @@ export async function parseArgv(params, tokens, options) {
|
|
|
404
411
|
if (positionalParam === undefined && stdinParam === undefined) {
|
|
405
412
|
throw parseArgvError('bad_invocation', `this leaf takes no positional arguments: ${token}`, token, undefined, 'Use --flag for parameters. Run -h for the schema.');
|
|
406
413
|
}
|
|
407
|
-
|
|
414
|
+
positionalValues.push(token);
|
|
408
415
|
positionalWasSupplied = true;
|
|
409
416
|
i++;
|
|
410
417
|
}
|
|
411
418
|
// Assign positional (already tracked in provided set above)
|
|
412
|
-
if (
|
|
413
|
-
result[flagNameToKey(positionalParam.name)] =
|
|
419
|
+
if (positionalValues.length > 0 && positionalParam !== undefined) {
|
|
420
|
+
result[flagNameToKey(positionalParam.name)] = repeatablePositional
|
|
421
|
+
? positionalValues
|
|
422
|
+
: positionalValues[0];
|
|
414
423
|
}
|
|
415
424
|
// Resolve stdin if declared. A positional token (when there's no dedicated
|
|
416
425
|
// positional param to claim it) satisfies the stdin param directly, so
|
|
@@ -421,7 +430,8 @@ export async function parseArgv(params, tokens, options) {
|
|
|
421
430
|
// and name both sources.
|
|
422
431
|
let stdinWasSupplied = false;
|
|
423
432
|
if (stdinParam !== undefined) {
|
|
424
|
-
if (
|
|
433
|
+
if (positionalValues.length > 0 && positionalParam === undefined) {
|
|
434
|
+
const positionalValue = positionalValues[0];
|
|
425
435
|
if (positionalValue === '-') {
|
|
426
436
|
const raw = await readStdinRaw();
|
|
427
437
|
// Empty required stdin is NOT thrown here: it is left unset so the
|
|
@@ -492,7 +502,7 @@ export async function parseArgv(params, tokens, options) {
|
|
|
492
502
|
const envValue = process.env[p.defaultFromEnv];
|
|
493
503
|
if (envValue === undefined || envValue === '')
|
|
494
504
|
continue;
|
|
495
|
-
result[key] = envValue;
|
|
505
|
+
result[key] = p.kind === 'positional' && p.repeatable === true ? [envValue] : envValue;
|
|
496
506
|
provided.add(key);
|
|
497
507
|
}
|
|
498
508
|
// Validate required params — COMPLETE, never first-failure. Drip-feeding one
|
|
@@ -190,6 +190,8 @@ function materializeBodyValue(spec, param, value) {
|
|
|
190
190
|
if (param.kind === 'context-file')
|
|
191
191
|
return value;
|
|
192
192
|
if ((param.kind === 'positional' || param.kind === 'flag') && param.type === 'path') {
|
|
193
|
+
if (Array.isArray(value))
|
|
194
|
+
return value.map((item) => materializeBodyValue(spec, param, item));
|
|
193
195
|
const filePath = String(value);
|
|
194
196
|
const resolved = resolvePath(process.cwd(), filePath);
|
|
195
197
|
let stat;
|
package/dist/core/help.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface Field {
|
|
|
6
6
|
* token caps. Lives here, never in a separate Preconditions section. */
|
|
7
7
|
constraint: string;
|
|
8
8
|
}
|
|
9
|
-
/** Positional argument — at most one per leaf. */
|
|
9
|
+
/** Positional argument — at most one parameter per leaf; optionally repeatable. */
|
|
10
10
|
export interface PositionalParam {
|
|
11
11
|
kind: 'positional';
|
|
12
12
|
name: string;
|
|
@@ -14,6 +14,8 @@ export interface PositionalParam {
|
|
|
14
14
|
type?: 'string' | 'path';
|
|
15
15
|
required: boolean;
|
|
16
16
|
constraint: string;
|
|
17
|
+
/** When true, every positional token is collected into an array in argv order. */
|
|
18
|
+
repeatable?: boolean;
|
|
17
19
|
/** Local-file materialization contract, valid only on a `path` param. The
|
|
18
20
|
* CLI-side value is a path to a local file; the invoker reads that file and
|
|
19
21
|
* sends its CONTENT as the mapped value — 'text' as UTF-8, 'base64' as the
|
package/dist/core/help.js
CHANGED
|
@@ -175,7 +175,7 @@ export function renderBranch(h) {
|
|
|
175
175
|
/** Build the display label for a param entry (left column). */
|
|
176
176
|
function paramLabel(p) {
|
|
177
177
|
if (p.kind === 'positional')
|
|
178
|
-
return p.name.toUpperCase()
|
|
178
|
+
return `${p.name.toUpperCase()}${p.repeatable === true ? '...' : ''}`;
|
|
179
179
|
if (p.kind === 'stdin')
|
|
180
180
|
return 'stdin';
|
|
181
181
|
if (p.kind === 'context-file')
|
|
@@ -207,7 +207,10 @@ function envDefaultNote(envVar) {
|
|
|
207
207
|
function paramDesc(p) {
|
|
208
208
|
const req = p.required ? 'required' : 'optional';
|
|
209
209
|
if (p.kind === 'positional') {
|
|
210
|
-
|
|
210
|
+
const repeatable = p.repeatable === true
|
|
211
|
+
? ' Repeatable — pass multiple values to accumulate in argv order.'
|
|
212
|
+
: '';
|
|
213
|
+
return `positional, ${req}.${fileEncodingNote(p.encoding)}${envDefaultNote(p.defaultFromEnv)} ${p.constraint}${repeatable}`;
|
|
211
214
|
}
|
|
212
215
|
if (p.kind === 'stdin')
|
|
213
216
|
return `${req}. ${p.constraint}`;
|
|
@@ -13,9 +13,12 @@ void rejectedBindingId;
|
|
|
13
13
|
function ids(prefix, actions) {
|
|
14
14
|
return actions.map((action) => `${prefix}.${action}`);
|
|
15
15
|
}
|
|
16
|
+
function pairedMenuIds(actions) {
|
|
17
|
+
return actions.flatMap((action) => [`crtr.tmux.menu.${action}`, `crtr.tmux.menu-global.${action}`]);
|
|
18
|
+
}
|
|
16
19
|
const REQUIRED_IDS = [
|
|
17
|
-
|
|
18
|
-
...
|
|
20
|
+
'crtr.tmux.menu.open',
|
|
21
|
+
...pairedMenuIds(['promote', 'resume', 'fork', 'demote', 'detach', 'close-subtree', 'background-bash', 'file-review', 'pin', 'show', 'view-settings', 'fold-tools', 'metadata', 'context', 'providers', 'graph', 'focus-manager', 'copy-id', 'reload-viewer', 'issues', 'issue.idea', 'issue.bug']),
|
|
19
22
|
...ids('crtr.tmux.node', ['next', 'previous']),
|
|
20
23
|
'crtr.tmux.inbox.toggle',
|
|
21
24
|
...ids('crtr.tmux.view', ['next', 'previous']),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BindingDefinition, SafetyRequirement } from './types.js';
|
|
2
2
|
type TmuxMenuAction = 'open' | 'show' | 'view-settings' | 'fold-tools' | 'metadata' | 'promote' | 'resume' | 'fork' | 'demote' | 'detach' | 'close-subtree' | 'background-bash' | 'file-review' | 'pin' | 'reload-viewer' | 'context' | 'providers' | 'graph' | 'focus-manager' | 'copy-id' | 'issues';
|
|
3
|
+
type TmuxMenuEntryAction = Exclude<TmuxMenuAction, 'open'> | `issue.${'idea' | 'bug'}`;
|
|
3
4
|
type GraphAction = 'down' | 'up' | 'first' | 'last' | 'collapse-or-manager' | 'expand-or-child' | 'focus' | 'revive' | 'focus-manager' | 'close-subtree' | 'dismiss';
|
|
4
5
|
type InboxAction = 'down' | 'up' | 'open' | 'reply' | 'react' | 'filter' | 'refresh' | 'connect-linkedin' | 'connect-gmail' | 'quit' | 'submit-reply' | 'cancel' | 'reaction-previous' | 'reaction-next' | 'submit-reaction';
|
|
5
6
|
type LinkedinAction = Exclude<InboxAction, 'filter' | 'connect-linkedin' | 'connect-gmail'>;
|
|
@@ -8,8 +9,8 @@ type ProfileMenuAction = 'up' | 'down' | 'select' | 'accept-default' | 'abort' |
|
|
|
8
9
|
type SetupAction = 'cancel' | 'tab-next' | 'tab-previous' | 'open' | 'list-down' | 'list-up' | 'toggle' | 'details' | 'advance' | 'cell-previous' | 'cell-next';
|
|
9
10
|
type SetupKeybindingAction = 'edit' | 'commit-edit' | 'cancel-edit' | 'unbind' | 'reset-row' | 'reset-all' | 'save' | 'discard' | 'page-up' | 'page-down' | 'filter' | 'filter-delete' | 'filter-clear';
|
|
10
11
|
type SetupModelsAction = 'open' | 'commit' | 'cancel' | 'scope-next';
|
|
11
|
-
export type BindingId = `crtr.tmux.menu.${TmuxMenuAction}` | `crtr.tmux.menu.issue.${'idea' | 'bug'}` | `crtr.tmux.node.${'next' | 'previous'}` | `crtr.tmux.view.${'next' | 'previous'}` | 'crtr.tmux.inbox.toggle' | `crtr.attach.${'detach' | 'clear-or-detach' | 'graph.toggle' | 'model-ladder.next' | 'model-ladder.previous' | 'command.inspect' | 'file-review'}` | 'crtr.mode.cycle' | `crtr.graph.${GraphAction}` | `crtr.graph.confirm.${'accept' | 'cancel'}` | `crtr.view.host.${'force-quit' | 'editor.backspace'}` | `crtr.view.canvas.${'down' | 'up' | 'refresh' | 'quit'}` | `crtr.view.chat.${'submit' | 'contextual-cancel' | 'reconnect' | 'quit'}` | `crtr.view.git-pr.${'down' | 'up' | 'refresh' | 'quit'}` | `crtr.view.inbox.${InboxAction}` | `crtr.view.linkedin.${LinkedinAction}` | `crtr.view.prompt-review.${'down' | 'up' | 'compare-previous' | 'compare-next' | 'toggle-mode' | 'expand' | 'export' | 'refresh' | 'quit'}` | `crtr.view.workspace-sidebar.${'down' | 'up' | 'open' | 'refresh' | 'quit'}` | `crtr.view.inspector.${'down' | 'up' | 'section-next' | 'section-prev' | 'page-down' | 'page-up' | 'toggle-detail' | 'cancel-item' | 'open-log' | 'confirm' | 'dismiss' | 'retarget' | 'refresh' | 'quit'}` | `crtr.view-picker.${'down' | 'up' | 'run' | 'quit'}` | `crtr.browse.${BrowseAction}` | `crtr.browse.search.${'cancel' | 'commit' | 'delete' | 'quit'}` | `crtr.profile.menu.${ProfileMenuAction}` | `crtr.profile.search.${'up' | 'down' | 'pick' | 'back' | 'delete' | 'abort'}` | `crtr.setup.${SetupAction}` | `crtr.setup.confirm.${'accept' | 'back'}` | `crtr.setup.keybindings.${SetupKeybindingAction}` | `crtr.setup.models.${SetupModelsAction}`;
|
|
12
|
-
export declare const BINDING_CATALOG: readonly [BindingDefinition<"crtr.tmux.menu.open">, BindingDefinition<
|
|
12
|
+
export type BindingId = `crtr.tmux.menu.${TmuxMenuAction}` | `crtr.tmux.menu.issue.${'idea' | 'bug'}` | `crtr.tmux.menu-global.${TmuxMenuEntryAction}` | `crtr.tmux.node.${'next' | 'previous'}` | `crtr.tmux.view.${'next' | 'previous'}` | 'crtr.tmux.inbox.toggle' | `crtr.attach.${'detach' | 'clear-or-detach' | 'graph.toggle' | 'model-ladder.next' | 'model-ladder.previous' | 'command.inspect' | 'file-review'}` | 'crtr.mode.cycle' | `crtr.graph.${GraphAction}` | `crtr.graph.confirm.${'accept' | 'cancel'}` | `crtr.view.host.${'force-quit' | 'editor.backspace'}` | `crtr.view.canvas.${'down' | 'up' | 'refresh' | 'quit'}` | `crtr.view.chat.${'submit' | 'contextual-cancel' | 'reconnect' | 'quit'}` | `crtr.view.git-pr.${'down' | 'up' | 'refresh' | 'quit'}` | `crtr.view.inbox.${InboxAction}` | `crtr.view.linkedin.${LinkedinAction}` | `crtr.view.prompt-review.${'down' | 'up' | 'compare-previous' | 'compare-next' | 'toggle-mode' | 'expand' | 'export' | 'refresh' | 'quit'}` | `crtr.view.workspace-sidebar.${'down' | 'up' | 'open' | 'refresh' | 'quit'}` | `crtr.view.inspector.${'down' | 'up' | 'section-next' | 'section-prev' | 'page-down' | 'page-up' | 'toggle-detail' | 'cancel-item' | 'open-log' | 'confirm' | 'dismiss' | 'retarget' | 'refresh' | 'quit'}` | `crtr.view-picker.${'down' | 'up' | 'run' | 'quit'}` | `crtr.browse.${BrowseAction}` | `crtr.browse.search.${'cancel' | 'commit' | 'delete' | 'quit'}` | `crtr.profile.menu.${ProfileMenuAction}` | `crtr.profile.search.${'up' | 'down' | 'pick' | 'back' | 'delete' | 'abort'}` | `crtr.setup.${SetupAction}` | `crtr.setup.confirm.${'accept' | 'back'}` | `crtr.setup.keybindings.${SetupKeybindingAction}` | `crtr.setup.models.${SetupModelsAction}`;
|
|
13
|
+
export declare const BINDING_CATALOG: readonly [BindingDefinition<"crtr.tmux.menu.open">, ...(BindingDefinition<BindingId> | BindingDefinition<"crtr.tmux.node.next"> | BindingDefinition<"crtr.tmux.node.previous"> | BindingDefinition<"crtr.tmux.inbox.toggle"> | BindingDefinition<"crtr.tmux.view.next"> | BindingDefinition<"crtr.tmux.view.previous"> | BindingDefinition<"crtr.attach.detach"> | BindingDefinition<"crtr.attach.clear-or-detach"> | BindingDefinition<"crtr.attach.graph.toggle"> | BindingDefinition<"crtr.attach.model-ladder.next"> | BindingDefinition<"crtr.attach.model-ladder.previous"> | BindingDefinition<"crtr.attach.command.inspect"> | BindingDefinition<"crtr.attach.file-review"> | BindingDefinition<"crtr.mode.cycle"> | BindingDefinition<"crtr.graph.down"> | BindingDefinition<"crtr.graph.up"> | BindingDefinition<"crtr.graph.first"> | BindingDefinition<"crtr.graph.last"> | BindingDefinition<"crtr.graph.collapse-or-manager"> | BindingDefinition<"crtr.graph.expand-or-child"> | BindingDefinition<"crtr.graph.focus"> | BindingDefinition<"crtr.graph.revive"> | BindingDefinition<"crtr.graph.focus-manager"> | BindingDefinition<"crtr.graph.close-subtree"> | BindingDefinition<"crtr.graph.dismiss"> | BindingDefinition<"crtr.graph.confirm.accept"> | BindingDefinition<"crtr.graph.confirm.cancel"> | BindingDefinition<"crtr.view.host.force-quit"> | BindingDefinition<"crtr.view.host.editor.backspace"> | BindingDefinition<"crtr.view.chat.submit"> | BindingDefinition<"crtr.view.chat.contextual-cancel"> | BindingDefinition<"crtr.view.chat.reconnect"> | BindingDefinition<"crtr.view.chat.quit"> | BindingDefinition<"crtr.view.prompt-review.down"> | BindingDefinition<"crtr.view.prompt-review.up"> | BindingDefinition<"crtr.view.prompt-review.compare-previous"> | BindingDefinition<"crtr.view.prompt-review.compare-next"> | BindingDefinition<"crtr.view.prompt-review.toggle-mode"> | BindingDefinition<"crtr.view.prompt-review.expand"> | BindingDefinition<"crtr.view.prompt-review.export"> | BindingDefinition<"crtr.view.prompt-review.refresh"> | BindingDefinition<"crtr.view.prompt-review.quit"> | BindingDefinition<"crtr.view.workspace-sidebar.down"> | BindingDefinition<"crtr.view.workspace-sidebar.up"> | BindingDefinition<"crtr.view.workspace-sidebar.open"> | BindingDefinition<"crtr.view.workspace-sidebar.refresh"> | BindingDefinition<"crtr.view.workspace-sidebar.quit"> | BindingDefinition<"crtr.view.inspector.down"> | BindingDefinition<"crtr.view.inspector.up"> | BindingDefinition<"crtr.view.inspector.section-next"> | BindingDefinition<"crtr.view.inspector.section-prev"> | BindingDefinition<"crtr.view.inspector.page-down"> | BindingDefinition<"crtr.view.inspector.page-up"> | BindingDefinition<"crtr.view.inspector.toggle-detail"> | BindingDefinition<"crtr.view.inspector.cancel-item"> | BindingDefinition<"crtr.view.inspector.open-log"> | BindingDefinition<"crtr.view.inspector.confirm"> | BindingDefinition<"crtr.view.inspector.dismiss"> | BindingDefinition<"crtr.view.inspector.retarget"> | BindingDefinition<"crtr.view.inspector.refresh"> | BindingDefinition<"crtr.view.inspector.quit"> | BindingDefinition<"crtr.view-picker.down"> | BindingDefinition<"crtr.view-picker.up"> | BindingDefinition<"crtr.view-picker.run"> | BindingDefinition<"crtr.view-picker.quit"> | BindingDefinition<"crtr.browse.quit"> | BindingDefinition<"crtr.browse.down"> | BindingDefinition<"crtr.browse.up"> | BindingDefinition<"crtr.browse.first"> | BindingDefinition<"crtr.browse.last"> | BindingDefinition<"crtr.browse.expand-or-child"> | BindingDefinition<"crtr.browse.collapse-or-parent"> | BindingDefinition<"crtr.browse.toggle-fold"> | BindingDefinition<"crtr.browse.tab-next"> | BindingDefinition<"crtr.browse.tab-previous"> | BindingDefinition<"crtr.browse.tab.2" | "crtr.browse.tab.1" | "crtr.browse.tab.3" | "crtr.browse.tab.4" | "crtr.browse.tab.5"> | BindingDefinition<"crtr.browse.close-subtree"> | BindingDefinition<"crtr.browse.confirm-close"> | BindingDefinition<"crtr.browse.sort"> | BindingDefinition<"crtr.browse.scope"> | BindingDefinition<"crtr.browse.profile"> | BindingDefinition<"crtr.browse.residents"> | BindingDefinition<"crtr.browse.preview"> | BindingDefinition<"crtr.browse.search"> | BindingDefinition<"crtr.browse.revive-now"> | BindingDefinition<"crtr.browse.focus"> | BindingDefinition<"crtr.browse.focus-background"> | BindingDefinition<"crtr.browse.search.cancel"> | BindingDefinition<"crtr.browse.search.commit"> | BindingDefinition<"crtr.browse.search.delete"> | BindingDefinition<"crtr.browse.search.quit"> | BindingDefinition<"crtr.profile.menu.up"> | BindingDefinition<"crtr.profile.menu.down"> | BindingDefinition<"crtr.profile.menu.select"> | BindingDefinition<"crtr.profile.menu.accept-default"> | BindingDefinition<"crtr.profile.menu.abort"> | BindingDefinition<"crtr.profile.menu.slot.2" | "crtr.profile.menu.slot.1" | "crtr.profile.menu.slot.3" | "crtr.profile.menu.slot.4" | "crtr.profile.menu.slot.5" | "crtr.profile.menu.slot.9" | "crtr.profile.menu.slot.8" | "crtr.profile.menu.slot.7" | "crtr.profile.menu.slot.6">)[], BindingDefinition<"crtr.profile.menu.create">, BindingDefinition<"crtr.profile.menu.search-existing">, BindingDefinition<"crtr.profile.menu.use-root">, BindingDefinition<"crtr.profile.menu.set-default">, BindingDefinition<"crtr.profile.menu.add-directory">, BindingDefinition<"crtr.profile.menu.unpin">, BindingDefinition<"crtr.profile.search.up">, BindingDefinition<"crtr.profile.search.down">, BindingDefinition<"crtr.profile.search.pick">, BindingDefinition<"crtr.profile.search.back">, BindingDefinition<"crtr.profile.search.delete">, BindingDefinition<"crtr.profile.search.abort">, BindingDefinition<"crtr.setup.cancel">, BindingDefinition<"crtr.setup.tab-next">, BindingDefinition<"crtr.setup.tab-previous">, BindingDefinition<"crtr.setup.open">, BindingDefinition<"crtr.setup.list-down">, BindingDefinition<"crtr.setup.list-up">, BindingDefinition<"crtr.setup.toggle">, BindingDefinition<"crtr.setup.details">, BindingDefinition<"crtr.setup.advance">, BindingDefinition<"crtr.setup.confirm.accept">, BindingDefinition<"crtr.setup.confirm.back">, BindingDefinition<"crtr.setup.keybindings.edit">, BindingDefinition<"crtr.setup.keybindings.commit-edit">, BindingDefinition<"crtr.setup.keybindings.cancel-edit">, BindingDefinition<"crtr.setup.keybindings.unbind">, BindingDefinition<"crtr.setup.keybindings.reset-row">, BindingDefinition<"crtr.setup.keybindings.reset-all">, BindingDefinition<"crtr.setup.keybindings.save">, BindingDefinition<"crtr.setup.keybindings.discard">, BindingDefinition<"crtr.setup.keybindings.page-up">, BindingDefinition<"crtr.setup.keybindings.page-down">, BindingDefinition<"crtr.setup.keybindings.filter">, BindingDefinition<"crtr.setup.keybindings.filter-delete">, BindingDefinition<"crtr.setup.keybindings.filter-clear">, BindingDefinition<"crtr.setup.cell-previous">, BindingDefinition<"crtr.setup.cell-next">, BindingDefinition<"crtr.setup.models.open">, BindingDefinition<"crtr.setup.models.scope-next">, BindingDefinition<"crtr.setup.models.commit">, BindingDefinition<"crtr.setup.models.cancel">];
|
|
13
14
|
export declare const BINDING_IDS: ReadonlySet<string>;
|
|
14
15
|
export declare const SAFETY_REQUIREMENTS: readonly SafetyRequirement[];
|
|
15
16
|
export {};
|