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.
Files changed (30) hide show
  1. package/composition.json +2 -2
  2. package/dist/composition.json +2 -2
  3. package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
  4. package/node_modules/toolcraft-design/README.md +6 -0
  5. package/node_modules/toolcraft-design/dist/acp/index.d.ts +2 -0
  6. package/node_modules/toolcraft-design/dist/acp/index.js +1 -0
  7. package/node_modules/toolcraft-design/dist/acp/plan.d.ts +10 -0
  8. package/node_modules/toolcraft-design/dist/acp/plan.js +50 -0
  9. package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.d.ts +6 -2
  10. package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.js +118 -6
  11. package/node_modules/toolcraft-design/dist/dashboard/components/run-view.d.ts +27 -0
  12. package/node_modules/toolcraft-design/dist/dashboard/components/run-view.js +275 -0
  13. package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.d.ts +1 -1
  14. package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.js +2 -8
  15. package/node_modules/toolcraft-design/dist/dashboard/composer-layout.d.ts +12 -0
  16. package/node_modules/toolcraft-design/dist/dashboard/composer-layout.js +45 -0
  17. package/node_modules/toolcraft-design/dist/dashboard/composer.d.ts +24 -0
  18. package/node_modules/toolcraft-design/dist/dashboard/composer.js +107 -0
  19. package/node_modules/toolcraft-design/dist/dashboard/dashboard.d.ts +4 -0
  20. package/node_modules/toolcraft-design/dist/dashboard/dashboard.js +198 -37
  21. package/node_modules/toolcraft-design/dist/dashboard/elapsed.d.ts +1 -0
  22. package/node_modules/toolcraft-design/dist/dashboard/elapsed.js +8 -0
  23. package/node_modules/toolcraft-design/dist/dashboard/index.d.ts +2 -1
  24. package/node_modules/toolcraft-design/dist/dashboard/store.js +2 -1
  25. package/node_modules/toolcraft-design/dist/dashboard/terminal-width.js +1 -1
  26. package/node_modules/toolcraft-design/dist/dashboard/terminal.js +22 -47
  27. package/node_modules/toolcraft-design/dist/dashboard/types.d.ts +37 -0
  28. package/node_modules/toolcraft-design/dist/terminal/input.js +24 -13
  29. package/node_modules/toolcraft-schema/package.json +1 -1
  30. 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 Set();
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 listener = (chunk) => {
132
- for (const event of parseKeypressChunk(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))) {
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.add(listener);
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 === 1 && pending[0] === ESC) {
22
- pending = pending.subarray(1);
23
- options.onEvent(key("escape"));
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.length === 1) {
45
+ if (pending.every((byte) => byte === ESC)) {
44
46
  if (escapeReady) {
45
- pending = pending.subarray(1);
47
+ const count = pending.length;
48
+ pending = Buffer.alloc(0);
46
49
  escapeReady = false;
47
- events.push(key("escape"));
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
- if (pending[1] === 0x5b) {
61
- const final = findCsiFinal(pending);
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(0, final + 1).toString("ascii");
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 === 1 && pending[0] === ESC)
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(";");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.243",
3
+ "version": "0.0.244",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.243",
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.243",
161
+ "toolcraft-schema": "0.0.244",
162
162
  "toolcraft-design": "*",
163
163
  "@poe-code/frontmatter": "*",
164
164
  "@poe-code/agent-mcp-config": "*",