@falkenslab/agent-kit 0.10.2 → 0.12.0
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/README.md +91 -97
- package/dist/core/agentSpec.d.ts +31 -1
- package/dist/core/hooks/stepGate.js +3 -2
- package/dist/core/language.d.ts +39 -0
- package/dist/core/language.js +59 -0
- package/dist/core/messages/de.d.ts +2 -0
- package/dist/core/messages/de.js +103 -0
- package/dist/core/messages/en.d.ts +90 -0
- package/dist/core/messages/en.js +102 -0
- package/dist/core/messages/es.d.ts +2 -0
- package/dist/core/messages/es.js +103 -0
- package/dist/core/messages/fr.d.ts +2 -0
- package/dist/core/messages/fr.js +103 -0
- package/dist/core/messages/index.d.ts +23 -0
- package/dist/core/messages/index.js +67 -0
- package/dist/core/runs.d.ts +51 -0
- package/dist/core/runs.js +194 -0
- package/dist/core/session.d.ts +8 -0
- package/dist/core/session.js +42 -7
- package/dist/core/toolLabels.d.ts +2 -1
- package/dist/core/toolLabels.js +31 -51
- package/dist/core/tools/humanApproval.js +3 -2
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/tui/chatTui.d.ts +23 -2
- package/dist/tui/chatTui.js +71 -7
- package/dist/tui/claudeAuth.d.ts +3 -1
- package/dist/tui/claudeAuth.js +12 -17
- package/dist/tui/consoleRenderer.js +2 -1
- package/dist/tui/ink/PromptInput.d.ts +0 -2
- package/dist/tui/ink/PromptInput.js +5 -15
- package/dist/tui/ink/SessionView.js +16 -15
- package/dist/tui/ink/inkInteraction.js +4 -7
- package/dist/tui/ink/progressView.d.ts +6 -0
- package/dist/tui/ink/progressView.js +2 -0
- package/dist/tui/ink/promptEditing.d.ts +2 -5
- package/dist/tui/ink/promptEditing.js +11 -3
- package/dist/tui/ink/runChatInk.d.ts +25 -2
- package/dist/tui/ink/runChatInk.js +156 -48
- package/dist/tui/ink/sessionModel.d.ts +10 -0
- package/dist/tui/ink/sessionModel.js +27 -2
- package/dist/tui/ink/toolGroup.d.ts +2 -1
- package/dist/tui/ink/toolGroup.js +5 -18
- package/dist/tui/ink/wizard.d.ts +6 -0
- package/dist/tui/ink/wizard.js +4 -1
- package/dist/tui/language.d.ts +5 -0
- package/dist/tui/language.js +11 -0
- package/dist/tui/runs.d.ts +27 -0
- package/dist/tui/runs.js +30 -0
- package/dist/tui/terminalInteraction.js +2 -2
- package/package.json +1 -1
|
@@ -8,8 +8,9 @@ import { isMouseReport } from "./fullscreen.js";
|
|
|
8
8
|
import { CursorContext } from "./terminalCursor.js";
|
|
9
9
|
import { isFocusReport } from "./terminalStatus.js";
|
|
10
10
|
import { matchingFiles, mentionAt } from "./fileMentions.js";
|
|
11
|
-
import { continueLine, createPasteRegistry, cursorLine, deleteBack, deleteForward, deleteWordBack, insert, isBlockPaste, isForwardDelete, killToLineEnd, moveLine, normalizePaste, searchHistory, toLineEnd, toLineStart, wordLeft, wordRight, } from "./promptEditing.js";
|
|
11
|
+
import { continueLine, createPasteRegistry, cursorLine, deleteBack, deleteForward, deleteWordBack, insert, isBlockPaste, isForwardDelete, killToLineEnd, moveLine, normalizePaste, PASTE_TOKEN_AFTER, PASTE_TOKEN_BEFORE, searchHistory, toLineEnd, toLineStart, wordLeft, wordRight, } from "./promptEditing.js";
|
|
12
12
|
import * as ui from "../ui.js";
|
|
13
|
+
import { t } from "../../core/messages/index.js";
|
|
13
14
|
const MAX_SUGGESTIONS = 6;
|
|
14
15
|
/** Commands (names without "/") a partial "/name" line could still become. */
|
|
15
16
|
export function matchingCommands(value, commands) {
|
|
@@ -44,19 +45,8 @@ export function visibleWindow(length, cursor, room) {
|
|
|
44
45
|
const start = Math.min(Math.max(0, cursor + 1 - span), Math.max(0, length + 1 - span));
|
|
45
46
|
return [start, Math.min(length, start + span)];
|
|
46
47
|
}
|
|
47
|
-
/** The shortcuts panel, opened with "?" on an empty prompt. */
|
|
48
|
-
export const SHORTCUTS = [
|
|
49
|
-
"Enter send · \\ + Enter or Ctrl+J new line · Tab complete or take the suggestion",
|
|
50
|
-
"↑/↓ history · Ctrl+R search history · ↑ on an empty prompt edits the last queued message",
|
|
51
|
-
"Ctrl+W delete word · Ctrl+K to line end · Ctrl+U clear · Ctrl+←/→ move by word",
|
|
52
|
-
"@ mention a file · /copy copy the last reply · Shift+Tab switch mode · Ctrl+O unfold tool calls",
|
|
53
|
-
"Esc interrupt · Ctrl+C interrupt or exit · PgUp/PgDn or wheel scroll · Ctrl+End bottom",
|
|
54
|
-
"Drag to select, right-click to copy (full screen)",
|
|
55
|
-
];
|
|
56
48
|
// A paste can reach us in several chunks; pieces this close together extend the same one.
|
|
57
49
|
const PASTE_CHUNK_MS = 100;
|
|
58
|
-
const PASTE_TOKEN_BEFORE = /\[Pasted text #\d+ \+\d+ lines?\]$/;
|
|
59
|
-
const PASTE_TOKEN_AFTER = /^\[Pasted text #\d+ \+\d+ lines?\]/;
|
|
60
50
|
/**
|
|
61
51
|
* The chat's prompt: history (↑/↓), "/command" completion and suggestions (Tab), pasted
|
|
62
52
|
* blocks folded into a token, multi-line input (`\` + Enter or Ctrl+J), word and line
|
|
@@ -310,7 +300,7 @@ export function PromptInput({ label, inset = 0, suggestion, history, commands, o
|
|
|
310
300
|
const width = liveWidth(stdout.columns) - inset;
|
|
311
301
|
const labelWidth = stringWidth(stripAnsi(label));
|
|
312
302
|
const room = width - labelWidth;
|
|
313
|
-
const ghost = suggestion ? `${suggestion} (
|
|
303
|
+
const ghost = suggestion ? `${suggestion} (${t().suggestionKey})` : null;
|
|
314
304
|
// Suggestions give way on a short terminal: the live area must stay shorter than it.
|
|
315
305
|
const maxSuggestions = Math.min(MAX_SUGGESTIONS, Math.max(0, (stdout.rows || 24) - 10));
|
|
316
306
|
const mention = mentionAt(value, cursor);
|
|
@@ -326,7 +316,7 @@ export function PromptInput({ label, inset = 0, suggestion, history, commands, o
|
|
|
326
316
|
let rows;
|
|
327
317
|
let cursorColumn;
|
|
328
318
|
if (search) {
|
|
329
|
-
const prefix =
|
|
319
|
+
const prefix = t().reverseSearch(search.query);
|
|
330
320
|
const match = search.index >= 0 ? history[search.index] : "";
|
|
331
321
|
cursorColumn = Math.min(stringWidth(prefix) - 3, width - 1);
|
|
332
322
|
rows = [
|
|
@@ -352,5 +342,5 @@ export function PromptInput({ label, inset = 0, suggestion, history, commands, o
|
|
|
352
342
|
cursorController.setTarget({ node: cursorLineRef.current, column: cursorColumn });
|
|
353
343
|
});
|
|
354
344
|
useLayoutEffect(() => () => cursorController?.setTarget(null), [cursorController]);
|
|
355
|
-
return (_jsxs(Box, { flexDirection: "column", children: [rows, suggestions.map((item) => (_jsx(Text, { children: ui.dim(fitWidth(` ${item}`, width)) }, item))), showHelp ?
|
|
345
|
+
return (_jsxs(Box, { flexDirection: "column", children: [rows, suggestions.map((item) => (_jsx(Text, { children: ui.dim(fitWidth(` ${item}`, width)) }, item))), showHelp ? t().shortcuts.map((line) => _jsx(Text, { children: ui.dim(fitWidth(` ${line}`, width)) }, line)) : null] }));
|
|
356
346
|
}
|
|
@@ -8,13 +8,14 @@ import { framePosition } from "./terminalCursor.js";
|
|
|
8
8
|
import { isFocusReport } from "./terminalStatus.js";
|
|
9
9
|
import { fitWidth, stripAnsi } from "./lineBuffer.js";
|
|
10
10
|
import * as ui from "../ui.js";
|
|
11
|
+
import { t } from "../../core/messages/index.js";
|
|
11
12
|
// Numbered as in the Claude Code CLI's permission prompt; the digits and y/n/q answer too.
|
|
12
|
-
const
|
|
13
|
-
{ label:
|
|
14
|
-
{ label:
|
|
15
|
-
{ label:
|
|
13
|
+
const decisionOptions = () => [
|
|
14
|
+
{ label: `1. ${t().yes}`, value: "y" },
|
|
15
|
+
{ label: `2. ${t().no}`, value: "n" },
|
|
16
|
+
{ label: `3. ${t().stop}`, value: "q" },
|
|
16
17
|
];
|
|
17
|
-
const
|
|
18
|
+
const manualOptions = () => [{ label: `1. ${t().doneContinue}`, value: "continue" }];
|
|
18
19
|
const DECISION_KEYS = { "1": "y", "2": "n", "3": "q", y: "y", n: "n", q: "q" };
|
|
19
20
|
const ACCENT_HEX = "#d77757";
|
|
20
21
|
/**
|
|
@@ -25,7 +26,7 @@ const ACCENT_HEX = "#d77757";
|
|
|
25
26
|
export function previewLines(lines, terminalRows) {
|
|
26
27
|
const all = lines.flatMap((line) => line.split("\n"));
|
|
27
28
|
const max = Math.max(3, (terminalRows || 24) - 12);
|
|
28
|
-
return all.length <= max ? all : [...all.slice(0, max - 1), ui.dim(
|
|
29
|
+
return all.length <= max ? all : [...all.slice(0, max - 1), ui.dim(t().moreLines(all.length - max + 1))];
|
|
29
30
|
}
|
|
30
31
|
function CheckpointPanel({ checkpoint, renderApproval }) {
|
|
31
32
|
const decision = checkpoint.kind === "decision";
|
|
@@ -38,17 +39,17 @@ function CheckpointPanel({ checkpoint, renderApproval }) {
|
|
|
38
39
|
});
|
|
39
40
|
const { prompt } = checkpoint;
|
|
40
41
|
const { stdout } = useStdout();
|
|
41
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: ACCENT_HEX, paddingX: 1, children: [renderApproval ? (renderApproval(prompt)) : (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: prompt.title }), previewLines(prompt.lines, stdout.rows).map((line, index) => (_jsx(Text, { children: line }, index))), !decision && prompt.question ? _jsx(Text, { dimColor: true, children: prompt.question }) : null] })), decision ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { children:
|
|
42
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: ACCENT_HEX, paddingX: 1, children: [renderApproval ? (renderApproval(prompt)) : (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: prompt.title }), previewLines(prompt.lines, stdout.rows).map((line, index) => (_jsx(Text, { children: line }, index))), !decision && prompt.question ? _jsx(Text, { dimColor: true, children: prompt.question }) : null] })), decision ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { children: t().proceed }) })) : null, _jsx(Box, { marginTop: decision ? 0 : 1, children: _jsx(Select, { options: decision ? decisionOptions() : manualOptions(), onChange: (value) => checkpoint.answer(decision ? value : "") }, checkpoint.id) })] }));
|
|
42
43
|
}
|
|
43
44
|
function formatTokens(count) {
|
|
44
45
|
return count >= 1000 ? `${(count / 1000).toFixed(1)}k` : String(count);
|
|
45
46
|
}
|
|
46
47
|
export function statusText(mode, turns, usage, extra = {}) {
|
|
47
48
|
const parts = [
|
|
48
|
-
...(mode ? [`⏵⏵ ${mode}${extra.modeSwitchable ?
|
|
49
|
-
|
|
50
|
-
...(usage ? [
|
|
51
|
-
...(extra.contextPercent != null ? [
|
|
49
|
+
...(mode ? [`⏵⏵ ${t().mode(mode)}${extra.modeSwitchable ? ` (${t().switchModeKey})` : ""}`] : []),
|
|
50
|
+
t().turns(turns),
|
|
51
|
+
...(usage ? [t().tokens(formatTokens(usage.inputTokens), formatTokens(usage.outputTokens))] : []),
|
|
52
|
+
...(extra.contextPercent != null ? [t().context(Math.round(extra.contextPercent))] : []),
|
|
52
53
|
];
|
|
53
54
|
return parts.join(" · ");
|
|
54
55
|
}
|
|
@@ -63,7 +64,7 @@ function Working({ label, startedAt, width }) {
|
|
|
63
64
|
}, []);
|
|
64
65
|
const seconds = startedAt === null ? 0 : Math.floor((Date.now() - startedAt) / 1000);
|
|
65
66
|
const glyph = WORKING_GLYPHS[tick % WORKING_GLYPHS.length];
|
|
66
|
-
return _jsx(Text, { children: fitWidth(`${ui.working(`${glyph} ${label}`)} ${ui.dim(`(${seconds}s ·
|
|
67
|
+
return _jsx(Text, { children: fitWidth(`${ui.working(`${glyph} ${label}`)} ${ui.dim(`(${seconds}s · ${t().escToInterrupt})`)}`, width) });
|
|
67
68
|
}
|
|
68
69
|
/** Line in progress, checkpoint panel or spinner, the input, and the status bar. */
|
|
69
70
|
function LiveArea({ session, checkpoint, renderApproval, mode, width, statusExtra, modeSwitchable, children }) {
|
|
@@ -74,7 +75,7 @@ function LiveArea({ session, checkpoint, renderApproval, mode, width, statusExtr
|
|
|
74
75
|
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, width: width, marginTop: session.liveGap ? 1 : 0, children: [stripAnsi(session.live) ? _jsx(Text, { children: session.live }) : null, checkpoint ? (_jsx(CheckpointPanel, { checkpoint: checkpoint, renderApproval: renderApproval })) : session.busy ? (
|
|
75
76
|
// Always one blank line above the spinner: from this margin, or from the live area's
|
|
76
77
|
// own when a separator is pending and nothing is written above the spinner yet.
|
|
77
|
-
_jsxs(Box, { flexDirection: "column", marginTop: stripAnsi(session.live) || !session.liveGap ? 1 : 0, children: [_jsx(Working, { label: session.activity ??
|
|
78
|
+
_jsxs(Box, { flexDirection: "column", marginTop: stripAnsi(session.live) || !session.liveGap ? 1 : 0, children: [_jsx(Working, { label: session.activity ?? t().thinking, startedAt: session.turnStartedAt, width: width }), session.subagentActivity ? _jsx(Text, { dimColor: true, children: fitWidth(` ↳ ${session.subagentActivity}`, width) }) : null] })) : null, checkpoint ? null : children, _jsx(Text, { children: fitWidth(status, width) })] }));
|
|
78
79
|
}
|
|
79
80
|
/** A history line for the inline view: the human's line as a bar, a tool group unfolded with Ctrl+O. */
|
|
80
81
|
function inlineItem(item, expanded, width) {
|
|
@@ -184,7 +185,7 @@ function FullscreenSession({ session, checkpoint, renderApproval, mode, header,
|
|
|
184
185
|
setSelection(null);
|
|
185
186
|
if (text && onCopy) {
|
|
186
187
|
onCopy(text);
|
|
187
|
-
setCopied(
|
|
188
|
+
setCopied(t().copiedCharacters(text.length));
|
|
188
189
|
}
|
|
189
190
|
}
|
|
190
191
|
}
|
|
@@ -205,7 +206,7 @@ function FullscreenSession({ session, checkpoint, renderApproval, mode, header,
|
|
|
205
206
|
setAnchor(null); // typing
|
|
206
207
|
});
|
|
207
208
|
const below = rowsBelow(anchor, total);
|
|
208
|
-
const statusExtra = [anchor === null ? null :
|
|
209
|
+
const statusExtra = [anchor === null ? null : t().linesBelow(below), copied]
|
|
209
210
|
.filter(Boolean)
|
|
210
211
|
.join(" · ");
|
|
211
212
|
return (_jsxs(Box, { flexDirection: "column", width: columns - 1, height: rows, children: [header && header.length > 0 ? (_jsx(Box, { flexDirection: "column", flexShrink: 0, marginBottom: 1, children: header.map((line, i) => (_jsx(Text, { wrap: "truncate-end", children: line || " " }, i))) })) : null, _jsx(Box, { ref: historyRef, flexDirection: "column", flexGrow: 1, flexShrink: 1, overflow: "hidden", justifyContent: "flex-end", children: wrapped.rows.slice(start, end).map((row, i) => (_jsx(Text, { wrap: "truncate-end", children: highlightRow(row, selection, start + i) || " " }, start + i))) }), _jsx(LiveArea, { session: session, checkpoint: checkpoint, renderApproval: renderApproval, mode: mode, width: width, statusExtra: statusExtra || undefined, modeSwitchable: modeSwitchable, children: children })] }));
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import * as ui from "../ui.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
n: ui.warn("✘ Rejected"),
|
|
5
|
-
q: ui.error("■ Stopped"),
|
|
6
|
-
};
|
|
2
|
+
import { t } from "../../core/messages/index.js";
|
|
3
|
+
const outcome = (answer) => ({ y: ui.success(t().approved), n: ui.warn(t().rejected), q: ui.error(t().stopped) })[answer];
|
|
7
4
|
/**
|
|
8
5
|
* An `InteractionPort` for the Ink views: each checkpoint waits in a queue (the step gate
|
|
9
6
|
* can fire for several tool calls at once) until the panel answers it, or until the
|
|
@@ -37,13 +34,13 @@ export function createInkInteraction(note) {
|
|
|
37
34
|
answer(value) {
|
|
38
35
|
if (!remove(checkpoint))
|
|
39
36
|
return;
|
|
40
|
-
record(prompt, kind === "decision" ? (
|
|
37
|
+
record(prompt, kind === "decision" ? (outcome(value) ?? value) : ui.success(t().done));
|
|
41
38
|
resolve(value);
|
|
42
39
|
},
|
|
43
40
|
};
|
|
44
41
|
signal.addEventListener("abort", () => {
|
|
45
42
|
if (remove(checkpoint))
|
|
46
|
-
record(prompt, ui.dim(
|
|
43
|
+
record(prompt, ui.dim(t().answeredByFile));
|
|
47
44
|
}, { once: true });
|
|
48
45
|
queue = [...queue, checkpoint];
|
|
49
46
|
emit();
|
|
@@ -11,6 +11,12 @@ export interface ProgressViewOptions extends Omit<ConsoleRendererOptions, "outpu
|
|
|
11
11
|
mode?: Mode;
|
|
12
12
|
/** Use the plain console renderer even on a TTY. */
|
|
13
13
|
plain?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The language of the kit's texts ("en", "es", "fr", "de"). `--language=<code>` on the
|
|
16
|
+
* command line wins; without either, the one `buildSessionOptions()` chose from
|
|
17
|
+
* `config.language`, or else the system's.
|
|
18
|
+
*/
|
|
19
|
+
language?: string;
|
|
14
20
|
}
|
|
15
21
|
export interface ProgressView extends ConsoleRenderer {
|
|
16
22
|
/** Removes the live area and restores the previous interaction port; await it before printing anything else. */
|
|
@@ -7,6 +7,7 @@ import { createConsoleRenderer } from "../consoleRenderer.js";
|
|
|
7
7
|
import { createInkInteraction } from "./inkInteraction.js";
|
|
8
8
|
import { createSessionModel, liveWidth } from "./sessionModel.js";
|
|
9
9
|
import { SessionView } from "./SessionView.js";
|
|
10
|
+
import { applyLanguage } from "../language.js";
|
|
10
11
|
function ProgressApp({ model, interaction, renderApproval, mode, closed }) {
|
|
11
12
|
const checkpoint = useSyncExternalStore(interaction.subscribe, interaction.getSnapshot);
|
|
12
13
|
// Only a panel reads the keyboard (and so puts stdin in raw mode, where Ctrl+C no longer
|
|
@@ -29,6 +30,7 @@ function ProgressApp({ model, interaction, renderApproval, mode, closed }) {
|
|
|
29
30
|
* Without a TTY or with `plain`, it is `createConsoleRenderer()` with a no-op `close()`.
|
|
30
31
|
*/
|
|
31
32
|
export function createProgressView(options = {}) {
|
|
33
|
+
applyLanguage(options.language);
|
|
32
34
|
if (options.plain || !stdin.isTTY || !stdout.isTTY) {
|
|
33
35
|
const renderer = createConsoleRenderer(options);
|
|
34
36
|
return Object.assign(renderer, { close: async () => { } });
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure editing operations on the prompt's text and cursor (a UTF-16 index into `value`),
|
|
3
|
-
* so PromptInput.tsx only maps keys to them. The value may hold line breaks ("\n"): a
|
|
4
|
-
* multi-line prompt.
|
|
5
|
-
*/
|
|
6
1
|
export interface EditState {
|
|
7
2
|
value: string;
|
|
8
3
|
cursor: number;
|
|
@@ -51,6 +46,8 @@ export interface PasteRegistry {
|
|
|
51
46
|
/** `value` with every token replaced by its text. */
|
|
52
47
|
expand(value: string): string;
|
|
53
48
|
}
|
|
49
|
+
export declare const PASTE_TOKEN_BEFORE: RegExp;
|
|
50
|
+
export declare const PASTE_TOKEN_AFTER: RegExp;
|
|
54
51
|
/** Normalizes a pasted chunk's line breaks (terminals send "\r" or "\r\n"). */
|
|
55
52
|
export declare function normalizePaste(text: string): string;
|
|
56
53
|
/** A paste worth folding into a token: more than one line. */
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure editing operations on the prompt's text and cursor (a UTF-16 index into `value`),
|
|
3
|
+
* so PromptInput.tsx only maps keys to them. The value may hold line breaks ("\n"): a
|
|
4
|
+
* multi-line prompt.
|
|
5
|
+
*/
|
|
6
|
+
import { t } from "../../core/messages/index.js";
|
|
1
7
|
export function insert({ value, cursor }, text) {
|
|
2
8
|
return { value: value.slice(0, cursor) + text + value.slice(cursor), cursor: cursor + text.length };
|
|
3
9
|
}
|
|
@@ -91,10 +97,12 @@ export function continueLine(state) {
|
|
|
91
97
|
return null;
|
|
92
98
|
return insert(deleteBack(state), "\n");
|
|
93
99
|
}
|
|
94
|
-
|
|
100
|
+
// A paste token in any language ("[Pasted text #1 +2 lines]", "[Texto pegado #1 +2 líneas]"...).
|
|
101
|
+
const PASTE_TOKEN = /\[[^\]\n#]+#(\d+) \+\d+ [^\]\n]+\]/g;
|
|
102
|
+
export const PASTE_TOKEN_BEFORE = /\[[^\]\n#]+#\d+ \+\d+ [^\]\n]+\]$/;
|
|
103
|
+
export const PASTE_TOKEN_AFTER = /^\[[^\]\n#]+#\d+ \+\d+ [^\]\n]+\]/;
|
|
95
104
|
function pasteToken(id, text) {
|
|
96
|
-
|
|
97
|
-
return `[Pasted text #${id} +${extra} ${extra === 1 ? "line" : "lines"}]`;
|
|
105
|
+
return t().pastedText(id, text.split("\n").length - 1);
|
|
98
106
|
}
|
|
99
107
|
/** Normalizes a pasted chunk's line breaks (terminals send "\r" or "\r\n"). */
|
|
100
108
|
export function normalizePaste(text) {
|
|
@@ -2,6 +2,7 @@ import type { Options } from "@anthropic-ai/claude-agent-sdk";
|
|
|
2
2
|
import type { Mode } from "../../core/agentSpec.js";
|
|
3
3
|
import { type ModeControl } from "../../core/session.js";
|
|
4
4
|
import { type ChatTuiOptions } from "../chatTui.js";
|
|
5
|
+
import { type SessionOpener } from "../runs.js";
|
|
5
6
|
import { type RenderApproval } from "./SessionView.js";
|
|
6
7
|
import type { ToolPhrase } from "./toolGroup.js";
|
|
7
8
|
import { type HeaderInfo } from "./header.js";
|
|
@@ -46,7 +47,8 @@ export interface InkChatOptions extends ChatTuiOptions {
|
|
|
46
47
|
/**
|
|
47
48
|
* The session's mode control (`buildSessionOptions()` returns it): the status bar shows the
|
|
48
49
|
* current mode, and Shift+Tab switches between the modes it allows ("guided" and
|
|
49
|
-
* "interactive"; an autonomous session can't switch).
|
|
50
|
+
* "interactive"; an autonomous session can't switch). With a session opener, the one it
|
|
51
|
+
* returns is used instead.
|
|
50
52
|
*/
|
|
51
53
|
modeControl?: ModeControl;
|
|
52
54
|
/**
|
|
@@ -55,6 +57,16 @@ export interface InkChatOptions extends ChatTuiOptions {
|
|
|
55
57
|
*/
|
|
56
58
|
firstPromptSuggestion?: string;
|
|
57
59
|
}
|
|
60
|
+
/** A choice asked in place of the prompt (the /resume list). */
|
|
61
|
+
export interface ChatPicker {
|
|
62
|
+
title: string;
|
|
63
|
+
options: {
|
|
64
|
+
label: string;
|
|
65
|
+
value: string;
|
|
66
|
+
}[];
|
|
67
|
+
/** Settles it with the chosen value, or null if cancelled. */
|
|
68
|
+
resolve(value: string | null): void;
|
|
69
|
+
}
|
|
58
70
|
export interface ChatInputSnapshot {
|
|
59
71
|
/** The chat loop is waiting for a line (no turn in flight). */
|
|
60
72
|
prompting: boolean;
|
|
@@ -68,6 +80,8 @@ export interface ChatInputSnapshot {
|
|
|
68
80
|
files: string[];
|
|
69
81
|
/** The current mode, when a mode control is given. */
|
|
70
82
|
mode: Mode | null;
|
|
83
|
+
/** A choice shown in place of the prompt, if any. */
|
|
84
|
+
picker: ChatPicker | null;
|
|
71
85
|
closed: boolean;
|
|
72
86
|
}
|
|
73
87
|
/**
|
|
@@ -85,6 +99,11 @@ export declare function createChatInput(): {
|
|
|
85
99
|
setSuggestion: (suggestion: string | null) => void;
|
|
86
100
|
setFiles: (files: string[]) => void;
|
|
87
101
|
setMode: (mode: Mode | null) => void;
|
|
102
|
+
/** Shows a choice in place of the prompt; resolves with the value chosen, or null if cancelled. */
|
|
103
|
+
pick(title: string, options: {
|
|
104
|
+
label: string;
|
|
105
|
+
value: string;
|
|
106
|
+
}[]): Promise<string | null>;
|
|
88
107
|
/** Takes the last queued line back out of the queue (to edit it in the prompt), or null. */
|
|
89
108
|
unqueueLast(): string | null;
|
|
90
109
|
close: () => void;
|
|
@@ -98,6 +117,10 @@ export declare function createChatInput(): {
|
|
|
98
117
|
*
|
|
99
118
|
* With `fullscreen` it takes the whole terminal instead (see `InkChatOptions.fullscreen`).
|
|
100
119
|
*
|
|
120
|
+
* Given a session opener instead of options, and `runsDir`, each run keeps its conversation
|
|
121
|
+
* in its own folder and can be resumed: `--continue` on the command line starts with the
|
|
122
|
+
* latest, and `/resume` picks one (see `ChatTuiOptions.runsDir`).
|
|
123
|
+
*
|
|
101
124
|
* Without a TTY (piped, background) or with `plain`, it is `runChatTui()` unchanged.
|
|
102
125
|
*/
|
|
103
|
-
export declare function runChatInk(options: Options, tuiOptions?: InkChatOptions): Promise<void>;
|
|
126
|
+
export declare function runChatInk(options: Options | SessionOpener, tuiOptions?: InkChatOptions): Promise<void>;
|