@wukonglabs/wukong 0.0.8 → 0.0.10

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 (70) hide show
  1. package/dist/cli/exit-codes.js +12 -16
  2. package/dist/cli/exit-codes.js.map +1 -1
  3. package/dist/cli/help.d.ts.map +1 -1
  4. package/dist/cli/help.js +10 -9
  5. package/dist/cli/help.js.map +1 -1
  6. package/dist/cli/session-commands.d.ts +28 -0
  7. package/dist/cli/session-commands.d.ts.map +1 -0
  8. package/dist/cli/session-commands.js +274 -0
  9. package/dist/cli/session-commands.js.map +1 -0
  10. package/dist/cli/summary.js +2 -6
  11. package/dist/cli/summary.js.map +1 -1
  12. package/dist/cli/workspace-commands.d.ts +15 -0
  13. package/dist/cli/workspace-commands.d.ts.map +1 -0
  14. package/dist/cli/workspace-commands.js +98 -0
  15. package/dist/cli/workspace-commands.js.map +1 -0
  16. package/dist/index.js +429 -131
  17. package/dist/index.js.map +1 -1
  18. package/dist/sigint-handler.d.ts +62 -0
  19. package/dist/sigint-handler.d.ts.map +1 -0
  20. package/dist/sigint-handler.js +62 -0
  21. package/dist/sigint-handler.js.map +1 -0
  22. package/dist/tui/app.d.ts +57 -0
  23. package/dist/tui/app.d.ts.map +1 -0
  24. package/dist/tui/app.js +194 -0
  25. package/dist/tui/app.js.map +1 -0
  26. package/dist/tui/header.d.ts +24 -0
  27. package/dist/tui/header.d.ts.map +1 -0
  28. package/dist/tui/header.js +26 -0
  29. package/dist/tui/header.js.map +1 -0
  30. package/dist/tui/permission-card.d.ts +27 -0
  31. package/dist/tui/permission-card.d.ts.map +1 -0
  32. package/dist/tui/permission-card.js +75 -0
  33. package/dist/tui/permission-card.js.map +1 -0
  34. package/dist/tui/plan-panel.d.ts +29 -0
  35. package/dist/tui/plan-panel.d.ts.map +1 -0
  36. package/dist/tui/plan-panel.js +26 -0
  37. package/dist/tui/plan-panel.js.map +1 -0
  38. package/dist/tui/response-panel.d.ts +49 -0
  39. package/dist/tui/response-panel.d.ts.map +1 -0
  40. package/dist/tui/response-panel.js +13 -0
  41. package/dist/tui/response-panel.js.map +1 -0
  42. package/dist/tui/run-ink.d.ts +22 -0
  43. package/dist/tui/run-ink.d.ts.map +1 -0
  44. package/dist/tui/run-ink.js +31 -0
  45. package/dist/tui/run-ink.js.map +1 -0
  46. package/dist/tui/should-use-tui.d.ts +25 -0
  47. package/dist/tui/should-use-tui.d.ts.map +1 -0
  48. package/dist/tui/should-use-tui.js +29 -0
  49. package/dist/tui/should-use-tui.js.map +1 -0
  50. package/dist/tui/text-input.d.ts +52 -0
  51. package/dist/tui/text-input.d.ts.map +1 -0
  52. package/dist/tui/text-input.js +321 -0
  53. package/dist/tui/text-input.js.map +1 -0
  54. package/dist/tui/thinking-panel.d.ts +40 -0
  55. package/dist/tui/thinking-panel.d.ts.map +1 -0
  56. package/dist/tui/thinking-panel.js +54 -0
  57. package/dist/tui/thinking-panel.js.map +1 -0
  58. package/dist/tui/tool-cards.d.ts +30 -0
  59. package/dist/tui/tool-cards.d.ts.map +1 -0
  60. package/dist/tui/tool-cards.js +34 -0
  61. package/dist/tui/tool-cards.js.map +1 -0
  62. package/dist/tui/use-input-history.d.ts +40 -0
  63. package/dist/tui/use-input-history.d.ts.map +1 -0
  64. package/dist/tui/use-input-history.js +123 -0
  65. package/dist/tui/use-input-history.js.map +1 -0
  66. package/dist/tui/use-plan-state.d.ts +4 -0
  67. package/dist/tui/use-plan-state.d.ts.map +1 -0
  68. package/dist/tui/use-plan-state.js +73 -0
  69. package/dist/tui/use-plan-state.js.map +1 -0
  70. package/package.json +7 -2
