toolcraft 0.0.243 → 0.0.244
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/composition.json +2 -2
- package/dist/composition.json +2 -2
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-design/README.md +6 -0
- package/node_modules/toolcraft-design/dist/acp/index.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/acp/index.js +1 -0
- package/node_modules/toolcraft-design/dist/acp/plan.d.ts +10 -0
- package/node_modules/toolcraft-design/dist/acp/plan.js +50 -0
- package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.d.ts +6 -2
- package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.js +118 -6
- package/node_modules/toolcraft-design/dist/dashboard/components/run-view.d.ts +27 -0
- package/node_modules/toolcraft-design/dist/dashboard/components/run-view.js +275 -0
- package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.js +2 -8
- package/node_modules/toolcraft-design/dist/dashboard/composer-layout.d.ts +12 -0
- package/node_modules/toolcraft-design/dist/dashboard/composer-layout.js +45 -0
- package/node_modules/toolcraft-design/dist/dashboard/composer.d.ts +24 -0
- package/node_modules/toolcraft-design/dist/dashboard/composer.js +107 -0
- package/node_modules/toolcraft-design/dist/dashboard/dashboard.d.ts +4 -0
- package/node_modules/toolcraft-design/dist/dashboard/dashboard.js +198 -37
- package/node_modules/toolcraft-design/dist/dashboard/elapsed.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/elapsed.js +8 -0
- package/node_modules/toolcraft-design/dist/dashboard/index.d.ts +2 -1
- package/node_modules/toolcraft-design/dist/dashboard/store.js +2 -1
- package/node_modules/toolcraft-design/dist/dashboard/terminal-width.js +1 -1
- package/node_modules/toolcraft-design/dist/dashboard/terminal.js +22 -47
- package/node_modules/toolcraft-design/dist/dashboard/types.d.ts +37 -0
- package/node_modules/toolcraft-design/dist/terminal/input.js +24 -13
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import readline from "node:readline";
|
|
2
2
|
import { PassThrough } from "node:stream";
|
|
3
|
+
import { createInputParser } from "../terminal/input.js";
|
|
3
4
|
import { cellToAnsi } from "./buffer.js";
|
|
4
5
|
import { graphemeWidth } from "./terminal-width.js";
|
|
5
6
|
export function createTerminalDriver(opts) {
|
|
6
7
|
const stdin = (opts?.stdin ?? process.stdin);
|
|
7
8
|
const stdout = (opts?.stdout ?? process.stdout);
|
|
8
9
|
const resizeListeners = new Set();
|
|
9
|
-
const keypressListeners = new
|
|
10
|
+
const keypressListeners = new Map();
|
|
10
11
|
let rawMode = false;
|
|
11
12
|
let altScreen = false;
|
|
12
13
|
let lineWrapEnabled = true;
|
|
13
14
|
let cursorHidden = false;
|
|
14
15
|
let destroyed = false;
|
|
15
|
-
readline.emitKeypressEvents(stdin);
|
|
16
16
|
function enterRawMode() {
|
|
17
17
|
if (destroyed || rawMode) {
|
|
18
18
|
return;
|
|
@@ -128,17 +128,31 @@ export function createTerminalDriver(opts) {
|
|
|
128
128
|
if (destroyed) {
|
|
129
129
|
return () => { };
|
|
130
130
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
handler(event);
|
|
131
|
+
const dispatch = (event) => {
|
|
132
|
+
if (event.type === "paste") {
|
|
133
|
+
handler({ name: "paste", ch: event.text, ctrl: false, meta: false, shift: false });
|
|
134
134
|
}
|
|
135
|
+
else if (event.type === "key") {
|
|
136
|
+
handler({
|
|
137
|
+
...(event.ch !== undefined ? { ch: event.ch } : { name: event.name === "enter" ? "return" : event.name }),
|
|
138
|
+
ctrl: event.ctrl,
|
|
139
|
+
meta: event.alt,
|
|
140
|
+
shift: event.shift
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const parser = createInputParser({ onEvent: dispatch });
|
|
145
|
+
const listener = (chunk) => {
|
|
146
|
+
for (const event of parser.feed(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)))
|
|
147
|
+
dispatch(event);
|
|
135
148
|
};
|
|
136
|
-
keypressListeners.
|
|
149
|
+
keypressListeners.set(listener, parser.destroy);
|
|
137
150
|
stdin.on("data", listener);
|
|
138
151
|
return () => {
|
|
139
152
|
if (!keypressListeners.delete(listener)) {
|
|
140
153
|
return;
|
|
141
154
|
}
|
|
155
|
+
parser.destroy();
|
|
142
156
|
stdin.off("data", listener);
|
|
143
157
|
};
|
|
144
158
|
}
|
|
@@ -146,7 +160,8 @@ export function createTerminalDriver(opts) {
|
|
|
146
160
|
if (destroyed) {
|
|
147
161
|
return;
|
|
148
162
|
}
|
|
149
|
-
for (const listener of keypressListeners) {
|
|
163
|
+
for (const [listener, cleanup] of keypressListeners) {
|
|
164
|
+
cleanup();
|
|
150
165
|
stdin.off("data", listener);
|
|
151
166
|
}
|
|
152
167
|
keypressListeners.clear();
|
|
@@ -196,46 +211,6 @@ export function parseKeypress(data) {
|
|
|
196
211
|
stream.destroy();
|
|
197
212
|
return event;
|
|
198
213
|
}
|
|
199
|
-
function parseKeypressChunk(data) {
|
|
200
|
-
const events = [];
|
|
201
|
-
const input = data.toString("utf8");
|
|
202
|
-
let index = 0;
|
|
203
|
-
while (index < input.length) {
|
|
204
|
-
const sequence = nextKeySequence(input, index);
|
|
205
|
-
if (sequence.length === 0) {
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
const event = parseKeypress(Buffer.from(sequence));
|
|
209
|
-
if (event !== undefined) {
|
|
210
|
-
events.push(event);
|
|
211
|
-
}
|
|
212
|
-
index += sequence.length;
|
|
213
|
-
}
|
|
214
|
-
return events;
|
|
215
|
-
}
|
|
216
|
-
function nextKeySequence(input, index) {
|
|
217
|
-
const character = input[index];
|
|
218
|
-
if (character === undefined) {
|
|
219
|
-
return "";
|
|
220
|
-
}
|
|
221
|
-
if (character !== "\u001b") {
|
|
222
|
-
return character;
|
|
223
|
-
}
|
|
224
|
-
const next = input[index + 1];
|
|
225
|
-
if (next === undefined) {
|
|
226
|
-
return character;
|
|
227
|
-
}
|
|
228
|
-
if (next !== "[") {
|
|
229
|
-
return input.slice(index, index + 2);
|
|
230
|
-
}
|
|
231
|
-
for (let cursor = index + 2; cursor < input.length; cursor += 1) {
|
|
232
|
-
const code = input.charCodeAt(cursor);
|
|
233
|
-
if ((code >= 0x40 && code <= 0x7e) || input[cursor] === "~") {
|
|
234
|
-
return input.slice(index, cursor + 1);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return input.slice(index);
|
|
238
|
-
}
|
|
239
214
|
function toKeypressEvent(str, key) {
|
|
240
215
|
const controlCharacter = controlCharacterToKeypress(key?.sequence);
|
|
241
216
|
if (controlCharacter !== undefined) {
|
|
@@ -5,6 +5,41 @@ export type OutputItem = {
|
|
|
5
5
|
kind: OutputItemKind;
|
|
6
6
|
text: string;
|
|
7
7
|
ts: number;
|
|
8
|
+
role?: "agent" | "reasoning" | "action" | "user" | "plan";
|
|
9
|
+
detail?: string;
|
|
10
|
+
};
|
|
11
|
+
export type DashboardWorkStatus = "pending" | "running" | "completed" | "failed" | "cancelled" | "paused";
|
|
12
|
+
export type DashboardQueueItem = Readonly<{
|
|
13
|
+
id: string;
|
|
14
|
+
status: DashboardWorkStatus;
|
|
15
|
+
} & ({
|
|
16
|
+
kind: "plan";
|
|
17
|
+
path: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "message";
|
|
20
|
+
text: string;
|
|
21
|
+
afterPlanId: string;
|
|
22
|
+
})>;
|
|
23
|
+
export type DashboardTask = {
|
|
24
|
+
id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
status: DashboardWorkStatus;
|
|
27
|
+
steps?: Array<{
|
|
28
|
+
name: string;
|
|
29
|
+
status: DashboardWorkStatus;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
export type DashboardRunState = {
|
|
33
|
+
agent?: string;
|
|
34
|
+
model?: string;
|
|
35
|
+
cwd?: string;
|
|
36
|
+
phase?: string;
|
|
37
|
+
activity?: string;
|
|
38
|
+
activePlanId?: string;
|
|
39
|
+
activeTaskId?: string;
|
|
40
|
+
activeStep?: string;
|
|
41
|
+
queue?: readonly DashboardQueueItem[];
|
|
42
|
+
tasks?: readonly DashboardTask[];
|
|
8
43
|
};
|
|
9
44
|
export type DashboardStats = {
|
|
10
45
|
status: "idle" | "running" | "paused" | "done" | "error";
|
|
@@ -21,6 +56,8 @@ export type DashboardStats = {
|
|
|
21
56
|
agent: string;
|
|
22
57
|
model?: string;
|
|
23
58
|
};
|
|
59
|
+
usageAvailable?: boolean;
|
|
60
|
+
run?: DashboardRunState;
|
|
24
61
|
};
|
|
25
62
|
export type Command = "quit" | "forceQuit" | "edit" | "pause" | "retry" | "view-log" | "scroll-up" | "scroll-down" | "page-up" | "page-down" | "follow" | "render-stats";
|
|
26
63
|
export type DashboardState = {
|
|
@@ -18,9 +18,11 @@ export function createInputParser(options = {}) {
|
|
|
18
18
|
return;
|
|
19
19
|
timer = setTimeout(() => {
|
|
20
20
|
timer = undefined;
|
|
21
|
-
if (options.onEvent !== undefined && pending.length
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (options.onEvent !== undefined && pending.length > 0 && pending.every((byte) => byte === ESC)) {
|
|
22
|
+
const count = pending.length;
|
|
23
|
+
pending = Buffer.alloc(0);
|
|
24
|
+
for (let index = 0; index < count; index++)
|
|
25
|
+
options.onEvent(key("escape"));
|
|
24
26
|
}
|
|
25
27
|
else {
|
|
26
28
|
escapeReady = true;
|
|
@@ -40,11 +42,13 @@ export function createInputParser(options = {}) {
|
|
|
40
42
|
continue;
|
|
41
43
|
}
|
|
42
44
|
if (pending[0] === ESC) {
|
|
43
|
-
if (pending.
|
|
45
|
+
if (pending.every((byte) => byte === ESC)) {
|
|
44
46
|
if (escapeReady) {
|
|
45
|
-
|
|
47
|
+
const count = pending.length;
|
|
48
|
+
pending = Buffer.alloc(0);
|
|
46
49
|
escapeReady = false;
|
|
47
|
-
|
|
50
|
+
for (let index = 0; index < count; index++)
|
|
51
|
+
events.push(key("escape"));
|
|
48
52
|
}
|
|
49
53
|
else {
|
|
50
54
|
armEscapeTimer();
|
|
@@ -57,15 +61,17 @@ export function createInputParser(options = {}) {
|
|
|
57
61
|
paste = true;
|
|
58
62
|
continue;
|
|
59
63
|
}
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
const altCsi = pending[1] === ESC && pending[2] === 0x5b;
|
|
65
|
+
if (pending[1] === 0x5b || altCsi) {
|
|
66
|
+
const offset = altCsi ? 1 : 0;
|
|
67
|
+
const final = findCsiFinal(pending.subarray(offset));
|
|
62
68
|
if (final < 0)
|
|
63
69
|
break;
|
|
64
|
-
const sequence = pending.subarray(
|
|
65
|
-
pending = pending.subarray(final + 1);
|
|
70
|
+
const sequence = pending.subarray(offset, offset + final + 1).toString("ascii");
|
|
71
|
+
pending = pending.subarray(offset + final + 1);
|
|
66
72
|
const event = parseCsi(sequence);
|
|
67
73
|
if (event !== undefined)
|
|
68
|
-
events.push(event);
|
|
74
|
+
events.push(altCsi && event.type === "key" ? { ...event, alt: true } : event);
|
|
69
75
|
continue;
|
|
70
76
|
}
|
|
71
77
|
if (pending[1] === 0x4f) {
|
|
@@ -82,6 +88,11 @@ export function createInputParser(options = {}) {
|
|
|
82
88
|
events.push(key("escape"));
|
|
83
89
|
continue;
|
|
84
90
|
}
|
|
91
|
+
if (pending[1] < 0x20 || pending[1] === 0x7f) {
|
|
92
|
+
events.push({ ...control(pending[1]), alt: true });
|
|
93
|
+
pending = pending.subarray(2);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
85
96
|
const decoded = decodeFirstCharacter(pending.subarray(1));
|
|
86
97
|
if (decoded === undefined)
|
|
87
98
|
break;
|
|
@@ -106,7 +117,7 @@ export function createInputParser(options = {}) {
|
|
|
106
117
|
return {
|
|
107
118
|
feed(chunk) {
|
|
108
119
|
if (chunk.length > 0) {
|
|
109
|
-
if (pending.length
|
|
120
|
+
if (pending.length > 0 && pending.every((byte) => byte === ESC))
|
|
110
121
|
clearEscapeTimer();
|
|
111
122
|
pending = pending.length === 0 ? Buffer.from(chunk) : Buffer.concat([pending, chunk]);
|
|
112
123
|
}
|
|
@@ -142,7 +153,7 @@ function parseCsi(sequence) {
|
|
|
142
153
|
}
|
|
143
154
|
}
|
|
144
155
|
const final = sequence.at(-1);
|
|
145
|
-
const name = navigationName(final) ?? ({ "5": "pageup", "6": "pagedown" }[sequence.slice(2, -1)]);
|
|
156
|
+
const name = navigationName(final) ?? ({ "1": "home", "3": "delete", "4": "end", "5": "pageup", "6": "pagedown", "7": "home", "8": "end" }[sequence.slice(2, -1)]);
|
|
146
157
|
if (name === undefined)
|
|
147
158
|
return undefined;
|
|
148
159
|
const parameters = sequence.slice(2, -1).split(";");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.244",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"yaml"
|
|
159
159
|
],
|
|
160
160
|
"optionalDependencies": {
|
|
161
|
-
"toolcraft-schema": "0.0.
|
|
161
|
+
"toolcraft-schema": "0.0.244",
|
|
162
162
|
"toolcraft-design": "*",
|
|
163
163
|
"@poe-code/frontmatter": "*",
|
|
164
164
|
"@poe-code/agent-mcp-config": "*",
|