@zhongqian97-code/ecode 0.5.60 → 0.5.62
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.
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const _ew=process.emitWarning.bind(process);process.emitWarning=function(w,...a){if((w?.message??w)?.includes?.('punycode'))return;_ew(w,...a);};
|
|
3
|
+
|
|
4
|
+
// src/ui/altScreen.ts
|
|
5
|
+
import { writeSync as fsWriteSync } from "fs";
|
|
6
|
+
var ENTER_ALT_SCREEN_SEQ = "\x1B[?1049h\x1B[H";
|
|
7
|
+
var EXIT_ALT_SCREEN_SEQ = "\x1B[?1049l";
|
|
8
|
+
var DISABLE_TERMINAL_MODES_SEQ = "\x1B[?1004l\x1B[?1002l\x1B[?1006l";
|
|
9
|
+
function installCrashCleanup(opts = {}) {
|
|
10
|
+
const writeSync = opts.writeSync ?? ((s) => {
|
|
11
|
+
try {
|
|
12
|
+
fsWriteSync(1, s);
|
|
13
|
+
} catch {
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const signals = opts.signals ?? ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
17
|
+
let cleaned = false;
|
|
18
|
+
const cleanup = () => {
|
|
19
|
+
if (cleaned) return;
|
|
20
|
+
cleaned = true;
|
|
21
|
+
writeSync(DISABLE_TERMINAL_MODES_SEQ);
|
|
22
|
+
writeSync(EXIT_ALT_SCREEN_SEQ);
|
|
23
|
+
};
|
|
24
|
+
const onExit = () => {
|
|
25
|
+
cleanup();
|
|
26
|
+
};
|
|
27
|
+
process.on("exit", onExit);
|
|
28
|
+
const sigHandlers = signals.map((sig) => {
|
|
29
|
+
const h = () => {
|
|
30
|
+
cleanup();
|
|
31
|
+
process.exit(0);
|
|
32
|
+
};
|
|
33
|
+
process.on(sig, h);
|
|
34
|
+
return [sig, h];
|
|
35
|
+
});
|
|
36
|
+
return () => {
|
|
37
|
+
process.off("exit", onExit);
|
|
38
|
+
for (const [sig, h] of sigHandlers) process.off(sig, h);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
DISABLE_TERMINAL_MODES_SEQ,
|
|
43
|
+
ENTER_ALT_SCREEN_SEQ,
|
|
44
|
+
EXIT_ALT_SCREEN_SEQ,
|
|
45
|
+
installCrashCleanup
|
|
46
|
+
};
|
|
@@ -126,6 +126,23 @@ async function loadSkillsFromDir(dir) {
|
|
|
126
126
|
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// src/llm.ts
|
|
130
|
+
function buildAssistantMessage(assistantText, toolCalls, assistantReasoning) {
|
|
131
|
+
const msg = {
|
|
132
|
+
role: "assistant",
|
|
133
|
+
content: assistantText || (toolCalls.length > 0 ? null : ""),
|
|
134
|
+
reasoning_content: assistantReasoning ?? ""
|
|
135
|
+
};
|
|
136
|
+
if (toolCalls.length > 0) {
|
|
137
|
+
msg.tool_calls = toolCalls.map((tc) => ({
|
|
138
|
+
id: tc.id,
|
|
139
|
+
type: "function",
|
|
140
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
return msg;
|
|
144
|
+
}
|
|
145
|
+
|
|
129
146
|
// src/tools/read.ts
|
|
130
147
|
import * as fs from "fs/promises";
|
|
131
148
|
var READ_TOOL = {
|
|
@@ -1708,6 +1725,7 @@ ${prompt}` : prompt;
|
|
|
1708
1725
|
export {
|
|
1709
1726
|
isTrustedSkillPath,
|
|
1710
1727
|
loadSkillsFromDir,
|
|
1728
|
+
buildAssistantMessage,
|
|
1711
1729
|
READ_TOOL,
|
|
1712
1730
|
readFile3 as readFile,
|
|
1713
1731
|
GLOB_TOOL,
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
WEB_FETCH_TOOL,
|
|
21
21
|
WRITE_TOOL,
|
|
22
22
|
applyPatch,
|
|
23
|
+
buildAssistantMessage,
|
|
23
24
|
classifyCommand,
|
|
24
25
|
createLogger,
|
|
25
26
|
createLoggerAtPath,
|
|
@@ -33,7 +34,7 @@ import {
|
|
|
33
34
|
todo,
|
|
34
35
|
webFetch,
|
|
35
36
|
writeFile
|
|
36
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-ZLF4HLQ7.js";
|
|
37
38
|
import {
|
|
38
39
|
createProvider,
|
|
39
40
|
createSessionMetadata,
|
|
@@ -185,14 +186,7 @@ async function runPipe(prompt, llm, out = process.stdout, systemPrompt) {
|
|
|
185
186
|
}
|
|
186
187
|
if (toolCalls.length > 0) {
|
|
187
188
|
messages.push({
|
|
188
|
-
|
|
189
|
-
content: assistantText || null,
|
|
190
|
-
tool_calls: toolCalls.map((tc) => ({
|
|
191
|
-
id: tc.id,
|
|
192
|
-
type: "function",
|
|
193
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
194
|
-
})),
|
|
195
|
-
...lastReasoning ? { reasoning_content: lastReasoning } : {},
|
|
189
|
+
...buildAssistantMessage(assistantText, toolCalls, lastReasoning),
|
|
196
190
|
...lastReasoningDetails ? { reasoning_details: lastReasoningDetails } : {}
|
|
197
191
|
});
|
|
198
192
|
for (const tc of toolCalls) {
|
|
@@ -456,14 +450,7 @@ var SessionRuntime = class {
|
|
|
456
450
|
if (toolCalls.length > 0) {
|
|
457
451
|
this._status = "tool_calling";
|
|
458
452
|
this.messages.push({
|
|
459
|
-
|
|
460
|
-
content: assistantText || null,
|
|
461
|
-
tool_calls: toolCalls.map((tc) => ({
|
|
462
|
-
id: tc.id,
|
|
463
|
-
type: "function",
|
|
464
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
465
|
-
})),
|
|
466
|
-
...assistantReasoning ? { reasoning_content: assistantReasoning } : {},
|
|
453
|
+
...buildAssistantMessage(assistantText, toolCalls, assistantReasoning),
|
|
467
454
|
...lastReasoningDetails ? { reasoning_details: lastReasoningDetails } : {}
|
|
468
455
|
});
|
|
469
456
|
(_a = this.logger) == null ? void 0 : _a.append({
|
|
@@ -486,11 +473,7 @@ var SessionRuntime = class {
|
|
|
486
473
|
}
|
|
487
474
|
} else {
|
|
488
475
|
if (assistantText) {
|
|
489
|
-
this.messages.push(
|
|
490
|
-
role: "assistant",
|
|
491
|
-
content: assistantText,
|
|
492
|
-
...assistantReasoning ? { reasoning_content: assistantReasoning } : {}
|
|
493
|
-
});
|
|
476
|
+
this.messages.push(buildAssistantMessage(assistantText, [], assistantReasoning));
|
|
494
477
|
(_c = this.logger) == null ? void 0 : _c.append({ ts: (/* @__PURE__ */ new Date()).toISOString(), role: "assistant", content: assistantText || null });
|
|
495
478
|
}
|
|
496
479
|
break;
|
|
@@ -903,21 +886,9 @@ Press Ctrl+C to stop.
|
|
|
903
886
|
process.exit(0);
|
|
904
887
|
}
|
|
905
888
|
if (process.stdout.isTTY) {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
process.on("SIGINT", () => {
|
|
910
|
-
exitAltScreen();
|
|
911
|
-
process.exit(0);
|
|
912
|
-
});
|
|
913
|
-
process.on("SIGTERM", () => {
|
|
914
|
-
exitAltScreen();
|
|
915
|
-
process.exit(0);
|
|
916
|
-
});
|
|
917
|
-
process.on("SIGHUP", () => {
|
|
918
|
-
exitAltScreen();
|
|
919
|
-
process.exit(0);
|
|
920
|
-
});
|
|
889
|
+
const { ENTER_ALT_SCREEN_SEQ, installCrashCleanup } = await import("./altScreen-I6W4LBRE.js");
|
|
890
|
+
process.stdout.write(ENTER_ALT_SCREEN_SEQ);
|
|
891
|
+
installCrashCleanup();
|
|
921
892
|
}
|
|
922
893
|
const [nodeMaj] = process.versions.node.split(".").map(Number);
|
|
923
894
|
if (nodeMaj < 20) {
|
|
@@ -928,7 +899,7 @@ Node.js 16/18 \u8BF7\u4F7F\u7528 --web \u6216 --pipe \u6A21\u5F0F\u3002
|
|
|
928
899
|
);
|
|
929
900
|
process.exit(1);
|
|
930
901
|
}
|
|
931
|
-
const { App, React, render, createStdinFilter } = await import("./ui-
|
|
902
|
+
const { App, React, render, createStdinFilter } = await import("./ui-445CQEQR.js");
|
|
932
903
|
const stdinFilter = process.stdin.isTTY ? createStdinFilter(process.stdin) : process.stdin;
|
|
933
904
|
render(
|
|
934
905
|
React.createElement(App, { config: finalConfig, version: VERSION, autoMode, registry, trustedSkillDirs, initialMessages, stdinFilter }),
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
WEB_FETCH_TOOL,
|
|
13
13
|
WRITE_TOOL,
|
|
14
14
|
applyPatch,
|
|
15
|
+
buildAssistantMessage,
|
|
15
16
|
createLogger,
|
|
16
17
|
editFile,
|
|
17
18
|
executeBash,
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
todo,
|
|
25
26
|
webFetch,
|
|
26
27
|
writeFile
|
|
27
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-ZLF4HLQ7.js";
|
|
28
29
|
import {
|
|
29
30
|
handleSkillInput,
|
|
30
31
|
loadJobs,
|
|
@@ -47,7 +48,7 @@ import { render } from "ink";
|
|
|
47
48
|
|
|
48
49
|
// src/ui/App.tsx
|
|
49
50
|
import { useState as useState3, useCallback, useRef as useRef2, useEffect as useEffect3, useMemo } from "react";
|
|
50
|
-
import { Box as Box6, Text as Text6, useInput as useInput2, useStdout } from "ink";
|
|
51
|
+
import { Box as Box6, Text as Text6, useInput as useInput2, useStdout, measureElement } from "ink";
|
|
51
52
|
|
|
52
53
|
// src/skills/executor.ts
|
|
53
54
|
import { exec } from "child_process";
|
|
@@ -296,6 +297,7 @@ function messageToLines(msg, expandTools, terminalWidth) {
|
|
|
296
297
|
}
|
|
297
298
|
|
|
298
299
|
// src/ui/mouseSelection.ts
|
|
300
|
+
import stringWidth2 from "string-width";
|
|
299
301
|
function initialSelectionState() {
|
|
300
302
|
return { anchor: null, focus: null, dragging: false };
|
|
301
303
|
}
|
|
@@ -328,6 +330,21 @@ function isCellSelected(row, col, state) {
|
|
|
328
330
|
if (row === end.row) return col <= end.col;
|
|
329
331
|
return true;
|
|
330
332
|
}
|
|
333
|
+
function colToStringIndex(line, col, roundUp) {
|
|
334
|
+
if (col <= 0) return 0;
|
|
335
|
+
let w = 0;
|
|
336
|
+
let idx = 0;
|
|
337
|
+
for (const ch of line) {
|
|
338
|
+
const cw = stringWidth2(ch);
|
|
339
|
+
if (w >= col) return idx;
|
|
340
|
+
if (w + cw > col) {
|
|
341
|
+
return roundUp ? idx + ch.length : idx;
|
|
342
|
+
}
|
|
343
|
+
w += cw;
|
|
344
|
+
idx += ch.length;
|
|
345
|
+
}
|
|
346
|
+
return idx;
|
|
347
|
+
}
|
|
331
348
|
function extractSelectedText(lines, state) {
|
|
332
349
|
const norm = normalizeSelection(state);
|
|
333
350
|
if (!norm) return "";
|
|
@@ -335,9 +352,11 @@ function extractSelectedText(lines, state) {
|
|
|
335
352
|
const parts = [];
|
|
336
353
|
for (let r = start.row; r <= end.row; r++) {
|
|
337
354
|
const line = lines[r] ?? "";
|
|
338
|
-
const
|
|
339
|
-
const
|
|
340
|
-
|
|
355
|
+
const colStartVis = r === start.row ? start.col : 0;
|
|
356
|
+
const colEndVis = r === end.row ? end.col + 1 : Number.POSITIVE_INFINITY;
|
|
357
|
+
const idxStart = colToStringIndex(line, colStartVis, false);
|
|
358
|
+
const idxEnd = colToStringIndex(line, colEndVis, true);
|
|
359
|
+
parts.push(line.slice(idxStart, idxEnd));
|
|
341
360
|
}
|
|
342
361
|
return parts.join("\n");
|
|
343
362
|
}
|
|
@@ -405,7 +424,20 @@ import { useState as useState2, useEffect as useEffect2, useRef, forwardRef, use
|
|
|
405
424
|
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
406
425
|
|
|
407
426
|
// src/ui/inputClick.ts
|
|
427
|
+
import stringWidth3 from "string-width";
|
|
408
428
|
var INPUT_PREFIX_WIDTH = 2;
|
|
429
|
+
function colToStringIndex2(line, col) {
|
|
430
|
+
if (col <= 0) return 0;
|
|
431
|
+
let w = 0;
|
|
432
|
+
let idx = 0;
|
|
433
|
+
for (const ch of line) {
|
|
434
|
+
const cw = stringWidth3(ch);
|
|
435
|
+
if (w + cw > col) break;
|
|
436
|
+
w += cw;
|
|
437
|
+
idx += ch.length;
|
|
438
|
+
}
|
|
439
|
+
return idx;
|
|
440
|
+
}
|
|
409
441
|
function clickToCursor(value, localRow, localCol) {
|
|
410
442
|
if (value.length === 0) return 0;
|
|
411
443
|
const lines = value.split("\n");
|
|
@@ -416,7 +448,7 @@ function clickToCursor(value, localRow, localCol) {
|
|
|
416
448
|
pos += lines[i].length + 1;
|
|
417
449
|
}
|
|
418
450
|
const lineCol = Math.max(0, localCol - INPUT_PREFIX_WIDTH);
|
|
419
|
-
pos +=
|
|
451
|
+
pos += colToStringIndex2(lines[localRow], lineCol);
|
|
420
452
|
return pos;
|
|
421
453
|
}
|
|
422
454
|
|
|
@@ -962,7 +994,7 @@ function computeVisibleWindow(totalLines, scrollOffset, historyMaxHeight) {
|
|
|
962
994
|
}
|
|
963
995
|
function mouseRowToLineIndex(opts) {
|
|
964
996
|
if (opts.visibleCount <= 0) return null;
|
|
965
|
-
const bottomRows = STATUS_BAR_ROWS + opts.skillAcRows + opts.fileAcRows + opts.selectedItemsRows + opts.inputRows;
|
|
997
|
+
const bottomRows = opts.bottomChromeRows ?? STATUS_BAR_ROWS + (opts.skillAcRows ?? 0) + (opts.fileAcRows ?? 0) + (opts.selectedItemsRows ?? 0) + (opts.inputRows ?? 1);
|
|
966
998
|
const innerBoxHeight = opts.terminalRows - bottomRows;
|
|
967
999
|
const chTopRow = innerBoxHeight - opts.visibleCount;
|
|
968
1000
|
const chBottomRow = innerBoxHeight - 1;
|
|
@@ -2542,6 +2574,7 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
2542
2574
|
const inputHistoryRef = useRef2([]);
|
|
2543
2575
|
inputHistoryRef.current = inputHistory;
|
|
2544
2576
|
const historyIndexRef = useRef2(-1);
|
|
2577
|
+
const historyDraftRef = useRef2("");
|
|
2545
2578
|
const isNavigatingHistoryRef = useRef2(false);
|
|
2546
2579
|
const totalLines = useMemo(() => {
|
|
2547
2580
|
const visible = messages.filter((m) => m.role !== "system");
|
|
@@ -2558,10 +2591,8 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
2558
2591
|
historyMaxHeightRef.current = historyMaxHeight;
|
|
2559
2592
|
const selectionStateRef = useRef2(selectionState);
|
|
2560
2593
|
selectionStateRef.current = selectionState;
|
|
2594
|
+
const bottomChromeRef = useRef2(null);
|
|
2561
2595
|
const inputRowsRef = useRef2(1);
|
|
2562
|
-
const skillAcRowsRef = useRef2(0);
|
|
2563
|
-
const fileAcRowsRef = useRef2(0);
|
|
2564
|
-
const selectedItemsRowsRef = useRef2(0);
|
|
2565
2596
|
const terminalRowsRef = useRef2((stdout == null ? void 0 : stdout.rows) ?? 24);
|
|
2566
2597
|
terminalRowsRef.current = (stdout == null ? void 0 : stdout.rows) ?? 24;
|
|
2567
2598
|
const pendingConfirmRef = useRef2(null);
|
|
@@ -2626,13 +2657,24 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
2626
2657
|
automationManagerRef.current.stop();
|
|
2627
2658
|
};
|
|
2628
2659
|
}, []);
|
|
2660
|
+
const enableModesSeq = "\x1B[?1002h\x1B[?1006h\x1B[?1004h";
|
|
2629
2661
|
useEffect3(() => {
|
|
2630
2662
|
if (!stdout) return;
|
|
2631
|
-
stdout.write(
|
|
2663
|
+
stdout.write(enableModesSeq);
|
|
2632
2664
|
return () => {
|
|
2633
|
-
stdout.write("\x1B[?1002l\x1B[?1006l");
|
|
2665
|
+
stdout.write("\x1B[?1004l\x1B[?1002l\x1B[?1006l");
|
|
2634
2666
|
};
|
|
2635
2667
|
}, [stdout]);
|
|
2668
|
+
useEffect3(() => {
|
|
2669
|
+
if (!stdinFilter || !stdout) return;
|
|
2670
|
+
const onResumed = () => {
|
|
2671
|
+
stdout.write(enableModesSeq);
|
|
2672
|
+
};
|
|
2673
|
+
stdinFilter.on("resumed", onResumed);
|
|
2674
|
+
return () => {
|
|
2675
|
+
stdinFilter.off("resumed", onResumed);
|
|
2676
|
+
};
|
|
2677
|
+
}, [stdinFilter, stdout]);
|
|
2636
2678
|
useEffect3(() => {
|
|
2637
2679
|
if (!stdinFilter) return;
|
|
2638
2680
|
const onMouse = (e) => {
|
|
@@ -2672,13 +2714,16 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
2672
2714
|
scrollOffsetRef.current,
|
|
2673
2715
|
historyMaxHeightRef.current
|
|
2674
2716
|
);
|
|
2717
|
+
let bottomRows;
|
|
2718
|
+
if (bottomChromeRef.current) {
|
|
2719
|
+
const m = measureElement(bottomChromeRef.current);
|
|
2720
|
+
if (m && m.height > 0) bottomRows = m.height;
|
|
2721
|
+
}
|
|
2675
2722
|
const lineIdx = mouseRowToLineIndex({
|
|
2676
2723
|
mouseRow: e.row,
|
|
2677
2724
|
terminalRows: tRows,
|
|
2725
|
+
bottomChromeRows: bottomRows,
|
|
2678
2726
|
inputRows: inRows,
|
|
2679
|
-
skillAcRows: skillAcRowsRef.current,
|
|
2680
|
-
fileAcRows: fileAcRowsRef.current,
|
|
2681
|
-
selectedItemsRows: selectedItemsRowsRef.current,
|
|
2682
2727
|
visibleCount: window.visibleCount,
|
|
2683
2728
|
hasScrollIndicator: window.hasScrollIndicator
|
|
2684
2729
|
});
|
|
@@ -2721,7 +2766,7 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
2721
2766
|
};
|
|
2722
2767
|
}, [fileAcState.query]);
|
|
2723
2768
|
useInput2((input, key) => {
|
|
2724
|
-
var _a, _b, _c
|
|
2769
|
+
var _a, _b, _c;
|
|
2725
2770
|
if (key.escape && status === "thinking" && abortControllerRef.current) {
|
|
2726
2771
|
abortControllerRef.current.abort();
|
|
2727
2772
|
return;
|
|
@@ -2796,52 +2841,47 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
2796
2841
|
return;
|
|
2797
2842
|
}
|
|
2798
2843
|
const currentInput = ((_c = inputRef.current) == null ? void 0 : _c.getValue()) ?? "";
|
|
2799
|
-
|
|
2844
|
+
const inHistory = historyIndexRef.current >= 0;
|
|
2845
|
+
const canEnterHistory = currentInput === "" || inHistory;
|
|
2846
|
+
const navUp = () => {
|
|
2847
|
+
var _a2;
|
|
2800
2848
|
const history = inputHistoryRef.current;
|
|
2801
2849
|
const newIndex = Math.min(historyIndexRef.current + 1, history.length - 1);
|
|
2802
|
-
if (newIndex
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2850
|
+
if (newIndex < 0 || newIndex >= history.length) return;
|
|
2851
|
+
if (historyIndexRef.current === -1) historyDraftRef.current = currentInput;
|
|
2852
|
+
historyIndexRef.current = newIndex;
|
|
2853
|
+
isNavigatingHistoryRef.current = true;
|
|
2854
|
+
(_a2 = inputRef.current) == null ? void 0 : _a2.fill(history[newIndex]);
|
|
2855
|
+
};
|
|
2856
|
+
const navDown = () => {
|
|
2857
|
+
var _a2, _b2;
|
|
2810
2858
|
const history = inputHistoryRef.current;
|
|
2811
2859
|
if (historyIndexRef.current > 0) {
|
|
2812
2860
|
const newIndex = historyIndexRef.current - 1;
|
|
2813
2861
|
historyIndexRef.current = newIndex;
|
|
2814
2862
|
isNavigatingHistoryRef.current = true;
|
|
2815
|
-
(
|
|
2863
|
+
(_a2 = inputRef.current) == null ? void 0 : _a2.fill(history[newIndex]);
|
|
2816
2864
|
} else if (historyIndexRef.current === 0) {
|
|
2817
2865
|
historyIndexRef.current = -1;
|
|
2818
2866
|
isNavigatingHistoryRef.current = true;
|
|
2819
|
-
(
|
|
2867
|
+
(_b2 = inputRef.current) == null ? void 0 : _b2.fill(historyDraftRef.current);
|
|
2868
|
+
historyDraftRef.current = "";
|
|
2820
2869
|
}
|
|
2870
|
+
};
|
|
2871
|
+
if (canEnterHistory && key.upArrow) {
|
|
2872
|
+
navUp();
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2875
|
+
if (canEnterHistory && key.downArrow) {
|
|
2876
|
+
navDown();
|
|
2821
2877
|
return;
|
|
2822
2878
|
}
|
|
2823
2879
|
if (key.ctrl && input === "p") {
|
|
2824
|
-
|
|
2825
|
-
const newIndex = Math.min(historyIndexRef.current + 1, history.length - 1);
|
|
2826
|
-
if (newIndex >= 0 && newIndex < history.length) {
|
|
2827
|
-
historyIndexRef.current = newIndex;
|
|
2828
|
-
isNavigatingHistoryRef.current = true;
|
|
2829
|
-
(_g = inputRef.current) == null ? void 0 : _g.fill(history[newIndex]);
|
|
2830
|
-
}
|
|
2880
|
+
navUp();
|
|
2831
2881
|
return;
|
|
2832
2882
|
}
|
|
2833
2883
|
if (key.ctrl && input === "n") {
|
|
2834
|
-
|
|
2835
|
-
if (historyIndexRef.current > 0) {
|
|
2836
|
-
const newIndex = historyIndexRef.current - 1;
|
|
2837
|
-
historyIndexRef.current = newIndex;
|
|
2838
|
-
isNavigatingHistoryRef.current = true;
|
|
2839
|
-
(_h = inputRef.current) == null ? void 0 : _h.fill(history[newIndex]);
|
|
2840
|
-
} else if (historyIndexRef.current === 0) {
|
|
2841
|
-
historyIndexRef.current = -1;
|
|
2842
|
-
isNavigatingHistoryRef.current = true;
|
|
2843
|
-
(_i = inputRef.current) == null ? void 0 : _i.fill("");
|
|
2844
|
-
}
|
|
2884
|
+
navDown();
|
|
2845
2885
|
return;
|
|
2846
2886
|
}
|
|
2847
2887
|
});
|
|
@@ -2930,18 +2970,11 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
2930
2970
|
throw err;
|
|
2931
2971
|
}
|
|
2932
2972
|
if (toolCalls.length > 0) {
|
|
2933
|
-
const assistantMsg =
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
id: tc.id,
|
|
2939
|
-
type: "function",
|
|
2940
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
2941
|
-
})),
|
|
2942
|
-
// 仅在存在思考内容时扩展 reasoning_content 字段
|
|
2943
|
-
...assistantReasoning ? { reasoning_content: assistantReasoning } : {}
|
|
2944
|
-
};
|
|
2973
|
+
const assistantMsg = buildAssistantMessage(
|
|
2974
|
+
assistantText,
|
|
2975
|
+
toolCalls,
|
|
2976
|
+
assistantReasoning
|
|
2977
|
+
);
|
|
2945
2978
|
setMessages((prev) => {
|
|
2946
2979
|
const last = prev[prev.length - 1];
|
|
2947
2980
|
const withoutStreaming = (last == null ? void 0 : last.role) === "assistant" && !last.tool_calls ? prev.slice(0, -1) : prev;
|
|
@@ -3017,11 +3050,7 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
3017
3050
|
setMessages((prev) => {
|
|
3018
3051
|
const last = prev[prev.length - 1];
|
|
3019
3052
|
if ((last == null ? void 0 : last.role) === "assistant" && !last.tool_calls) {
|
|
3020
|
-
const updated =
|
|
3021
|
-
role: "assistant",
|
|
3022
|
-
content: assistantText,
|
|
3023
|
-
...assistantReasoning ? { reasoning_content: assistantReasoning } : {}
|
|
3024
|
-
};
|
|
3053
|
+
const updated = buildAssistantMessage(assistantText, [], assistantReasoning);
|
|
3025
3054
|
return [...prev.slice(0, -1), updated];
|
|
3026
3055
|
}
|
|
3027
3056
|
return prev;
|
|
@@ -3029,11 +3058,7 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
3029
3058
|
if (assistantText) {
|
|
3030
3059
|
currentMessages = [
|
|
3031
3060
|
...currentMessages,
|
|
3032
|
-
|
|
3033
|
-
role: "assistant",
|
|
3034
|
-
content: assistantText,
|
|
3035
|
-
...assistantReasoning ? { reasoning_content: assistantReasoning } : {}
|
|
3036
|
-
}
|
|
3061
|
+
buildAssistantMessage(assistantText, [], assistantReasoning)
|
|
3037
3062
|
];
|
|
3038
3063
|
}
|
|
3039
3064
|
}
|
|
@@ -3186,9 +3211,6 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
3186
3211
|
const skillSuggestions = registry ? computeSuggestions(registry.list(), acState) : [];
|
|
3187
3212
|
const acOpen = isOpen(acState, skillSuggestions);
|
|
3188
3213
|
const facOpen = isOpen2(fileAcState, fileSuggestions);
|
|
3189
|
-
skillAcRowsRef.current = acOpen ? skillSuggestions.length + 2 : 0;
|
|
3190
|
-
fileAcRowsRef.current = facOpen ? fileSuggestions.length + 2 : 0;
|
|
3191
|
-
selectedItemsRowsRef.current = selectedItems.length > 0 ? 1 : 0;
|
|
3192
3214
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", height: "100%", children: [
|
|
3193
3215
|
/* @__PURE__ */ jsx6(Box6, { flexGrow: 1, flexDirection: "column", justifyContent: "flex-end", children: /* @__PURE__ */ jsx6(
|
|
3194
3216
|
ConversationHistory,
|
|
@@ -3201,46 +3223,48 @@ function App({ config, version, autoMode = false, registry, trustedSkillDirs = [
|
|
|
3201
3223
|
selection: selectionState
|
|
3202
3224
|
}
|
|
3203
3225
|
) }),
|
|
3204
|
-
/* @__PURE__ */
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3226
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", ref: bottomChromeRef, children: [
|
|
3227
|
+
/* @__PURE__ */ jsx6(
|
|
3228
|
+
StatusBar,
|
|
3229
|
+
{
|
|
3230
|
+
status,
|
|
3231
|
+
toolName,
|
|
3232
|
+
confirmPrompt,
|
|
3233
|
+
version,
|
|
3234
|
+
tokenUsage
|
|
3235
|
+
}
|
|
3236
|
+
),
|
|
3237
|
+
/* @__PURE__ */ jsx6(
|
|
3238
|
+
SkillAutocomplete,
|
|
3239
|
+
{
|
|
3240
|
+
suggestions: skillSuggestions,
|
|
3241
|
+
selectedIndex: acState.selectedIndex,
|
|
3242
|
+
isOpen: acOpen
|
|
3243
|
+
}
|
|
3244
|
+
),
|
|
3245
|
+
/* @__PURE__ */ jsx6(
|
|
3246
|
+
FileAutocomplete,
|
|
3247
|
+
{
|
|
3248
|
+
suggestions: fileSuggestions,
|
|
3249
|
+
selectedIndex: fileAcState.selectedIndex,
|
|
3250
|
+
isOpen: facOpen
|
|
3251
|
+
}
|
|
3252
|
+
),
|
|
3253
|
+
selectedItems.length > 0 && /* @__PURE__ */ jsx6(Box6, { paddingLeft: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: "cyan", dimColor: true, children: [
|
|
3254
|
+
"Selected: ",
|
|
3255
|
+
selectedItems.map((i) => i.label).join(", ")
|
|
3256
|
+
] }) }),
|
|
3257
|
+
/* @__PURE__ */ jsx6(
|
|
3258
|
+
Input_default,
|
|
3259
|
+
{
|
|
3260
|
+
ref: inputRef,
|
|
3261
|
+
isActive: isInputActive,
|
|
3262
|
+
onSubmit: handleSubmit,
|
|
3263
|
+
onChange: handleInputTextChange,
|
|
3264
|
+
placeholder: status === "awaiting_confirm" ? "y / n" : void 0
|
|
3265
|
+
}
|
|
3266
|
+
)
|
|
3267
|
+
] })
|
|
3244
3268
|
] });
|
|
3245
3269
|
}
|
|
3246
3270
|
|
|
@@ -3306,6 +3330,7 @@ function tokenizeStdin(input, pendingIn) {
|
|
|
3306
3330
|
let i = 0;
|
|
3307
3331
|
let keyboardOut = "";
|
|
3308
3332
|
const events = [];
|
|
3333
|
+
const focusEvents = [];
|
|
3309
3334
|
while (i < data.length) {
|
|
3310
3335
|
const ch = data[i];
|
|
3311
3336
|
if (ch !== "\x1B") {
|
|
@@ -3327,13 +3352,18 @@ function tokenizeStdin(input, pendingIn) {
|
|
|
3327
3352
|
i += mLen;
|
|
3328
3353
|
continue;
|
|
3329
3354
|
}
|
|
3355
|
+
if (tail.length >= 3 && tail[1] === "[" && (tail[2] === "I" || tail[2] === "O")) {
|
|
3356
|
+
focusEvents.push({ kind: "focus", focused: tail[2] === "I" });
|
|
3357
|
+
i += 3;
|
|
3358
|
+
continue;
|
|
3359
|
+
}
|
|
3330
3360
|
if (isMousePartial(tail) && tail.length < MAX_MOUSE_SEQ) {
|
|
3331
|
-
return { keyboard: keyboardOut, pending: tail, events };
|
|
3361
|
+
return { keyboard: keyboardOut, pending: tail, events, focusEvents };
|
|
3332
3362
|
}
|
|
3333
3363
|
keyboardOut += ch;
|
|
3334
3364
|
i++;
|
|
3335
3365
|
}
|
|
3336
|
-
return { keyboard: keyboardOut, pending: "", events };
|
|
3366
|
+
return { keyboard: keyboardOut, pending: "", events, focusEvents };
|
|
3337
3367
|
}
|
|
3338
3368
|
|
|
3339
3369
|
// src/ui/stdinFilter.ts
|
|
@@ -3341,12 +3371,22 @@ var StdinFilterImpl = class extends Transform {
|
|
|
3341
3371
|
isTTY;
|
|
3342
3372
|
source;
|
|
3343
3373
|
pending = "";
|
|
3344
|
-
|
|
3374
|
+
// 焦点状态——cc-haha 把 focus 作 signal 而非 mode switch(§5.4)。
|
|
3375
|
+
// CSI I/O 切换;任何字节到达时若为 false 由 failsafe 强制回 true。
|
|
3376
|
+
focused = true;
|
|
3377
|
+
// resume 检测:最近一次活动时间戳;间隔 > resumeIdleMs 算 SSH 重连 / tmux detach 回来。
|
|
3378
|
+
lastByteAt = 0;
|
|
3379
|
+
resumeIdleMs;
|
|
3380
|
+
constructor(source, opts = {}) {
|
|
3345
3381
|
super();
|
|
3346
3382
|
this.source = source;
|
|
3347
3383
|
this.isTTY = source.isTTY ?? false;
|
|
3384
|
+
this.resumeIdleMs = opts.resumeIdleMs ?? 5e3;
|
|
3348
3385
|
source.pipe(this);
|
|
3349
3386
|
}
|
|
3387
|
+
isFocused() {
|
|
3388
|
+
return this.focused;
|
|
3389
|
+
}
|
|
3350
3390
|
setRawMode(mode) {
|
|
3351
3391
|
var _a, _b;
|
|
3352
3392
|
(_b = (_a = this.source).setRawMode) == null ? void 0 : _b.call(_a, mode);
|
|
@@ -3361,9 +3401,23 @@ var StdinFilterImpl = class extends Transform {
|
|
|
3361
3401
|
}
|
|
3362
3402
|
_transform(chunk, _encoding, callback) {
|
|
3363
3403
|
const s = typeof chunk === "string" ? chunk : chunk.toString("binary");
|
|
3364
|
-
const
|
|
3404
|
+
const now = Date.now();
|
|
3405
|
+
if (this.lastByteAt > 0 && now - this.lastByteAt >= this.resumeIdleMs) {
|
|
3406
|
+
this.emit("resumed");
|
|
3407
|
+
}
|
|
3408
|
+
this.lastByteAt = now;
|
|
3409
|
+
const { keyboard, pending, events, focusEvents } = tokenizeStdin(s, this.pending);
|
|
3365
3410
|
this.pending = pending;
|
|
3411
|
+
for (const ev of focusEvents) {
|
|
3412
|
+
this.focused = ev.focused;
|
|
3413
|
+
this.emit("focus", ev);
|
|
3414
|
+
}
|
|
3366
3415
|
for (const ev of events) this.emit("mouse", ev);
|
|
3416
|
+
const hadNonFocusActivity = events.length > 0 || keyboard.length > 0;
|
|
3417
|
+
if (hadNonFocusActivity && !this.focused) {
|
|
3418
|
+
this.focused = true;
|
|
3419
|
+
this.emit("focus", { kind: "focus", focused: true });
|
|
3420
|
+
}
|
|
3367
3421
|
if (keyboard.length > 0) {
|
|
3368
3422
|
callback(null, Buffer.from(keyboard, "binary"));
|
|
3369
3423
|
} else {
|
|
@@ -3371,8 +3425,8 @@ var StdinFilterImpl = class extends Transform {
|
|
|
3371
3425
|
}
|
|
3372
3426
|
}
|
|
3373
3427
|
};
|
|
3374
|
-
function createStdinFilter(source) {
|
|
3375
|
-
return new StdinFilterImpl(source);
|
|
3428
|
+
function createStdinFilter(source, opts) {
|
|
3429
|
+
return new StdinFilterImpl(source, opts);
|
|
3376
3430
|
}
|
|
3377
3431
|
export {
|
|
3378
3432
|
App,
|