@@ -0,0 +1,321 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * 多行文本输入组件(自研,Ink 自带 ink-text-input 不支持多行 + Ctrl+R)。
4
+ *
5
+ * ## 交互
6
+ *
7
+ * - **Enter**:提交(若 `multiline` 为 false,任何 Enter 都提交)
8
+ * - **Shift+Enter / Alt+Enter**:换行(仅 multiline=true 时)
9
+ * - **Ctrl+J**:换行(终端友好,Ctrl+J = LF)
10
+ * - **↑ / ↓**(单行时):浏览历史
11
+ * - **↑ / ↓**(多行时):移动光标到上/下一行
12
+ * - **Ctrl+R**:进入反查模式;再按跳下一条匹配;Enter 提交当前匹配;Esc 退出
13
+ * - **Ctrl+C**:清理后退出(由 App 层接管 graceful shutdown)
14
+ * - **Ctrl+L**:清屏(Ink 原生 clear)
15
+ *
16
+ * ## 粘贴检测
17
+ *
18
+ * Ink 没有原生 paste 事件。通过监听 `stdin` 的 data chunk,当一次 chunk 中
19
+ * 含多个字符(特别是含换行)时,判定为粘贴,直接整体插入(不走逐字符 input)。
20
+ * 这避免了粘贴中文/长文本时每个字符都触发一次 re-render 的性能问题。
21
+ *
22
+ * ## 可访问性
23
+ *
24
+ * `multiline` 模式下不依赖光标可视化(终端光标控制在不同终端差异大),
25
+ * 用 `▋` 字符标记光标位置;`NO_COLOR` 时仍可见。
26
+ */
27
+ import { useState, useCallback, useEffect, useRef } from 'react';
28
+ import { Text, useInput, useStdin } from 'ink';
29
+ import { reverseSearch, reverseSearchNext, } from './use-input-history.js';
30
+ const INITIAL_RS = {
31
+ active: false,
32
+ query: '',
33
+ matchIndex: -1,
34
+ };
35
+ export function TextInput({ value, onChange, onSubmit, multiline = true, history, prompt = '> ', placeholder = '', disabled = false, onAbort, }) {
36
+ const [cursor, setCursor] = useState(value.length);
37
+ const [rs, setRs] = useState(INITIAL_RS);
38
+ const { stdin, setRawMode } = useStdin();
39
+ // 用于粘贴检测:记录上次 stdin data 到达时间
40
+ const lastChunkTimeRef = useRef(0);
41
+ // stdin 原始模式(捕获 Ctrl+C / Ctrl+R 等控制字符)
42
+ useEffect(() => {
43
+ if (stdin && typeof setRawMode === 'function') {
44
+ setRawMode(true);
45
+ return () => setRawMode(false);
46
+ }
47
+ return;
48
+ }, [stdin, setRawMode]);
49
+ // ── 粘贴检测:监听 stdin chunk,多字符 chunk 视为粘贴 ──
50
+ useEffect(() => {
51
+ if (!stdin)
52
+ return;
53
+ const onData = (chunk) => {
54
+ const data = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
55
+ // 单字符走 useInput;多字符(>=2)且不是控制序列 → 粘贴
56
+ if (data.length >= 2) {
57
+ const now = Date.now();
58
+ const isPaste = now - lastChunkTimeRef.current < 50 || data.includes('\n');
59
+ lastChunkTimeRef.current = now;
60
+ if (isPaste) {
61
+ // 走整体插入(去掉末尾的单个换行,因为很多粘贴末尾带 \n)
62
+ const cleaned = data.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
63
+ insertText(cleaned);
64
+ }
65
+ }
66
+ else {
67
+ lastChunkTimeRef.current = Date.now();
68
+ }
69
+ };
70
+ stdin.on('data', onData);
71
+ return () => {
72
+ stdin.off('data', onData);
73
+ };
74
+ // eslint-disable-next-line react-hooks/exhaustive-deps
75
+ }, [stdin, value, cursor, rs]);
76
+ // 插入文本到光标位置
77
+ const insertText = useCallback((text) => {
78
+ const newValue = value.slice(0, cursor) + text + value.slice(cursor);
79
+ onChange(newValue);
80
+ setCursor(cursor + text.length);
81
+ }, [value, cursor, onChange]);
82
+ // 提交当前输入
83
+ const submit = useCallback(() => {
84
+ const trimmed = value;
85
+ if (!trimmed)
86
+ return;
87
+ history?.push(trimmed);
88
+ onSubmit(trimmed);
89
+ onChange('');
90
+ setCursor(0);
91
+ setRs(INITIAL_RS);
92
+ }, [value, history, onSubmit, onChange]);
93
+ useInput((input, key) => {
94
+ if (disabled)
95
+ return;
96
+ // ── Ctrl+R:反查模式 ──
97
+ if (key.ctrl && input === 'r') {
98
+ if (!history || history.entries.length === 0)
99
+ return;
100
+ if (!rs.active) {
101
+ // 进入反查模式,空 query → 找最近一条
102
+ const match = reverseSearch(history.entries, '', history.entries.length - 1);
103
+ setRs({ active: true, query: '', matchIndex: match.index });
104
+ if (match.text)
105
+ onChange(match.text);
106
+ }
107
+ else {
108
+ // 已经在反查模式 → 跳下一条匹配
109
+ if (rs.matchIndex >= 0) {
110
+ const next = reverseSearchNext(history.entries, rs.query, rs.matchIndex);
111
+ setRs({ ...rs, matchIndex: next.index });
112
+ if (next.text)
113
+ onChange(next.text);
114
+ }
115
+ }
116
+ return;
117
+ }
118
+ // ── 反查模式下的其他按键 ──
119
+ if (rs.active) {
120
+ if (key.escape) {
121
+ // Esc:退出反查,保留当前匹配作为输入
122
+ setRs(INITIAL_RS);
123
+ return;
124
+ }
125
+ if (key.return) {
126
+ // Enter:提交当前匹配
127
+ setRs(INITIAL_RS);
128
+ submit();
129
+ return;
130
+ }
131
+ if (key.backspace || key.delete) {
132
+ // 删除 query 最后一个字符,重新匹配
133
+ const newQuery = rs.query.slice(0, -1);
134
+ const match = reverseSearch(history.entries, newQuery);
135
+ setRs({ ...rs, query: newQuery, matchIndex: match.index });
136
+ if (match.text)
137
+ onChange(match.text);
138
+ else
139
+ onChange(newQuery);
140
+ return;
141
+ }
142
+ // 可打印字符 → 追加到 query,重新匹配
143
+ if (input && !key.ctrl && !key.meta && input.length === 1 && input >= ' ') {
144
+ const newQuery = rs.query + input;
145
+ const match = reverseSearch(history.entries, newQuery);
146
+ setRs({ ...rs, query: newQuery, matchIndex: match.index });
147
+ if (match.text)
148
+ onChange(match.text);
149
+ else
150
+ onChange(newQuery);
151
+ return;
152
+ }
153
+ // 其他控制键在反查模式下忽略
154
+ return;
155
+ }
156
+ // ── 普通模式 ──
157
+ if (key.return) {
158
+ // Shift+Enter / Meta+Enter → 换行(多行模式)
159
+ if (multiline && (key.shift || key.meta)) {
160
+ insertText('\n');
161
+ return;
162
+ }
163
+ // 普通 Enter → 提交
164
+ submit();
165
+ return;
166
+ }
167
+ if (key.ctrl && input === 'j') {
168
+ // Ctrl+J = LF,多行模式下换行
169
+ if (multiline) {
170
+ insertText('\n');
171
+ }
172
+ else {
173
+ submit();
174
+ }
175
+ return;
176
+ }
177
+ if (key.ctrl && input === 'c') {
178
+ // §3.3 M3:Ctrl+C 交给 App 层处理两次语义(graceful shutdown)
179
+ // Ink raw mode拦截 Ctrl+C 不走 process SIGINT,故需显式回调
180
+ onAbort?.();
181
+ return;
182
+ }
183
+ if (key.backspace || key.delete) {
184
+ if (cursor > 0) {
185
+ // 删除光标前一个字符(含跨行 \n)
186
+ const newValue = value.slice(0, cursor - 1) + value.slice(cursor);
187
+ onChange(newValue);
188
+ setCursor(cursor - 1);
189
+ }
190
+ return;
191
+ }
192
+ if (key.leftArrow) {
193
+ setCursor(Math.max(0, cursor - 1));
194
+ return;
195
+ }
196
+ if (key.rightArrow) {
197
+ setCursor(Math.min(value.length, cursor + 1));
198
+ return;
199
+ }
200
+ if (key.upArrow) {
201
+ // 多行模式:光标移到上一行(如果有)
202
+ if (multiline) {
203
+ const { lineStart, lineIndex, colInLine } = getLineInfo(value, cursor);
204
+ if (lineIndex > 0) {
205
+ // 上一行同列位置
206
+ const prevLineStart = value.lastIndexOf('\n', lineStart - 1) + 1;
207
+ const prevLineEnd = lineStart - 1;
208
+ const prevLineLen = prevLineEnd - prevLineStart;
209
+ const newCol = Math.min(colInLine, prevLineLen);
210
+ setCursor(prevLineStart + newCol);
211
+ return;
212
+ }
213
+ }
214
+ // 单行模式 或 多行已在第一行 → 浏览历史
215
+ if (history) {
216
+ const text = history.navigate('up', value);
217
+ if (text !== null) {
218
+ onChange(text);
219
+ setCursor(text.length);
220
+ }
221
+ }
222
+ return;
223
+ }
224
+ if (key.downArrow) {
225
+ if (multiline) {
226
+ const { lineEnd, lineIndex, colInLine, totalLines } = getLineInfo(value, cursor);
227
+ if (lineIndex < totalLines - 1) {
228
+ const nextLineStart = lineEnd + 1;
229
+ const nextLineEnd = value.indexOf('\n', nextLineStart);
230
+ const nextLineLen = (nextLineEnd === -1 ? value.length : nextLineEnd) - nextLineStart;
231
+ const newCol = Math.min(colInLine, nextLineLen);
232
+ setCursor(nextLineStart + newCol);
233
+ return;
234
+ }
235
+ }
236
+ // 单行 或 多行最后一行 → 浏览历史
237
+ if (history) {
238
+ const text = history.navigate('down', value);
239
+ if (text !== null) {
240
+ onChange(text);
241
+ setCursor(text.length);
242
+ }
243
+ else {
244
+ // 到底恢复草稿
245
+ history.exitNavigation();
246
+ onChange(history.draftBeforeNav);
247
+ setCursor(history.draftBeforeNav.length);
248
+ }
249
+ }
250
+ return;
251
+ }
252
+ // Home / End
253
+ if (key.ctrl && input === 'a') {
254
+ // 行首
255
+ const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
256
+ setCursor(lineStart);
257
+ return;
258
+ }
259
+ if (key.ctrl && input === 'e') {
260
+ // 行尾
261
+ const lineEnd = value.indexOf('\n', cursor);
262
+ setCursor(lineEnd === -1 ? value.length : lineEnd);
263
+ return;
264
+ }
265
+ // 可打印字符
266
+ if (input && !key.ctrl && !key.meta && input.length === 1 && input >= ' ') {
267
+ // 任何编辑都退出历史导航
268
+ history?.exitNavigation();
269
+ insertText(input);
270
+ return;
271
+ }
272
+ });
273
+ // ── 渲染 ──
274
+ // 反查模式:显示 (reverse-i-search)`query`: match
275
+ if (rs.active) {
276
+ const matchText = rs.matchIndex >= 0 && history ? history.entries[rs.matchIndex] : '';
277
+ return (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: ["(reverse-i-search)`", rs.query, "`: "] }), _jsx(Text, { color: "cyan", children: matchText || '(无匹配)' })] }));
278
+ }
279
+ // 空输入 + placeholder
280
+ if (!value && placeholder) {
281
+ return (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: prompt }), _jsx(Text, { dimColor: true, children: placeholder })] }));
282
+ }
283
+ // 带光标渲染(▋ 标记光标位置)
284
+ const before = value.slice(0, cursor);
285
+ const at = value.slice(cursor, cursor + 1);
286
+ const after = value.slice(cursor + 1);
287
+ return (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: prompt }), before, _jsx(Text, { inverse: true, children: at || ' ' }), after] }));
288
+ }
289
+ function getLineInfo(value, cursor) {
290
+ // 找当前行的起始
291
+ let lineStart = 0;
292
+ for (let i = cursor - 1; i >= 0; i--) {
293
+ if (value[i] === '\n') {
294
+ lineStart = i + 1;
295
+ break;
296
+ }
297
+ }
298
+ // 找当前行的结束
299
+ let lineEnd = value.length;
300
+ for (let i = cursor; i < value.length; i++) {
301
+ if (value[i] === '\n') {
302
+ lineEnd = i;
303
+ break;
304
+ }
305
+ }
306
+ // 行号 + 总行数
307
+ let lineIndex = 0;
308
+ for (let i = 0; i < lineStart; i++) {
309
+ if (value[i] === '\n')
310
+ lineIndex++;
311
+ }
312
+ const totalLines = (value.match(/\n/g)?.length ?? 0) + 1;
313
+ return {
314
+ lineStart,
315
+ lineEnd,
316
+ lineIndex,
317
+ totalLines,
318
+ colInLine: cursor - lineStart,
319
+ };
320
+ }
321
+ //# sourceMappingURL=text-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-input.js","sourceRoot":"","sources":["../../src/tui/text-input.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Thinking panel —— 流式渲染 agent 的思考过程(thinking_delta)。
3
+ *
4
+ * ## 数据源
5
+ *
6
+ * onStream 回调的 `thinking_delta` 事件(不在 EventBus 里,属于 LLM 流式层)。
7
+ * App 层把 thinking_delta 转发到本组件的 props。
8
+ *
9
+ * ## 渲染
10
+ *
11
+ * 可折叠;默认折叠(只显示一行摘要),按 `t` 键展开。
12
+ * 折叠时:
13
+ * ```
14
+ * 💭 Thinking... (247 字符)
15
+ * ```
16
+ * 展开时:
17
+ * ```
18
+ * 💭 Thinking...
19
+ * 让我先看看 utils/ 下面的文件,可能有重复...
20
+ * ```
21
+ *
22
+ * ## 性能
23
+ *
24
+ * thinking_delta 可能高频(每秒几十次)。本组件用 React state 累积,
25
+ * 依赖 React 的 batch 更新 + Ink 的 30 FPS 渲染上限自然 throttle。
26
+ * 若实测卡顿,M5 后续可加显式 throttle(参见 §3.1 性能预算)。
27
+ */
28
+ import React from 'react';
29
+ export interface ThinkingPanelProps {
30
+ /** 当前累积的思考内容 */
31
+ thinking: string;
32
+ /** 是否正在流式接收 thinking */
33
+ streaming: boolean;
34
+ /** 是否展开(受控);不传则组件内部管理 */
35
+ expanded?: boolean;
36
+ /** 折叠/展开切换回调 */
37
+ onToggleExpand?: (expanded: boolean) => void;
38
+ }
39
+ export declare function ThinkingPanel({ thinking, streaming, expanded, onToggleExpand, }: ThinkingPanelProps): React.ReactElement | null;
40
+ //# sourceMappingURL=thinking-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinking-panel.d.ts","sourceRoot":"","sources":["../../src/tui/thinking-panel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,MAAM,WAAW,kBAAkB;IACjC,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB;IAChB,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CAC9C;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,SAAS,EACT,QAAgB,EAChB,cAAc,GACf,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAuChD"}
@@ -0,0 +1,54 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Thinking panel —— 流式渲染 agent 的思考过程(thinking_delta)。
4
+ *
5
+ * ## 数据源
6
+ *
7
+ * onStream 回调的 `thinking_delta` 事件(不在 EventBus 里,属于 LLM 流式层)。
8
+ * App 层把 thinking_delta 转发到本组件的 props。
9
+ *
10
+ * ## 渲染
11
+ *
12
+ * 可折叠;默认折叠(只显示一行摘要),按 `t` 键展开。
13
+ * 折叠时:
14
+ * ```
15
+ * 💭 Thinking... (247 字符)
16
+ * ```
17
+ * 展开时:
18
+ * ```
19
+ * 💭 Thinking...
20
+ * 让我先看看 utils/ 下面的文件,可能有重复...
21
+ * ```
22
+ *
23
+ * ## 性能
24
+ *
25
+ * thinking_delta 可能高频(每秒几十次)。本组件用 React state 累积,
26
+ * 依赖 React 的 batch 更新 + Ink 的 30 FPS 渲染上限自然 throttle。
27
+ * 若实测卡顿,M5 后续可加显式 throttle(参见 §3.1 性能预算)。
28
+ */
29
+ import { useState } from 'react';
30
+ import { Box, Text, useInput } from 'ink';
31
+ export function ThinkingPanel({ thinking, streaming, expanded = false, onToggleExpand, }) {
32
+ const [internalExpanded, setInternalExpanded] = useState(expanded);
33
+ const isExpanded = onToggleExpand ? expanded : internalExpanded;
34
+ // 按 t 键切换展开/折叠(仅在有内容时响应)
35
+ useInput((input) => {
36
+ if (input === 't' && thinking.length > 0) {
37
+ const next = !isExpanded;
38
+ if (onToggleExpand)
39
+ onToggleExpand(next);
40
+ else
41
+ setInternalExpanded(next);
42
+ }
43
+ });
44
+ // 无内容 → 不渲染
45
+ if (!thinking)
46
+ return null;
47
+ const icon = streaming ? '💭' : '💭';
48
+ const summary = `(按 t 展开, ${thinking.length} 字符)`;
49
+ if (!isExpanded) {
50
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: "magenta", children: [icon, " Thinking..."] }), _jsxs(Text, { dimColor: true, children: [" ", summary] })] }));
51
+ }
52
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: "magenta", bold: true, children: [icon, " Thinking"] }), _jsx(Text, { dimColor: true, children: " (\u6309 t \u6298\u53E0)" })] }), _jsx(Text, { dimColor: true, color: "magenta", children: thinking })] }));
53
+ }
54
+ //# sourceMappingURL=thinking-panel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinking-panel.js","sourceRoot":"","sources":["../../src/tui/thinking-panel.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAa1C,MAAM,UAAU,aAAa,CAAC,EAC5B,QAAQ,EACR,SAAS,EACT,QAAQ,GAAG,KAAK,EAChB,cAAc,GACK;IACnB,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAEhE,yBAAyB;IACzB,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,IAAI,KAAK,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC;YACzB,IAAI,cAAc;gBAAE,cAAc,CAAC,IAAI,CAAC,CAAC;;gBACpC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,YAAY;IACZ,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACrC,MAAM,OAAO,GAAG,YAAY,QAAQ,CAAC,MAAM,MAAM,CAAC;IAElD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CACL,MAAC,GAAG,eACF,MAAC,IAAI,IAAC,KAAK,EAAC,SAAS,aAAE,IAAI,oBAAoB,EAC/C,MAAC,IAAI,IAAC,QAAQ,wBAAG,OAAO,IAAQ,IAC5B,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,MAAC,GAAG,eACF,MAAC,IAAI,IAAC,KAAK,EAAC,SAAS,EAAC,IAAI,mBAAE,IAAI,iBAAiB,EACjD,KAAC,IAAI,IAAC,QAAQ,+CAAiB,IAC3B,EACN,KAAC,IAAI,IAAC,QAAQ,QAAC,KAAK,EAAC,SAAS,YAC3B,QAAQ,GACJ,IACH,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Tool cards —— 每个工具调用一张卡片,展示状态/耗时/参数摘要。
3
+ *
4
+ * ## 数据源
5
+ *
6
+ * 订阅 EventBus 的 `tool_call_start` / `tool_call_end` 事件。
7
+ * 本组件接收 App 维护的 activeTools + completedTools 列表(由 App 订阅事件后传入),
8
+ * 保持组件本身纯展示,便于测试。
9
+ *
10
+ * ## 折叠
11
+ *
12
+ * 并行工具超过 `maxVisible`(默认 5)时,只展示最近 N 个 + "还有 X 个" 折叠提示。
13
+ * 避免一次 15 个 read_file 把终端刷爆(§3.1 性能预算)。
14
+ *
15
+ * ## 状态
16
+ *
17
+ * - ⏳ running(黄色)
18
+ * - ✓ done(绿色)
19
+ * - ✗ failed(红色)
20
+ */
21
+ import React from 'react';
22
+ import type { ToolCallState } from './response-panel.js';
23
+ export interface ToolCardsProps {
24
+ /** 活跃 + 最近完成的 tool call(已完成但未过期的) */
25
+ tools: ToolCallState[];
26
+ /** 最大可见卡片数;超出折叠(默认 5) */
27
+ maxVisible?: number;
28
+ }
29
+ export declare function ToolCards({ tools, maxVisible }: ToolCardsProps): React.ReactElement | null;
30
+ //# sourceMappingURL=tool-cards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-cards.d.ts","sourceRoot":"","sources":["../../src/tui/tool-cards.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,yBAAyB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,UAAc,EAAE,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAmB9F"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ export function ToolCards({ tools, maxVisible = 5 }) {
4
+ if (tools.length === 0)
5
+ return null;
6
+ // 按开始时间排序,最近在前
7
+ const sorted = [...tools].sort((a, b) => b.startedAt - a.startedAt);
8
+ const visible = sorted.slice(0, maxVisible);
9
+ const hidden = sorted.length - visible.length;
10
+ return (_jsxs(Box, { flexDirection: "column", marginY: 0, children: [_jsx(Text, { dimColor: true, bold: true, children: "\uD83D\uDD27 Tools" }), visible.map((t) => (_jsx(ToolCardRow, { tool: t }, t.toolCallId))), hidden > 0 && (_jsxs(Text, { dimColor: true, children: [" \u2026 \u8FD8\u6709 ", hidden, " \u4E2A\u5DE5\u5177\u8C03\u7528(\u5DF2\u6298\u53E0)"] }))] }));
11
+ }
12
+ function ToolCardRow({ tool }) {
13
+ const now = Date.now();
14
+ const elapsed = ((tool.endedAt ?? now) - tool.startedAt) / 1000;
15
+ const status = tool.success === undefined ? 'running' : tool.success ? 'done' : 'failed';
16
+ const { icon, color } = statusVisual(status);
17
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: color, children: icon }), _jsxs(Text, { children: [" \u26A1 ", tool.name] }), _jsxs(Text, { dimColor: true, children: [" [", elapsed.toFixed(2), "s]"] }), tool.step > 0 && _jsxs(Text, { dimColor: true, children: [" (step ", tool.step, ")"] })] }), tool.resultPreview && (_jsxs(Text, { dimColor: true, children: [" \u21B3 ", truncate(tool.resultPreview, 100)] }))] }));
18
+ }
19
+ function statusVisual(status) {
20
+ switch (status) {
21
+ case 'done':
22
+ return { icon: '✓', color: 'green' };
23
+ case 'failed':
24
+ return { icon: '✗', color: 'red' };
25
+ default:
26
+ return { icon: '⏳', color: 'yellow' };
27
+ }
28
+ }
29
+ function truncate(s, max) {
30
+ if (s.length <= max)
31
+ return s;
32
+ return s.slice(0, max - 1) + '…';
33
+ }
34
+ //# sourceMappingURL=tool-cards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-cards.js","sourceRoot":"","sources":["../../src/tui/tool-cards.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export interface InputHistoryState {
2
+ /** 当前已落盘的历史条目(已提交过的输入),最新在数组末尾 */
3
+ entries: string[];
4
+ /** 添加一条新历史(去重最后一条,空字符串不入) */
5
+ push: (entry: string) => void;
6
+ /** 当前导航到的历史索引;null 表示不在历史导航中(在编辑草稿) */
7
+ navIndex: number | null;
8
+ /** 跳到指定历史索引,并返回该条文本 */
9
+ navigate: (direction: 'up' | 'down', currentDraft: string) => string | null;
10
+ /** 退出历史导航,保留草稿 */
11
+ exitNavigation: () => void;
12
+ /** 保存进入历史导航前的草稿(用于下键到底时恢复) */
13
+ draftBeforeNav: string;
14
+ }
15
+ export declare function useInputHistory(initial?: string[]): InputHistoryState;
16
+ export interface ReverseSearchResult {
17
+ /** 匹配到的历史索引;-1 表示无匹配 */
18
+ index: number;
19
+ /** 匹配到的文本(无匹配时为空串) */
20
+ text: string;
21
+ }
22
+ /**
23
+ * 在历史中从 `fromIndex`(含)向更早的方向找第一条包含 `query` 的条目。
24
+ *
25
+ * @param entries 历史条目(末尾为最新)
26
+ * @param query 查询子串(大小写不敏感)
27
+ * @param fromIndex 从哪个索引开始往前找(含);默认从末尾开始
28
+ * @returns 匹配结果;无匹配返回 { index: -1, text: '' }
29
+ */
30
+ export declare function reverseSearch(entries: string[], query: string, fromIndex?: number): ReverseSearchResult;
31
+ /**
32
+ * 跳到当前匹配之前的下一条匹配(用于"再按 Ctrl+R")。
33
+ *
34
+ * @param entries 历史条目
35
+ * @param query 查询子串
36
+ * @param currentIndex 当前匹配的索引
37
+ * @returns 新的匹配结果;无更早匹配时返回原 currentIndex 的结果
38
+ */
39
+ export declare function reverseSearchNext(entries: string[], query: string, currentIndex: number): ReverseSearchResult;
40
+ //# sourceMappingURL=use-input-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-input-history.d.ts","sourceRoot":"","sources":["../../src/tui/use-input-history.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,6BAA6B;IAC7B,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uBAAuB;IACvB,QAAQ,EAAE,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC5E,kBAAkB;IAClB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,MAAM,EAAO,GAAG,iBAAiB,CAiEzE;AAMD,MAAM,WAAW,mBAAmB;IAClC,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EAAE,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAA2B,GACrC,mBAAmB,CASrB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EAAE,EACjB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,GACnB,mBAAmB,CAOrB"}