toolcraft 0.0.16 → 0.0.18
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.d.ts +2 -0
- package/dist/cli.js +833 -124
- package/dist/error-report.d.ts +39 -0
- package/dist/error-report.js +330 -0
- package/dist/human-in-loop/approval-tasks.js +11 -8
- package/dist/human-in-loop/approvals-commands.js +21 -20
- package/dist/human-in-loop/default-provider.js +5 -3
- package/dist/human-in-loop/runner.js +45 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +55 -35
- package/dist/json-schema-converter.d.ts +1 -0
- package/dist/json-schema-converter.js +102 -52
- package/dist/mcp-proxy.d.ts +1 -0
- package/dist/mcp-proxy.js +13 -6
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +131 -55
- package/dist/sdk.d.ts +4 -2
- package/dist/sdk.js +132 -48
- package/dist/source-snippet.d.ts +8 -0
- package/dist/source-snippet.js +42 -0
- package/dist/stack-trim.d.ts +4 -0
- package/dist/stack-trim.js +70 -0
- package/dist/suggest.d.ts +4 -0
- package/dist/suggest.js +46 -0
- package/dist/user-error.d.ts +3 -0
- package/dist/user-error.js +7 -1
- package/dist/validation-errors.d.ts +5 -0
- package/dist/validation-errors.js +18 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter-plain.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/text.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/components/text.js +8 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +8 -1
- package/node_modules/@poe-code/design-system/dist/dashboard/keymap.d.ts +5 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/keymap.js +146 -12
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal.js +31 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/actions.d.ts +16 -0
- package/node_modules/@poe-code/design-system/dist/explorer/actions.js +39 -0
- package/node_modules/@poe-code/design-system/dist/explorer/demo.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/explorer/demo.js +297 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +61 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.js +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/filter.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/explorer/filter.js +95 -0
- package/node_modules/@poe-code/design-system/dist/explorer/index.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/explorer/index.js +8 -0
- package/node_modules/@poe-code/design-system/dist/explorer/jobs.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/explorer/jobs.js +59 -0
- package/node_modules/@poe-code/design-system/dist/explorer/keymap.d.ts +21 -0
- package/node_modules/@poe-code/design-system/dist/explorer/keymap.js +363 -0
- package/node_modules/@poe-code/design-system/dist/explorer/layout.d.ts +20 -0
- package/node_modules/@poe-code/design-system/dist/explorer/layout.js +73 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.d.ts +9 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +704 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +96 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/footer.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/footer.js +49 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/header.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/header.js +56 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/index.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/index.js +61 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/list.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/list.js +106 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/modal.d.ts +3 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/modal.js +91 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/test-fixtures.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/test-fixtures.js +156 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +282 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.test-helpers.d.ts +50 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.test-helpers.js +101 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +130 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.js +87 -0
- package/node_modules/@poe-code/design-system/dist/explorer/theme.d.ts +27 -0
- package/node_modules/@poe-code/design-system/dist/explorer/theme.js +97 -0
- package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -0
- package/node_modules/@poe-code/design-system/dist/index.js +3 -0
- package/node_modules/@poe-code/design-system/package.json +1 -0
- package/node_modules/@poe-code/task-list/README.md +98 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.d.ts +46 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.js +309 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +22 -2
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +266 -99
- package/node_modules/@poe-code/task-list/dist/index.d.ts +1 -0
- package/node_modules/@poe-code/task-list/dist/index.js +1 -0
- package/node_modules/@poe-code/task-list/dist/open.js +3 -0
- package/node_modules/@poe-code/task-list/dist/types.d.ts +4 -0
- package/package.json +6 -2
|
@@ -7,23 +7,78 @@ const defaultBindings = {
|
|
|
7
7
|
retry: ["r"],
|
|
8
8
|
"view-log": ["l"]
|
|
9
9
|
};
|
|
10
|
-
export function createKeymap(overrides) {
|
|
10
|
+
export function createKeymap(overrides, options) {
|
|
11
|
+
const resolvedCommands = options?.commands ?? commands;
|
|
12
|
+
const resolvedDefaults = options?.defaultBindings ?? defaultBindings;
|
|
11
13
|
const bindings = new Map();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
const sequences = new Set();
|
|
15
|
+
let pendingSequence = "";
|
|
16
|
+
for (const command of resolvedCommands) {
|
|
17
|
+
const keys = overrides?.[command] ?? resolvedDefaults[command];
|
|
18
|
+
const commandBindings = keys
|
|
15
19
|
.map(parseBinding)
|
|
16
|
-
.filter((binding) => binding !== undefined)
|
|
20
|
+
.filter((binding) => binding !== undefined);
|
|
21
|
+
for (const binding of commandBindings) {
|
|
22
|
+
if (binding.sequence !== undefined) {
|
|
23
|
+
sequences.add(binding.sequence);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
bindings.set(command, commandBindings);
|
|
17
27
|
}
|
|
18
28
|
return (event) => {
|
|
19
|
-
for (const command of
|
|
29
|
+
for (const command of resolvedCommands) {
|
|
20
30
|
const commandBindings = bindings.get(command);
|
|
21
|
-
if (commandBindings?.some((binding) =>
|
|
31
|
+
if (commandBindings?.some((binding) => matchesSingleKey(binding, event))) {
|
|
32
|
+
pendingSequence = "";
|
|
22
33
|
return command;
|
|
23
34
|
}
|
|
24
35
|
}
|
|
36
|
+
const sequenceCommand = resolveSequence(event);
|
|
37
|
+
if (sequenceCommand !== undefined) {
|
|
38
|
+
return sequenceCommand;
|
|
39
|
+
}
|
|
25
40
|
return undefined;
|
|
26
41
|
};
|
|
42
|
+
function resolveSequence(event) {
|
|
43
|
+
const token = eventToSequenceToken(event);
|
|
44
|
+
if (token === undefined) {
|
|
45
|
+
pendingSequence = "";
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
pendingSequence = `${pendingSequence}${token}`;
|
|
49
|
+
for (const command of resolvedCommands) {
|
|
50
|
+
const commandBindings = bindings.get(command);
|
|
51
|
+
if (commandBindings?.some((binding) => binding.sequence === pendingSequence)) {
|
|
52
|
+
pendingSequence = "";
|
|
53
|
+
return command;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (hasSequencePrefix(sequences, pendingSequence)) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
pendingSequence = token;
|
|
60
|
+
if (hasSequencePrefix(sequences, pendingSequence)) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
pendingSequence = "";
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function canonicalizeBinding(binding) {
|
|
68
|
+
const parsed = parseBinding(binding);
|
|
69
|
+
if (parsed === undefined) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const modifiers = [
|
|
73
|
+
parsed.ctrl ? "ctrl" : undefined,
|
|
74
|
+
parsed.meta ? "meta" : undefined,
|
|
75
|
+
parsed.shift ? "shift" : undefined
|
|
76
|
+
].filter((modifier) => modifier !== undefined);
|
|
77
|
+
const key = parsed.name ?? parsed.ch;
|
|
78
|
+
if (parsed.sequence !== undefined) {
|
|
79
|
+
return parsed.sequence.toLowerCase();
|
|
80
|
+
}
|
|
81
|
+
return key === undefined ? undefined : [...modifiers, key.toLowerCase()].join("+");
|
|
27
82
|
}
|
|
28
83
|
function parseBinding(binding) {
|
|
29
84
|
const value = binding.trim();
|
|
@@ -56,25 +111,41 @@ function parseBinding(binding) {
|
|
|
56
111
|
continue;
|
|
57
112
|
}
|
|
58
113
|
}
|
|
59
|
-
|
|
114
|
+
const normalizedKey = normalizeKeyName(key);
|
|
115
|
+
if (parts.length === 1 && isShiftedCharacter(normalizedKey)) {
|
|
60
116
|
shift = true;
|
|
61
117
|
}
|
|
62
|
-
if (
|
|
118
|
+
if (normalizedKey.length === 1) {
|
|
119
|
+
return {
|
|
120
|
+
ch: normalizeBindingCharacter(normalizedKey, shift),
|
|
121
|
+
ctrl,
|
|
122
|
+
meta,
|
|
123
|
+
shift
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (!ctrl &&
|
|
127
|
+
!meta &&
|
|
128
|
+
!shift &&
|
|
129
|
+
!isNamedKey(normalizedKey) &&
|
|
130
|
+
isPrintableSequence(normalizedKey)) {
|
|
63
131
|
return {
|
|
64
|
-
|
|
132
|
+
sequence: normalizedKey,
|
|
65
133
|
ctrl,
|
|
66
134
|
meta,
|
|
67
135
|
shift
|
|
68
136
|
};
|
|
69
137
|
}
|
|
70
138
|
return {
|
|
71
|
-
name:
|
|
139
|
+
name: normalizedKey.toLowerCase(),
|
|
72
140
|
ctrl,
|
|
73
141
|
meta,
|
|
74
142
|
shift
|
|
75
143
|
};
|
|
76
144
|
}
|
|
77
|
-
function
|
|
145
|
+
function matchesSingleKey(binding, event) {
|
|
146
|
+
if (binding.sequence !== undefined) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
78
149
|
if (binding.ctrl !== event.ctrl ||
|
|
79
150
|
binding.meta !== event.meta ||
|
|
80
151
|
binding.shift !== event.shift) {
|
|
@@ -88,6 +159,20 @@ function matches(binding, event) {
|
|
|
88
159
|
}
|
|
89
160
|
return false;
|
|
90
161
|
}
|
|
162
|
+
function eventToSequenceToken(event) {
|
|
163
|
+
if (event.ctrl || event.meta || event.ch === undefined) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
return event.ch;
|
|
167
|
+
}
|
|
168
|
+
function hasSequencePrefix(sequences, prefix) {
|
|
169
|
+
for (const sequence of sequences) {
|
|
170
|
+
if (sequence.startsWith(prefix)) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
91
176
|
function isShiftedCharacter(value) {
|
|
92
177
|
return value.length === 1 && value.toLowerCase() !== value && value.toUpperCase() === value;
|
|
93
178
|
}
|
|
@@ -97,3 +182,52 @@ function normalizeBindingCharacter(value, shift) {
|
|
|
97
182
|
}
|
|
98
183
|
return value.toUpperCase();
|
|
99
184
|
}
|
|
185
|
+
function normalizeKeyName(value) {
|
|
186
|
+
if (value.toLowerCase() === "space") {
|
|
187
|
+
return " ";
|
|
188
|
+
}
|
|
189
|
+
if (value === "↑") {
|
|
190
|
+
return "up";
|
|
191
|
+
}
|
|
192
|
+
if (value === "↓") {
|
|
193
|
+
return "down";
|
|
194
|
+
}
|
|
195
|
+
if (value === "←") {
|
|
196
|
+
return "left";
|
|
197
|
+
}
|
|
198
|
+
if (value === "→") {
|
|
199
|
+
return "right";
|
|
200
|
+
}
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
function isNamedKey(value) {
|
|
204
|
+
return namedKeys.has(value.toLowerCase());
|
|
205
|
+
}
|
|
206
|
+
function isPrintableSequence(value) {
|
|
207
|
+
if (Array.from(value).length <= 1) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
for (const char of value) {
|
|
211
|
+
const codePoint = char.codePointAt(0);
|
|
212
|
+
if (codePoint === undefined || codePoint < 0x20 || codePoint === 0x7f) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
const namedKeys = new Set([
|
|
219
|
+
"backspace",
|
|
220
|
+
"delete",
|
|
221
|
+
"down",
|
|
222
|
+
"end",
|
|
223
|
+
"enter",
|
|
224
|
+
"escape",
|
|
225
|
+
"home",
|
|
226
|
+
"left",
|
|
227
|
+
"pagedown",
|
|
228
|
+
"pageup",
|
|
229
|
+
"return",
|
|
230
|
+
"right",
|
|
231
|
+
"tab",
|
|
232
|
+
"up"
|
|
233
|
+
]);
|
|
@@ -185,6 +185,10 @@ export function parseKeypress(data) {
|
|
|
185
185
|
return event;
|
|
186
186
|
}
|
|
187
187
|
function toKeypressEvent(str, key) {
|
|
188
|
+
const controlCharacter = controlCharacterToKeypress(key?.sequence);
|
|
189
|
+
if (controlCharacter !== undefined) {
|
|
190
|
+
return controlCharacter;
|
|
191
|
+
}
|
|
188
192
|
const ctrl = key?.ctrl ?? false;
|
|
189
193
|
const meta = key?.meta ?? false;
|
|
190
194
|
const shift = key?.shift ?? false;
|
|
@@ -206,12 +210,22 @@ function extractPrintableCharacter(str, sequence) {
|
|
|
206
210
|
if (isPrintableCharacter(str)) {
|
|
207
211
|
return str;
|
|
208
212
|
}
|
|
213
|
+
if (isSinglePrintableSequence(sequence)) {
|
|
214
|
+
return sequence;
|
|
215
|
+
}
|
|
209
216
|
if (sequence === undefined || sequence.length <= 1 || sequence[0] !== "\u001b") {
|
|
210
217
|
return undefined;
|
|
211
218
|
}
|
|
212
219
|
const candidate = sequence.slice(1);
|
|
213
220
|
return isPrintableCharacter(candidate) ? candidate : undefined;
|
|
214
221
|
}
|
|
222
|
+
function isSinglePrintableSequence(value) {
|
|
223
|
+
if (value === undefined || Array.from(value).length !== 1) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
const codePoint = value.codePointAt(0);
|
|
227
|
+
return codePoint !== undefined && codePoint >= 0x20 && codePoint !== 0x7f;
|
|
228
|
+
}
|
|
215
229
|
function isPrintableCharacter(value) {
|
|
216
230
|
if (value === undefined || Array.from(value).length !== 1) {
|
|
217
231
|
return false;
|
|
@@ -219,6 +233,23 @@ function isPrintableCharacter(value) {
|
|
|
219
233
|
const codePoint = value.codePointAt(0);
|
|
220
234
|
return codePoint !== undefined && codePoint >= 0x20 && codePoint !== 0x7f;
|
|
221
235
|
}
|
|
236
|
+
function controlCharacterToKeypress(sequence) {
|
|
237
|
+
if (sequence === "\u001f") {
|
|
238
|
+
return { ch: "/", ctrl: true, meta: false, shift: false };
|
|
239
|
+
}
|
|
240
|
+
if (sequence !== undefined && sequence.length === 1) {
|
|
241
|
+
const code = sequence.charCodeAt(0);
|
|
242
|
+
if (code >= 1 && code <= 26 && code !== 9 && code !== 10 && code !== 13) {
|
|
243
|
+
return {
|
|
244
|
+
name: String.fromCharCode(code + 96),
|
|
245
|
+
ctrl: true,
|
|
246
|
+
meta: false,
|
|
247
|
+
shift: false
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
222
253
|
function cursorPositionAnsi(x, y) {
|
|
223
254
|
return `\u001b[${normalizeCoordinate(y) + 1};${normalizeCoordinate(x) + 1}H`;
|
|
224
255
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExplorerEvent } from "./events.js";
|
|
2
|
+
import type { Action, ActionContext, ExplorerState, Row, Tone } from "./state.js";
|
|
3
|
+
export type ActionSource = "row" | "detail";
|
|
4
|
+
type ExplorerKeypressEvent = Extract<ExplorerEvent, {
|
|
5
|
+
type: "key";
|
|
6
|
+
}>["key"];
|
|
7
|
+
export type ActionRuntimeHandles = {
|
|
8
|
+
refresh: () => Promise<void>;
|
|
9
|
+
suspendAnd: <T>(fn: () => Promise<T>) => Promise<T>;
|
|
10
|
+
toast: (msg: string, tone?: Tone) => void;
|
|
11
|
+
confirm: (prompt: string) => Promise<boolean>;
|
|
12
|
+
exit: (after?: () => void | Promise<void>) => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function resolveAction<R>(state: ExplorerState, keyEvent: ExplorerKeypressEvent): Action<R> | null;
|
|
15
|
+
export declare function buildActionContext<R>(state: ExplorerState, _action: Action<R>, source: ActionSource, runtimeHandles: ActionRuntimeHandles, rowsOverride?: Row[]): ActionContext<R>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function resolveAction(state, keyEvent) {
|
|
2
|
+
const target = state.bindings.resolve(keyEvent);
|
|
3
|
+
if (target?.type !== "action") {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
const actionState = state.actionState.get(target.id);
|
|
7
|
+
if (actionState?.available !== true ||
|
|
8
|
+
actionState.running === true ||
|
|
9
|
+
actionState.action === undefined) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return actionState.action;
|
|
13
|
+
}
|
|
14
|
+
export function buildActionContext(state, _action, source, runtimeHandles, rowsOverride) {
|
|
15
|
+
const row = rowsOverride?.[0] ?? currentRow(state) ?? { id: "", title: "" };
|
|
16
|
+
return {
|
|
17
|
+
row,
|
|
18
|
+
rows: rowsOverride ?? selectedRows(state, row),
|
|
19
|
+
item: source === "detail" ? currentDetailItem(state) : undefined,
|
|
20
|
+
filter: state.filter,
|
|
21
|
+
refresh: runtimeHandles.refresh,
|
|
22
|
+
suspendAnd: runtimeHandles.suspendAnd,
|
|
23
|
+
toast: runtimeHandles.toast,
|
|
24
|
+
confirm: runtimeHandles.confirm,
|
|
25
|
+
exit: runtimeHandles.exit
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function currentRow(state) {
|
|
29
|
+
return state.rows[state.filtered[state.cursor] ?? -1];
|
|
30
|
+
}
|
|
31
|
+
function currentDetailItem(state) {
|
|
32
|
+
return state.detail.items?.[state.detail.cursor];
|
|
33
|
+
}
|
|
34
|
+
function selectedRows(state, fallback) {
|
|
35
|
+
if (state.selected.size === 0) {
|
|
36
|
+
return fallback.id === "" ? [] : [fallback];
|
|
37
|
+
}
|
|
38
|
+
return state.rows.filter((row) => state.selected.has(row.id));
|
|
39
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExplorerConfig } from "./state.js";
|
|
2
|
+
type ExplorerDemoMode = "single-detail-mode" | "list-detail-mode";
|
|
3
|
+
export interface ExplorerDemoOptions {
|
|
4
|
+
mode: ExplorerDemoMode;
|
|
5
|
+
slowDetail: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface BuildExplorerDemoConfigOptions extends ExplorerDemoOptions {
|
|
8
|
+
onReorder?: (orderedIds: string[]) => void | Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseExplorerDemoOptions(argv?: string[], env?: NodeJS.ProcessEnv): ExplorerDemoOptions;
|
|
11
|
+
export declare function buildExplorerDemoConfig(options: BuildExplorerDemoConfigOptions): ExplorerConfig<void>;
|
|
12
|
+
export declare function main(): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { runExplorer } from "./index.js";
|
|
5
|
+
const detailDelayMs = 500;
|
|
6
|
+
const truthyEnvValues = new Set(["1", "true", "yes", "on"]);
|
|
7
|
+
const singleDetailRows = [
|
|
8
|
+
{
|
|
9
|
+
id: "configure-commands",
|
|
10
|
+
title: "Configure commands",
|
|
11
|
+
subtitle: "Provider config mutation flow",
|
|
12
|
+
badge: { text: "ready", tone: "success" },
|
|
13
|
+
group: "Planning"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "provider-boilerplate",
|
|
17
|
+
title: "Provider boilerplate audit",
|
|
18
|
+
subtitle: "Keep providers declarative and minimal",
|
|
19
|
+
badge: { text: "review", tone: "warning" },
|
|
20
|
+
group: "Planning"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "markdown-reader",
|
|
24
|
+
title: "Markdown reader",
|
|
25
|
+
subtitle: "Terminal-rendered plan preview",
|
|
26
|
+
badge: { text: "done", tone: "muted" },
|
|
27
|
+
group: "Docs"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "design-system-prompts",
|
|
31
|
+
title: "Design system prompts",
|
|
32
|
+
subtitle: "Prompt primitives owned by the package",
|
|
33
|
+
badge: { text: "active", tone: "info" },
|
|
34
|
+
group: "UI"
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
const singleDetailMarkdown = {
|
|
38
|
+
"configure-commands": [
|
|
39
|
+
"# Configure commands",
|
|
40
|
+
"",
|
|
41
|
+
"Provider configuration should be derived from declarative provider config.",
|
|
42
|
+
"",
|
|
43
|
+
"- Parse existing files with structured parsers.",
|
|
44
|
+
"- Deep merge edits instead of replacing user-owned configuration.",
|
|
45
|
+
"- Keep CLI and SDK arguments in parity."
|
|
46
|
+
].join("\n"),
|
|
47
|
+
"provider-boilerplate": [
|
|
48
|
+
"# Provider boilerplate audit",
|
|
49
|
+
"",
|
|
50
|
+
"Adding a provider should mean adding one provider file. Everything else should come from the provider config.",
|
|
51
|
+
"",
|
|
52
|
+
"Avoid provider-specific branches in shared code paths."
|
|
53
|
+
].join("\n"),
|
|
54
|
+
"markdown-reader": [
|
|
55
|
+
"# Markdown reader",
|
|
56
|
+
"",
|
|
57
|
+
"Render markdown plans with stable wrapping and predictable terminal styling.",
|
|
58
|
+
"",
|
|
59
|
+
"The reader is a display surface, not a planning store."
|
|
60
|
+
].join("\n"),
|
|
61
|
+
"design-system-prompts": [
|
|
62
|
+
"# Design system prompts",
|
|
63
|
+
"",
|
|
64
|
+
"Prompt primitives belong in the design-system package so CLI surfaces share one style.",
|
|
65
|
+
"",
|
|
66
|
+
"Direct use of unrelated prompt libraries should stay out of consumers."
|
|
67
|
+
].join("\n")
|
|
68
|
+
};
|
|
69
|
+
const reviewRows = [
|
|
70
|
+
{
|
|
71
|
+
id: "pr-1842",
|
|
72
|
+
title: "PR #1842 provider config parser",
|
|
73
|
+
subtitle: "3 unresolved comments by reviewbot",
|
|
74
|
+
badge: { text: "changes", tone: "warning" },
|
|
75
|
+
group: "Review queue"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "pr-1847",
|
|
79
|
+
title: "PR #1847 explorer TUI library",
|
|
80
|
+
subtitle: "2 comments, one destructive flow question",
|
|
81
|
+
badge: { text: "ready", tone: "success" },
|
|
82
|
+
group: "Review queue"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "pr-1851",
|
|
86
|
+
title: "PR #1851 markdown QA checklist",
|
|
87
|
+
subtitle: "1 docs-only note",
|
|
88
|
+
badge: { text: "docs", tone: "info" },
|
|
89
|
+
group: "Review queue"
|
|
90
|
+
}
|
|
91
|
+
];
|
|
92
|
+
const reviewComments = {
|
|
93
|
+
"pr-1842": [
|
|
94
|
+
{
|
|
95
|
+
id: "pr-1842-comment-1",
|
|
96
|
+
title: "Review: provider registry",
|
|
97
|
+
subtitle: "packages/providers/src/registry.ts:42",
|
|
98
|
+
body: "The provider data is already present in config. Derive the registry entry instead of repeating provider names here.",
|
|
99
|
+
tone: "warning"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "pr-1842-comment-2",
|
|
103
|
+
title: "Review: config merge",
|
|
104
|
+
subtitle: "packages/config-mutations/src/apply.ts:88",
|
|
105
|
+
body: "This should deep merge the parsed structure so user-owned fields remain intact.",
|
|
106
|
+
tone: "error"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "pr-1842-comment-3",
|
|
110
|
+
title: "Review: parser coverage",
|
|
111
|
+
subtitle: "packages/config-mutations/src/apply.test.ts:131",
|
|
112
|
+
body: "Add a fixture that proves comments and unknown keys survive the update path.",
|
|
113
|
+
tone: "info"
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"pr-1847": [
|
|
117
|
+
{
|
|
118
|
+
id: "pr-1847-comment-1",
|
|
119
|
+
title: "Review: detail loading",
|
|
120
|
+
subtitle: "packages/design-system/src/explorer/jobs.ts:19",
|
|
121
|
+
body: "The 150 ms loading threshold is the right behavior. This demo should make that visible with --slow-detail.",
|
|
122
|
+
tone: "success"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: "pr-1847-comment-2",
|
|
126
|
+
title: "Review: destructive confirm",
|
|
127
|
+
subtitle: "packages/design-system/src/explorer/reducer.ts:435",
|
|
128
|
+
body: "Confirm modal behavior should be reachable from manual QA with a simple keybinding.",
|
|
129
|
+
tone: "warning"
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"pr-1851": [
|
|
133
|
+
{
|
|
134
|
+
id: "pr-1851-comment-1",
|
|
135
|
+
title: "Review: QA format",
|
|
136
|
+
subtitle: "docs/qa/explorer-tui-library.md",
|
|
137
|
+
body: "Keep this as a markdown checklist. Do not convert manual QA into a script.",
|
|
138
|
+
tone: "info"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
};
|
|
142
|
+
export function parseExplorerDemoOptions(argv = process.argv.slice(2), env = process.env) {
|
|
143
|
+
let mode = parseMode(env.EXPLORER_DEMO_MODE) ?? "single-detail-mode";
|
|
144
|
+
let slowDetail = isTruthy(env.EXPLORER_DEMO_SLOW_DETAIL);
|
|
145
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
146
|
+
const arg = argv[index];
|
|
147
|
+
if (arg === "--slow-detail") {
|
|
148
|
+
slowDetail = true;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (arg === "--single-detail-mode") {
|
|
152
|
+
mode = "single-detail-mode";
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (arg === "--list-detail-mode") {
|
|
156
|
+
mode = "list-detail-mode";
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (arg === "--mode") {
|
|
160
|
+
const next = argv[index + 1];
|
|
161
|
+
const parsed = parseMode(next);
|
|
162
|
+
if (parsed === undefined) {
|
|
163
|
+
throw new Error(`Unsupported explorer demo mode: ${next ?? ""}`);
|
|
164
|
+
}
|
|
165
|
+
mode = parsed;
|
|
166
|
+
index += 1;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (arg.startsWith("--mode=")) {
|
|
170
|
+
const parsed = parseMode(arg.slice("--mode=".length));
|
|
171
|
+
if (parsed === undefined) {
|
|
172
|
+
throw new Error(`Unsupported explorer demo mode: ${arg.slice("--mode=".length)}`);
|
|
173
|
+
}
|
|
174
|
+
mode = parsed;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return { mode, slowDetail };
|
|
178
|
+
}
|
|
179
|
+
export function buildExplorerDemoConfig(options) {
|
|
180
|
+
const rows = options.mode === "single-detail-mode" ? singleDetailRows : reviewRows;
|
|
181
|
+
const detail = options.mode === "single-detail-mode"
|
|
182
|
+
? buildSingleDetail(options.slowDetail)
|
|
183
|
+
: buildReviewDetail(options.slowDetail);
|
|
184
|
+
return {
|
|
185
|
+
title: `Explorer Demo - ${options.mode}`,
|
|
186
|
+
rows: async () => rows,
|
|
187
|
+
detail,
|
|
188
|
+
actions: demoActions(),
|
|
189
|
+
reorder: {
|
|
190
|
+
onReorder: options.onReorder ?? (() => undefined)
|
|
191
|
+
},
|
|
192
|
+
multiSelect: true,
|
|
193
|
+
emptyHint: "No rows match the current filter"
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
export async function main() {
|
|
197
|
+
const options = parseExplorerDemoOptions();
|
|
198
|
+
await runExplorer(buildExplorerDemoConfig(options));
|
|
199
|
+
}
|
|
200
|
+
function buildSingleDetail(slowDetail) {
|
|
201
|
+
return {
|
|
202
|
+
items: async (row, ctx) => {
|
|
203
|
+
await delayDetailIfNeeded(slowDetail, ctx);
|
|
204
|
+
const markdown = singleDetailMarkdown[row.id] ?? `# ${row.title}\n\nNo demo detail is available.`;
|
|
205
|
+
return [{ id: row.id, render: () => markdown }];
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function buildReviewDetail(slowDetail) {
|
|
210
|
+
return {
|
|
211
|
+
items: async (row, ctx) => {
|
|
212
|
+
await delayDetailIfNeeded(slowDetail, ctx);
|
|
213
|
+
const comments = reviewComments[row.id] ?? [];
|
|
214
|
+
return comments.map((comment) => ({
|
|
215
|
+
id: comment.id,
|
|
216
|
+
title: comment.title,
|
|
217
|
+
subtitle: comment.subtitle,
|
|
218
|
+
badge: { text: "comment", tone: comment.tone },
|
|
219
|
+
render: () => comment.body
|
|
220
|
+
}));
|
|
221
|
+
},
|
|
222
|
+
actions: [
|
|
223
|
+
{
|
|
224
|
+
id: "resolve-comment",
|
|
225
|
+
label: "Resolve comment",
|
|
226
|
+
key: "x",
|
|
227
|
+
showInFooter: true,
|
|
228
|
+
handler: (ctx) => {
|
|
229
|
+
ctx.toast(`Resolved ${ctx.item?.title ?? ctx.row.title}`, "success");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function demoActions() {
|
|
236
|
+
return [
|
|
237
|
+
{
|
|
238
|
+
id: "open",
|
|
239
|
+
label: "Open",
|
|
240
|
+
primary: true,
|
|
241
|
+
showInFooter: true,
|
|
242
|
+
handler: (ctx) => {
|
|
243
|
+
ctx.toast(`Opened ${ctx.row.title}`, "info");
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "refresh",
|
|
248
|
+
label: "Refresh",
|
|
249
|
+
key: "r",
|
|
250
|
+
showInFooter: true,
|
|
251
|
+
handler: async (ctx) => {
|
|
252
|
+
await ctx.refresh();
|
|
253
|
+
ctx.toast("Rows refreshed", "success");
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: "archive",
|
|
258
|
+
label: () => "Archive selected",
|
|
259
|
+
key: "a",
|
|
260
|
+
destructive: true,
|
|
261
|
+
showInFooter: true,
|
|
262
|
+
handler: (ctx) => {
|
|
263
|
+
ctx.toast(`Archived ${ctx.rows.length} row${ctx.rows.length === 1 ? "" : "s"}`, "warning");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
async function delayDetailIfNeeded(slowDetail, ctx) {
|
|
269
|
+
if (!slowDetail) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
await new Promise((resolve) => {
|
|
273
|
+
const timeout = setTimeout(resolve, detailDelayMs);
|
|
274
|
+
ctx.signal.addEventListener("abort", () => {
|
|
275
|
+
clearTimeout(timeout);
|
|
276
|
+
resolve();
|
|
277
|
+
}, { once: true });
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
function parseMode(value) {
|
|
281
|
+
if (value === "single-detail-mode" || value === "list-detail-mode") {
|
|
282
|
+
return value;
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
function isTruthy(value) {
|
|
287
|
+
return value === undefined ? false : truthyEnvValues.has(value.toLowerCase());
|
|
288
|
+
}
|
|
289
|
+
const entry = process.argv[1];
|
|
290
|
+
const isMain = typeof entry === "string" && path.resolve(entry) === fileURLToPath(import.meta.url);
|
|
291
|
+
if (isMain) {
|
|
292
|
+
main().catch((error) => {
|
|
293
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
294
|
+
process.stderr.write(`${message}\n`);
|
|
295
|
+
process.exitCode = 1;
|
|
296
|
+
});
|
|
297
|
+
}
|