toolcraft 0.0.69 → 0.0.71

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.
@@ -40,13 +40,15 @@ export function computeExplorerLayout(opts) {
40
40
  footer
41
41
  };
42
42
  }
43
- const listWidth = mode === "wide" ? Math.floor((cols * 5) / 12) : Math.floor((cols * 2) / 5);
44
- const detailWidth = cols - listWidth;
43
+ const gutterWidth = 1;
44
+ const availableWidth = Math.max(0, cols - gutterWidth);
45
+ const listWidth = mode === "wide" ? Math.floor((availableWidth * 5) / 12) : Math.floor((availableWidth * 2) / 5);
46
+ const detailWidth = availableWidth - listWidth;
45
47
  return {
46
48
  mode,
47
49
  header,
48
50
  list: { x: 0, y: contentY, width: listWidth, height: contentHeight },
49
- detail: { x: listWidth, y: contentY, width: detailWidth, height: contentHeight },
51
+ detail: { x: listWidth + gutterWidth, y: contentY, width: detailWidth, height: contentHeight },
50
52
  footer
51
53
  };
52
54
  }
@@ -238,7 +238,9 @@ function detailLoaded(state, rowId, token, items) {
238
238
  };
239
239
  }
240
240
  function detailItemRendered(state, rowId, token, itemIndex, content) {
241
- if (state.detail.rowId !== rowId || state.detail.token !== token || state.detail.items?.[itemIndex] === undefined) {
241
+ if (state.detail.rowId !== rowId ||
242
+ state.detail.token !== token ||
243
+ state.detail.items?.[itemIndex] === undefined) {
242
244
  return mark(state, 0);
243
245
  }
244
246
  const items = state.detail.items.map((item, index) => index === itemIndex ? { ...item, renderedContent: content } : item);
@@ -354,7 +356,14 @@ function updateFilter(state, filter) {
354
356
  matchPositions,
355
357
  cursor,
356
358
  detail,
357
- actionState: recomputeActionState({ ...state, filter, filtered, matchPositions, cursor, detail }),
359
+ actionState: recomputeActionState({
360
+ ...state,
361
+ filter,
362
+ filtered,
363
+ matchPositions,
364
+ cursor,
365
+ detail
366
+ }),
358
367
  dirty: REGION_HEADER | REGION_LIST | REGION_DETAIL | REGION_FOOTER
359
368
  };
360
369
  const effect = detailEffect(next);
@@ -385,7 +394,11 @@ function escape(state, runtimeHandles) {
385
394
  if (state.filterFocused || state.filter.length > 0) {
386
395
  const cleared = updateFilter({ ...state, filterFocused: false }, "");
387
396
  return {
388
- state: { ...cleared.state, filterFocused: false, dirty: cleared.state.dirty | REGION_HEADER | REGION_FOOTER },
397
+ state: {
398
+ ...cleared.state,
399
+ filterFocused: false,
400
+ dirty: cleared.state.dirty | REGION_HEADER | REGION_FOOTER
401
+ },
389
402
  effects: cleared.effects
390
403
  };
391
404
  }
@@ -500,9 +513,9 @@ function detailBodyHeight(state) {
500
513
  if (state.layout === "narrow-vertical") {
501
514
  const listHeight = Math.ceil(contentHeight / 2);
502
515
  const detailHeight = contentHeight - listHeight;
503
- return Math.max(0, detailHeight - 1);
516
+ return Math.max(0, detailHeight - 2);
504
517
  }
505
- return contentHeight;
518
+ return Math.max(0, contentHeight - 2);
506
519
  }
507
520
  function extendSelection(state, delta) {
508
521
  if (!state.multiSelect) {
@@ -557,7 +570,10 @@ function reorder(state, delta) {
557
570
  actionState: recomputeActionState({ ...state, rows, filtered, matchPositions, cursor }),
558
571
  dirty: REGION_LIST | REGION_FOOTER
559
572
  };
560
- return { state: next, effects: [{ type: "persistOrder", orderedIds: rows.map((row) => row.id) }] };
573
+ return {
574
+ state: next,
575
+ effects: [{ type: "persistOrder", orderedIds: rows.map((row) => row.id) }]
576
+ };
561
577
  }
562
578
  function paletteInput(state, key) {
563
579
  if (state.modal?.kind !== "palette") {
@@ -1,4 +1,7 @@
1
+ import { stripAnsi } from "../../internal/strip-ansi.js";
2
+ import { renderMarkdown } from "../../terminal-markdown/index.js";
1
3
  import { getExplorerStyles } from "../theme.js";
4
+ import { drawPaneFrame, paneBodyRect } from "./pane.js";
2
5
  import { fitToWidth } from "./text.js";
3
6
  export function renderDetail(state, screen, layout) {
4
7
  const rect = layout.detail;
@@ -9,16 +12,19 @@ export function renderDetail(state, screen, layout) {
9
12
  }
10
13
  const row = state.rows.find((r) => r.id === state.detail.rowId) ?? null;
11
14
  if (layout.mode === "narrow-vertical") {
12
- screen.put(rect.x, rect.y, `├─ Detail ${"".repeat(Math.max(0, rect.width - 11))}┤`, styles.border);
13
- renderDetailBody(state, screen, { ...rect, y: rect.y + 1, height: rect.height - 1 }, row);
15
+ drawPaneFrame(screen, rect, "Preview", state.focused === "detail" ? styles.borderFocused : styles.border);
16
+ renderDetailBody(state, screen, paneBodyRect(rect), row);
14
17
  return;
15
18
  }
16
- screen.put(rect.x, rect.y, "", styles.border);
17
- renderDetailBody(state, screen, { ...rect, x: rect.x + 1, width: rect.width - 1 }, row);
19
+ drawPaneFrame(screen, rect, "Preview", state.focused === "detail" ? styles.borderFocused : styles.border);
20
+ renderDetailBody(state, screen, paneBodyRect(rect), row);
18
21
  }
19
22
  function renderDetailBody(state, screen, rect, row) {
20
23
  const styles = getExplorerStyles();
21
24
  const items = state.detail.items;
25
+ if (rect.width <= 0 || rect.height <= 0) {
26
+ return;
27
+ }
22
28
  if (items === null) {
23
29
  writeLine(screen, rect, 0, state.detail.loading ? "Loading detail..." : state.emptyHint, styles.muted);
24
30
  return;
@@ -28,7 +34,7 @@ function renderDetailBody(state, screen, rect, row) {
28
34
  return;
29
35
  }
30
36
  if (items.length === 1 && items[0]?.title === undefined) {
31
- renderBlob(screen, rect, renderItem(items[0], rect, row), state.detail.scroll);
37
+ renderBlob(screen, rect, renderItemMarkdown(items[0], rect, row), state.detail.scroll);
32
38
  return;
33
39
  }
34
40
  renderListMode(state, screen, rect, items, row);
@@ -49,7 +55,7 @@ function renderListMode(state, screen, rect, items, row) {
49
55
  writeLine(screen, rect, y, ` ${item.subtitle}`, styles.muted);
50
56
  y += 1;
51
57
  }
52
- for (const line of renderItem(item, rect, row).split("\n")) {
58
+ for (const line of renderItemMarkdown(item, rect, row).split("\n")) {
53
59
  if (y >= rect.height) {
54
60
  break;
55
61
  }
@@ -69,6 +75,13 @@ function renderBlob(screen, rect, text, scroll) {
69
75
  writeLine(screen, rect, row, lines[row] ?? "");
70
76
  }
71
77
  }
78
+ function renderItemMarkdown(item, rect, row) {
79
+ const content = renderItem(item, rect, row);
80
+ if (content.trim().length === 0) {
81
+ return "";
82
+ }
83
+ return stripAnsi(renderMarkdown(content, { width: Math.max(1, rect.width) }).trimEnd());
84
+ }
72
85
  function renderItem(item, rect, row) {
73
86
  if (item.renderedContent !== undefined) {
74
87
  return item.renderedContent;
@@ -1,4 +1,5 @@
1
1
  import { getExplorerStyles } from "../theme.js";
2
+ import { drawPaneFrame, paneBodyRect } from "./pane.js";
2
3
  import { cellWidth, centerCells, fitToWidth, splitGraphemeCells, stripAnsi } from "./text.js";
3
4
  const listLineCache = new WeakMap();
4
5
  export function renderList(state, screen, layout) {
@@ -12,36 +13,41 @@ export function renderList(state, screen, layout) {
12
13
  writeLine(screen, rect, 0, "Terminal too narrow", styles.muted);
13
14
  return;
14
15
  }
15
- const rectKey = `${rect.x}:${rect.y}:${rect.width}:${rect.height}`;
16
+ drawPaneFrame(screen, rect, "Plans", state.focused === "list" ? styles.borderFocused : styles.border);
17
+ const bodyRect = paneBodyRect(rect);
18
+ if (bodyRect.width <= 0 || bodyRect.height <= 0) {
19
+ return;
20
+ }
21
+ const rectKey = `${bodyRect.x}:${bodyRect.y}:${bodyRect.width}:${bodyRect.height}`;
16
22
  const cached = listLineCache.get(screen);
17
23
  const cache = cached?.rectKey === rectKey ? cached.lines : new Map();
18
24
  listLineCache.set(screen, { rectKey, lines: cache });
19
25
  if (state.filtered.length === 0) {
20
26
  const hint = state.emptyHint;
21
- writeLine(screen, rect, Math.floor(rect.height / 2), centerCells(hint, rect.width, rect.x), styles.muted);
27
+ writeLine(screen, bodyRect, Math.floor(bodyRect.height / 2), centerCells(hint, bodyRect.width, bodyRect.x), styles.muted);
22
28
  cache.clear();
23
29
  return;
24
30
  }
25
31
  let lastGroup;
26
32
  let y = 0;
27
33
  for (const rowIndex of state.filtered) {
28
- if (y >= rect.height) {
34
+ if (y >= bodyRect.height) {
29
35
  break;
30
36
  }
31
37
  const row = state.rows[rowIndex];
32
38
  if (!row) {
33
39
  continue;
34
40
  }
35
- if (row.group && row.group !== lastGroup && y < rect.height) {
41
+ if (row.group && row.group !== lastGroup && y < bodyRect.height) {
36
42
  const hash = `group:${row.group}`;
37
43
  if (cache.get(y) !== hash) {
38
- writeLine(screen, rect, y, row.group, styles.muted);
44
+ writeLine(screen, bodyRect, y, row.group, styles.muted);
39
45
  cache.set(y, hash);
40
46
  }
41
47
  y += 1;
42
48
  lastGroup = row.group;
43
49
  }
44
- if (y >= rect.height) {
50
+ if (y >= bodyRect.height) {
45
51
  break;
46
52
  }
47
53
  const selected = state.multiSelect && state.selected.has(row.id);
@@ -49,14 +55,19 @@ export function renderList(state, screen, layout) {
49
55
  const positions = state.matchPositions.get(rowIndex) ?? [];
50
56
  const hash = lineHash(row, selected, cursor, positions);
51
57
  if (cache.get(y) !== hash) {
52
- renderRow(screen, rect, y, row, { selected, cursor, focused: state.focused === "list", positions });
58
+ renderRow(screen, bodyRect, y, row, {
59
+ selected,
60
+ cursor,
61
+ focused: state.focused === "list",
62
+ positions
63
+ });
53
64
  cache.set(y, hash);
54
65
  }
55
66
  y += 1;
56
- if (row.subtitle && y < rect.height) {
67
+ if (row.subtitle && y < bodyRect.height) {
57
68
  const subtitleHash = `${hash}:subtitle:${row.subtitle}`;
58
69
  if (cache.get(y) !== subtitleHash) {
59
- writeLine(screen, rect, y, ` ${row.subtitle}`, styles.muted);
70
+ writeLine(screen, bodyRect, y, ` ${row.subtitle}`, styles.muted);
60
71
  cache.set(y, subtitleHash);
61
72
  }
62
73
  y += 1;
@@ -0,0 +1,5 @@
1
+ import { ScreenBuffer } from "../../dashboard/buffer.js";
2
+ import type { CellStyle } from "../../dashboard/types.js";
3
+ import type { Rect } from "../layout.js";
4
+ export declare function drawPaneFrame(screen: ScreenBuffer, rect: Rect, title: string, style?: CellStyle): void;
5
+ export declare function paneBodyRect(rect: Rect): Rect;
@@ -0,0 +1,30 @@
1
+ import { fitToWidth, padEndCells } from "./text.js";
2
+ export function drawPaneFrame(screen, rect, title, style = {}) {
3
+ if (rect.width <= 0 || rect.height <= 0) {
4
+ return;
5
+ }
6
+ if (rect.width === 1) {
7
+ for (let y = 0; y < rect.height; y += 1) {
8
+ screen.put(rect.x, rect.y + y, "│", style);
9
+ }
10
+ return;
11
+ }
12
+ const innerWidth = Math.max(0, rect.width - 2);
13
+ const titleSegment = padEndCells(fitToWidth(`─ ${title} `, innerWidth, rect.x + 1), innerWidth, "─", rect.x + 1);
14
+ screen.put(rect.x, rect.y, `┌${titleSegment}┐`, style);
15
+ for (let y = 1; y < rect.height - 1; y += 1) {
16
+ screen.put(rect.x, rect.y + y, "│", style);
17
+ screen.put(rect.x + rect.width - 1, rect.y + y, "│", style);
18
+ }
19
+ if (rect.height > 1) {
20
+ screen.put(rect.x, rect.y + rect.height - 1, `└${"─".repeat(innerWidth)}┘`, style);
21
+ }
22
+ }
23
+ export function paneBodyRect(rect) {
24
+ return {
25
+ x: rect.x + 2,
26
+ y: rect.y + 1,
27
+ width: Math.max(0, rect.width - 4),
28
+ height: Math.max(0, rect.height - 2)
29
+ };
30
+ }
@@ -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.unsubscribeKeypress = this.driver.onKeypress((key) => {
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.name === "tab") {
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: string;
14
- readonly checkboxSelected: string;
15
- readonly checkboxInactive: string;
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: glyph("◻", "[ ]"),
31
- checkboxSelected: glyph("◼", "[+]"),
32
- checkboxInactive: glyph("◻", "[ ]"),
30
+ checkboxActive: "[ ]",
31
+ checkboxSelected: "[x]",
32
+ checkboxInactive: "[ ]",
33
33
  passwordMask: glyph("•", "*"),
34
34
  ellipsis: "..."
35
35
  };
@@ -21,6 +21,9 @@ export function mapKey(name, char) {
21
21
  if (char === " ") {
22
22
  return "space";
23
23
  }
24
+ if (char !== undefined && aliases[char] !== undefined) {
25
+ return aliases[char];
26
+ }
24
27
  if (!name) {
25
28
  return undefined;
26
29
  }
@@ -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 labels = prompt.visibleOptions
106
- .filter((option) => hasValue(prompt.value, option.value))
107
- .map((option) => prompt.state === "submit" ? color.dim(option.label) : color.dim.strikethrough(option.label))
108
- .join(", ");
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.69",
3
+ "version": "0.0.71",
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.69",
51
- "commander": "^14.0.3",
50
+ "toolcraft-schema": "0.0.71",
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": ">=20"
66
+ "node": ">=18.18"
67
67
  },
68
68
  "repository": {
69
69
  "type": "git",