toolcraft 0.0.68 → 0.0.70
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/cli.js +43 -1
- package/node_modules/toolcraft-design/dist/explorer/two-pane.d.ts +8 -0
- package/node_modules/toolcraft-design/dist/explorer/two-pane.js +70 -6
- package/node_modules/toolcraft-design/dist/prompts/interactive/glyphs.d.ts +3 -3
- package/node_modules/toolcraft-design/dist/prompts/interactive/glyphs.js +3 -3
- package/node_modules/toolcraft-design/dist/prompts/interactive/keys.js +3 -0
- package/node_modules/toolcraft-design/dist/prompts/interactive/multiselect.js +8 -4
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -3328,11 +3328,21 @@ function findUnknownCommanderCommand(program, argv) {
|
|
|
3328
3328
|
commandPath: pathSegments.join(" ")
|
|
3329
3329
|
};
|
|
3330
3330
|
}
|
|
3331
|
-
if (current.commands.length === 0
|
|
3331
|
+
if (current.commands.length === 0) {
|
|
3332
3332
|
return undefined;
|
|
3333
3333
|
}
|
|
3334
3334
|
const child = current.commands.find((command) => command.name() === token || command.aliases().includes(token));
|
|
3335
3335
|
if (child === undefined) {
|
|
3336
|
+
if (getDefaultCommanderCommandName(current) !== undefined) {
|
|
3337
|
+
if (shouldRejectDefaultCommandToken(current, token, pathSegments)) {
|
|
3338
|
+
return {
|
|
3339
|
+
input: token,
|
|
3340
|
+
currentCommand: current,
|
|
3341
|
+
commandPath: pathSegments.join(" ")
|
|
3342
|
+
};
|
|
3343
|
+
}
|
|
3344
|
+
return undefined;
|
|
3345
|
+
}
|
|
3336
3346
|
return {
|
|
3337
3347
|
input: token,
|
|
3338
3348
|
currentCommand: current,
|
|
@@ -3344,6 +3354,38 @@ function findUnknownCommanderCommand(program, argv) {
|
|
|
3344
3354
|
}
|
|
3345
3355
|
return undefined;
|
|
3346
3356
|
}
|
|
3357
|
+
function shouldRejectDefaultCommandToken(command, token, pathSegments) {
|
|
3358
|
+
return (pathSegments.length === 0 &&
|
|
3359
|
+
isBareCommandLikeToken(token) &&
|
|
3360
|
+
hasNonDefaultPublicChildCommand(command));
|
|
3361
|
+
}
|
|
3362
|
+
function hasNonDefaultPublicChildCommand(command) {
|
|
3363
|
+
const defaultName = getDefaultCommanderCommandName(command);
|
|
3364
|
+
return command.commands.some((child) => child.name() !== defaultName &&
|
|
3365
|
+
!isToolcraftHiddenCommander(child) &&
|
|
3366
|
+
!getToolcraftReservedChildNames(command).includes(child.name()));
|
|
3367
|
+
}
|
|
3368
|
+
function isBareCommandLikeToken(token) {
|
|
3369
|
+
if (token.length === 0) {
|
|
3370
|
+
return false;
|
|
3371
|
+
}
|
|
3372
|
+
for (const character of token) {
|
|
3373
|
+
if (!isCommandNameCharacter(character)) {
|
|
3374
|
+
return false;
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
return true;
|
|
3378
|
+
}
|
|
3379
|
+
function isCommandNameCharacter(character) {
|
|
3380
|
+
const code = character.codePointAt(0);
|
|
3381
|
+
if (code === undefined) {
|
|
3382
|
+
return false;
|
|
3383
|
+
}
|
|
3384
|
+
const isLowercaseLetter = code >= 97 && code <= 122;
|
|
3385
|
+
const isUppercaseLetter = code >= 65 && code <= 90;
|
|
3386
|
+
const isDigit = code >= 48 && code <= 57;
|
|
3387
|
+
return isLowercaseLetter || isUppercaseLetter || isDigit || character === "-" || character === "_";
|
|
3388
|
+
}
|
|
3347
3389
|
function getDefaultCommanderCommandName(command) {
|
|
3348
3390
|
const candidate = command;
|
|
3349
3391
|
return typeof candidate._defaultCommandName === "string"
|
|
@@ -37,6 +37,11 @@ export interface TwoPaneExplorerConfig<R> {
|
|
|
37
37
|
panes: [TwoPaneDefinition, TwoPaneDefinition];
|
|
38
38
|
actions: TwoPaneAction<R>[];
|
|
39
39
|
refresh?: () => void | Promise<void>;
|
|
40
|
+
trace?: (record: TwoPaneTraceRecord) => void | Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export interface TwoPaneTraceRecord {
|
|
43
|
+
event: string;
|
|
44
|
+
[key: string]: unknown;
|
|
40
45
|
}
|
|
41
46
|
export interface TwoPanePaneState {
|
|
42
47
|
id: string;
|
|
@@ -75,6 +80,8 @@ export declare class TwoPaneExplorerRuntime<R> {
|
|
|
75
80
|
constructor(config: TwoPaneExplorerConfig<R>, driver: TerminalDriver);
|
|
76
81
|
run(): Promise<R | null>;
|
|
77
82
|
private startTerminal;
|
|
83
|
+
private subscribeKeypress;
|
|
84
|
+
private pauseKeypress;
|
|
78
85
|
private loadRows;
|
|
79
86
|
private refresh;
|
|
80
87
|
private dispatchKey;
|
|
@@ -90,5 +97,6 @@ export declare class TwoPaneExplorerRuntime<R> {
|
|
|
90
97
|
private render;
|
|
91
98
|
private exit;
|
|
92
99
|
private fail;
|
|
100
|
+
private trace;
|
|
93
101
|
}
|
|
94
102
|
export declare function renderTwoPaneExplorer<R>(state: TwoPaneExplorerState, actions: TwoPaneAction<R>[], screen: ScreenBuffer): void;
|
|
@@ -51,14 +51,21 @@ export class TwoPaneExplorerRuntime {
|
|
|
51
51
|
this.driver.enterAltScreen();
|
|
52
52
|
this.driver.disableLineWrap();
|
|
53
53
|
this.driver.hideCursor();
|
|
54
|
-
this.
|
|
55
|
-
this.dispatchKey(key);
|
|
56
|
-
});
|
|
54
|
+
this.subscribeKeypress();
|
|
57
55
|
this.unsubscribeResize = this.driver.onResize(() => {
|
|
58
56
|
this.state = { ...this.state, size: normalizeSize(this.driver.getSize()) };
|
|
59
57
|
this.render();
|
|
60
58
|
});
|
|
61
59
|
}
|
|
60
|
+
subscribeKeypress() {
|
|
61
|
+
this.unsubscribeKeypress = this.driver.onKeypress((key) => {
|
|
62
|
+
this.dispatchKey(key);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
pauseKeypress() {
|
|
66
|
+
this.unsubscribeKeypress?.();
|
|
67
|
+
this.unsubscribeKeypress = undefined;
|
|
68
|
+
}
|
|
62
69
|
async loadRows(requestToken = ++this.rowsRequestToken) {
|
|
63
70
|
const [leftRows, rightRows] = await Promise.all([
|
|
64
71
|
this.config.panes[0].rows(),
|
|
@@ -84,6 +91,14 @@ export class TwoPaneExplorerRuntime {
|
|
|
84
91
|
if (this.stopped) {
|
|
85
92
|
return;
|
|
86
93
|
}
|
|
94
|
+
this.trace("key", {
|
|
95
|
+
key: traceKey(key),
|
|
96
|
+
filterFocused: this.state.filterFocused,
|
|
97
|
+
pane: this.activePane().id,
|
|
98
|
+
cursor: this.activePane().cursor,
|
|
99
|
+
selected: this.activePane().selected.size,
|
|
100
|
+
filter: this.activePane().filter
|
|
101
|
+
});
|
|
87
102
|
if (this.state.filterFocused) {
|
|
88
103
|
this.dispatchFilterKey(key);
|
|
89
104
|
return;
|
|
@@ -92,7 +107,7 @@ export class TwoPaneExplorerRuntime {
|
|
|
92
107
|
this.exit(null);
|
|
93
108
|
return;
|
|
94
109
|
}
|
|
95
|
-
if (key
|
|
110
|
+
if (isTabKey(key)) {
|
|
96
111
|
this.state = {
|
|
97
112
|
...this.state,
|
|
98
113
|
activePaneIndex: this.state.activePaneIndex === 0 ? 1 : 0
|
|
@@ -112,7 +127,7 @@ export class TwoPaneExplorerRuntime {
|
|
|
112
127
|
this.setCursor(filteredRows(this.activePane()).length - 1);
|
|
113
128
|
return;
|
|
114
129
|
}
|
|
115
|
-
if (key.ch === " ") {
|
|
130
|
+
if (key.ch === " " || key.name === "space") {
|
|
116
131
|
this.toggleSelection();
|
|
117
132
|
return;
|
|
118
133
|
}
|
|
@@ -131,8 +146,14 @@ export class TwoPaneExplorerRuntime {
|
|
|
131
146
|
}
|
|
132
147
|
}
|
|
133
148
|
dispatchFilterKey(key) {
|
|
134
|
-
if (key.name === "escape" || key.name === "return") {
|
|
149
|
+
if (key.name === "escape" || key.name === "return" || key.name === "enter") {
|
|
135
150
|
this.state = { ...this.state, filterFocused: false };
|
|
151
|
+
this.trace("filter.submit", {
|
|
152
|
+
key: traceKey(key),
|
|
153
|
+
pane: this.activePane().id,
|
|
154
|
+
filter: this.activePane().filter,
|
|
155
|
+
rows: filteredRows(this.activePane()).length
|
|
156
|
+
});
|
|
136
157
|
this.render();
|
|
137
158
|
return;
|
|
138
159
|
}
|
|
@@ -145,6 +166,12 @@ export class TwoPaneExplorerRuntime {
|
|
|
145
166
|
cursor: 0
|
|
146
167
|
}))
|
|
147
168
|
};
|
|
169
|
+
this.trace("filter.update", {
|
|
170
|
+
key: traceKey(key),
|
|
171
|
+
pane: this.activePane().id,
|
|
172
|
+
filter: this.activePane().filter,
|
|
173
|
+
rows: filteredRows(this.activePane()).length
|
|
174
|
+
});
|
|
148
175
|
this.render();
|
|
149
176
|
return;
|
|
150
177
|
}
|
|
@@ -157,6 +184,12 @@ export class TwoPaneExplorerRuntime {
|
|
|
157
184
|
cursor: 0
|
|
158
185
|
}))
|
|
159
186
|
};
|
|
187
|
+
this.trace("filter.update", {
|
|
188
|
+
key: traceKey(key),
|
|
189
|
+
pane: this.activePane().id,
|
|
190
|
+
filter: this.activePane().filter,
|
|
191
|
+
rows: filteredRows(this.activePane()).length
|
|
192
|
+
});
|
|
160
193
|
this.render();
|
|
161
194
|
}
|
|
162
195
|
}
|
|
@@ -195,6 +228,14 @@ export class TwoPaneExplorerRuntime {
|
|
|
195
228
|
...this.state,
|
|
196
229
|
panes: updateActivePane(this.state, (candidate) => ({ ...candidate, selected }))
|
|
197
230
|
};
|
|
231
|
+
this.trace("selection.toggle", {
|
|
232
|
+
pane: pane.id,
|
|
233
|
+
row: row.id,
|
|
234
|
+
selected: selected.size,
|
|
235
|
+
checked: selected.has(row.id),
|
|
236
|
+
filter: pane.filter,
|
|
237
|
+
cursor: pane.cursor
|
|
238
|
+
});
|
|
198
239
|
this.render();
|
|
199
240
|
}
|
|
200
241
|
runAction(action) {
|
|
@@ -223,6 +264,7 @@ export class TwoPaneExplorerRuntime {
|
|
|
223
264
|
});
|
|
224
265
|
}
|
|
225
266
|
async suspendAnd(fn) {
|
|
267
|
+
this.pauseKeypress();
|
|
226
268
|
this.driver.exitAltScreen();
|
|
227
269
|
this.driver.enableLineWrap();
|
|
228
270
|
this.driver.showCursor();
|
|
@@ -236,6 +278,7 @@ export class TwoPaneExplorerRuntime {
|
|
|
236
278
|
this.driver.enterAltScreen();
|
|
237
279
|
this.driver.disableLineWrap();
|
|
238
280
|
this.driver.hideCursor();
|
|
281
|
+
this.subscribeKeypress();
|
|
239
282
|
this.state = { ...this.state, size: normalizeSize(this.driver.getSize()) };
|
|
240
283
|
this.render();
|
|
241
284
|
}
|
|
@@ -290,6 +333,15 @@ export class TwoPaneExplorerRuntime {
|
|
|
290
333
|
}
|
|
291
334
|
this.settle?.reject(error);
|
|
292
335
|
}
|
|
336
|
+
trace(event, fields = {}) {
|
|
337
|
+
const trace = this.config.trace;
|
|
338
|
+
if (trace === undefined) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
void Promise.resolve(trace({ event, ...fields })).catch(() => {
|
|
342
|
+
// Diagnostic tracing must not interfere with TUI input handling.
|
|
343
|
+
});
|
|
344
|
+
}
|
|
293
345
|
}
|
|
294
346
|
export function renderTwoPaneExplorer(state, actions, screen) {
|
|
295
347
|
screen.clear();
|
|
@@ -402,9 +454,21 @@ function actionMatchesKey(action, key) {
|
|
|
402
454
|
const keys = Array.isArray(action.key) ? action.key : [action.key];
|
|
403
455
|
return keys.some((candidate) => key.ch === candidate || key.name === candidate);
|
|
404
456
|
}
|
|
457
|
+
function isTabKey(key) {
|
|
458
|
+
return key.name === "tab" || key.ch === "\t" || (key.name === "i" && key.ctrl);
|
|
459
|
+
}
|
|
405
460
|
function firstKey(key) {
|
|
406
461
|
return Array.isArray(key) ? key[0] ?? "" : key;
|
|
407
462
|
}
|
|
463
|
+
function traceKey(key) {
|
|
464
|
+
return {
|
|
465
|
+
name: key.name,
|
|
466
|
+
ch: key.ch,
|
|
467
|
+
ctrl: key.ctrl,
|
|
468
|
+
meta: key.meta,
|
|
469
|
+
shift: key.shift
|
|
470
|
+
};
|
|
471
|
+
}
|
|
408
472
|
function isQuitKey(key) {
|
|
409
473
|
return key.ch === "q" || (key.name === "c" && key.ctrl);
|
|
410
474
|
}
|
|
@@ -10,9 +10,9 @@ export declare const GLYPHS: {
|
|
|
10
10
|
readonly barEnd: string;
|
|
11
11
|
readonly radioActive: string;
|
|
12
12
|
readonly radioInactive: string;
|
|
13
|
-
readonly checkboxActive:
|
|
14
|
-
readonly checkboxSelected:
|
|
15
|
-
readonly checkboxInactive:
|
|
13
|
+
readonly checkboxActive: "[ ]";
|
|
14
|
+
readonly checkboxSelected: "[x]";
|
|
15
|
+
readonly checkboxInactive: "[ ]";
|
|
16
16
|
readonly passwordMask: string;
|
|
17
17
|
readonly ellipsis: "...";
|
|
18
18
|
};
|
|
@@ -27,9 +27,9 @@ export const GLYPHS = {
|
|
|
27
27
|
barEnd: glyph("└", "-"),
|
|
28
28
|
radioActive: glyph("●", ">"),
|
|
29
29
|
radioInactive: glyph("○", " "),
|
|
30
|
-
checkboxActive:
|
|
31
|
-
checkboxSelected:
|
|
32
|
-
checkboxInactive:
|
|
30
|
+
checkboxActive: "[ ]",
|
|
31
|
+
checkboxSelected: "[x]",
|
|
32
|
+
checkboxInactive: "[ ]",
|
|
33
33
|
passwordMask: glyph("•", "*"),
|
|
34
34
|
ellipsis: "..."
|
|
35
35
|
};
|
|
@@ -102,10 +102,14 @@ function renderOption(option, values, active, submitted, cancelled) {
|
|
|
102
102
|
}
|
|
103
103
|
function renderMultiselectPrompt(prompt, opts) {
|
|
104
104
|
if (prompt.state === "submit" || prompt.state === "cancel") {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
const selectedOptions = prompt.visibleOptions.filter((option) => hasValue(prompt.value, option.value));
|
|
106
|
+
const labels = selectedOptions.length > 3
|
|
107
|
+
? prompt.state === "submit"
|
|
108
|
+
? color.dim(`${selectedOptions.length} selected`)
|
|
109
|
+
: color.dim.strikethrough(`${selectedOptions.length} selected`)
|
|
110
|
+
: selectedOptions
|
|
111
|
+
.map((option) => prompt.state === "submit" ? color.dim(option.label) : color.dim.strikethrough(option.label))
|
|
112
|
+
.join(", ");
|
|
109
113
|
const end = prompt.state === "submit" ? color.green(GLYPHS.barEnd) : color.red(GLYPHS.barEnd);
|
|
110
114
|
return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}\n${color.gray(GLYPHS.bar)} ${labels}\n${end}`;
|
|
111
115
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.70",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"postpack": "node ../../scripts/manage-bundled-workspace-deps.mjs cleanup . toolcraft-design @poe-code/frontmatter @poe-code/agent-mcp-config @poe-code/agent-human-in-loop @poe-code/task-list @poe-code/agent-defs @poe-code/config-mutations @poe-code/process-runner tiny-mcp-client mcp-oauth auth-store"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"toolcraft-schema": "0.0.
|
|
51
|
-
"commander": "^
|
|
50
|
+
"toolcraft-schema": "0.0.70",
|
|
51
|
+
"commander": "^13.1.0",
|
|
52
52
|
"fast-string-width": "^3.0.2",
|
|
53
53
|
"fast-wrap-ansi": "^0.2.0",
|
|
54
54
|
"ignore": "^5.3.2",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"dist"
|
|
64
64
|
],
|
|
65
65
|
"engines": {
|
|
66
|
-
"node": ">=
|
|
66
|
+
"node": ">=18.18"
|
|
67
67
|
},
|
|
68
68
|
"repository": {
|
|
69
69
|
"type": "git",
|