@datename/fastcode-termux 0.1.6-beta → 0.1.6
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 +6 -24
- package/dist/agent/guards.d.ts +0 -8
- package/dist/agent/guards.js +0 -195
- package/dist/agent/protocol.js +1 -4
- package/dist/agent/tools.d.ts +1 -3
- package/dist/agent/tools.js +377 -2
- package/dist/app.js +103 -335
- package/dist/cli.js +311 -362
- package/dist/commands/index.js +0 -22
- package/dist/commands/login.d.ts +2 -4
- package/dist/commands/login.js +10 -12
- package/dist/commands/logout.js +16 -8
- package/dist/commands/model.d.ts +0 -13
- package/dist/commands/model.js +22 -42
- package/dist/commands/provider.d.ts +0 -13
- package/dist/commands/provider.js +9 -20
- package/dist/commands/whoami.js +5 -6
- package/dist/config/store.d.ts +12 -32
- package/dist/config/store.js +65 -117
- package/dist/config/trust.d.ts +8 -21
- package/dist/config/trust.js +13 -99
- package/dist/lib/diagnostics.d.ts +4 -29
- package/dist/lib/diagnostics.js +4 -60
- package/dist/lib/http.d.ts +1 -11
- package/dist/lib/http.js +4 -62
- package/dist/providers/anthropic.d.ts +0 -1
- package/dist/providers/anthropic.js +0 -12
- package/dist/providers/custom.d.ts +0 -9
- package/dist/providers/deepseek.d.ts +0 -10
- package/dist/providers/deepseek.js +1 -2
- package/dist/providers/google.d.ts +0 -9
- package/dist/providers/groq.d.ts +0 -10
- package/dist/providers/groq.js +1 -2
- package/dist/providers/index.d.ts +201 -10
- package/dist/providers/index.js +0 -30
- package/dist/providers/mistral.d.ts +0 -10
- package/dist/providers/mistral.js +1 -2
- package/dist/providers/moonshot.d.ts +0 -10
- package/dist/providers/moonshot.js +1 -2
- package/dist/providers/ollama.d.ts +0 -9
- package/dist/providers/openai.d.ts +0 -10
- package/dist/providers/openai.js +1 -2
- package/dist/providers/openaiCompatible.d.ts +0 -9
- package/dist/providers/openaiCompatible.js +10 -58
- package/dist/providers/openrouter.d.ts +0 -10
- package/dist/providers/openrouter.js +1 -2
- package/dist/providers/qwen.d.ts +0 -10
- package/dist/providers/qwen.js +1 -2
- package/dist/providers/xai.d.ts +0 -10
- package/dist/providers/xai.js +1 -2
- package/dist/providers/zhipu.d.ts +0 -10
- package/dist/providers/zhipu.js +1 -2
- package/dist/session/log.d.ts +12 -35
- package/dist/session/log.js +10 -206
- package/dist/types.d.ts +1 -60
- package/dist/ui/Message.d.ts +7 -1
- package/dist/ui/Message.js +2 -13
- package/dist/ui/Prompt.d.ts +2 -1
- package/dist/ui/Prompt.js +2 -2
- package/dist/ui/Spinner.js +5 -11
- package/dist/ui/StatusBar.d.ts +1 -2
- package/dist/ui/StatusBar.js +5 -6
- package/package.json +1 -1
- package/src/agent/guards.ts +0 -196
- package/src/agent/protocol.ts +1 -4
- package/src/agent/tools.ts +377 -3
- package/src/app.ts +103 -335
- package/src/cli.ts +3 -30
- package/src/commands/index.ts +0 -22
- package/src/commands/login.ts +10 -12
- package/src/commands/logout.ts +16 -8
- package/src/commands/model.ts +22 -42
- package/src/commands/provider.ts +9 -20
- package/src/commands/whoami.ts +5 -6
- package/src/config/store.ts +69 -122
- package/src/config/trust.ts +21 -137
- package/src/lib/diagnostics.ts +4 -60
- package/src/lib/http.ts +4 -66
- package/src/providers/anthropic.ts +1 -13
- package/src/providers/deepseek.ts +1 -2
- package/src/providers/groq.ts +1 -2
- package/src/providers/index.ts +1 -32
- package/src/providers/mistral.ts +1 -2
- package/src/providers/moonshot.ts +1 -2
- package/src/providers/openai.ts +1 -2
- package/src/providers/openaiCompatible.ts +10 -58
- package/src/providers/openrouter.ts +1 -2
- package/src/providers/qwen.ts +1 -2
- package/src/providers/xai.ts +1 -2
- package/src/providers/zhipu.ts +1 -2
- package/src/session/log.ts +28 -267
- package/src/types.ts +1 -63
- package/src/ui/Message.ts +2 -13
- package/src/ui/Prompt.ts +2 -2
- package/src/ui/Spinner.ts +5 -11
- package/src/ui/StatusBar.ts +5 -6
- package/dist/agent/approval.d.ts +0 -50
- package/dist/agent/approval.js +0 -7
- package/dist/agent/compaction.d.ts +0 -39
- package/dist/agent/compaction.js +0 -227
- package/dist/agent/context.d.ts +0 -29
- package/dist/agent/context.js +0 -62
- package/dist/agent/ignore.d.ts +0 -11
- package/dist/agent/ignore.js +0 -129
- package/dist/agent/process-tree.d.ts +0 -2
- package/dist/agent/process-tree.js +0 -31
- package/dist/agent/protocol-mode.d.ts +0 -6
- package/dist/agent/protocol-mode.js +0 -51
- package/dist/agent/registry.d.ts +0 -17
- package/dist/agent/registry.js +0 -90
- package/dist/agent/runner.d.ts +0 -106
- package/dist/agent/runner.js +0 -352
- package/dist/agent/shell-session.d.ts +0 -37
- package/dist/agent/shell-session.js +0 -400
- package/dist/agent/stream.d.ts +0 -29
- package/dist/agent/stream.js +0 -103
- package/dist/agent/tool-schemas.d.ts +0 -2
- package/dist/agent/tool-schemas.js +0 -63
- package/dist/agent/tools/append_file.d.ts +0 -2
- package/dist/agent/tools/append_file.js +0 -26
- package/dist/agent/tools/apply_patch.d.ts +0 -10
- package/dist/agent/tools/apply_patch.js +0 -52
- package/dist/agent/tools/copy_file.d.ts +0 -2
- package/dist/agent/tools/copy_file.js +0 -30
- package/dist/agent/tools/delete_file.d.ts +0 -2
- package/dist/agent/tools/delete_file.js +0 -24
- package/dist/agent/tools/grep.d.ts +0 -2
- package/dist/agent/tools/grep.js +0 -23
- package/dist/agent/tools/index.d.ts +0 -2
- package/dist/agent/tools/index.js +0 -32
- package/dist/agent/tools/list_files.d.ts +0 -2
- package/dist/agent/tools/list_files.js +0 -17
- package/dist/agent/tools/make_dir.d.ts +0 -2
- package/dist/agent/tools/make_dir.js +0 -24
- package/dist/agent/tools/move_path.d.ts +0 -2
- package/dist/agent/tools/move_path.js +0 -47
- package/dist/agent/tools/paths.d.ts +0 -7
- package/dist/agent/tools/paths.js +0 -84
- package/dist/agent/tools/pwd.d.ts +0 -2
- package/dist/agent/tools/pwd.js +0 -10
- package/dist/agent/tools/read_file.d.ts +0 -2
- package/dist/agent/tools/read_file.js +0 -21
- package/dist/agent/tools/read_many.d.ts +0 -2
- package/dist/agent/tools/read_many.js +0 -25
- package/dist/agent/tools/readers.d.ts +0 -6
- package/dist/agent/tools/readers.js +0 -216
- package/dist/agent/tools/replace_in_file.d.ts +0 -2
- package/dist/agent/tools/replace_in_file.js +0 -30
- package/dist/agent/tools/shell.d.ts +0 -2
- package/dist/agent/tools/shell.js +0 -48
- package/dist/agent/tools/stat_path.d.ts +0 -2
- package/dist/agent/tools/stat_path.js +0 -31
- package/dist/agent/tools/types.d.ts +0 -39
- package/dist/agent/tools/types.js +0 -1
- package/dist/agent/tools/validate.d.ts +0 -6
- package/dist/agent/tools/validate.js +0 -29
- package/dist/agent/tools/write_file.d.ts +0 -2
- package/dist/agent/tools/write_file.js +0 -26
- package/dist/commands/attach.d.ts +0 -9
- package/dist/commands/attach.js +0 -15
- package/dist/commands/bg.d.ts +0 -7
- package/dist/commands/bg.js +0 -9
- package/dist/commands/cancel.d.ts +0 -10
- package/dist/commands/cancel.js +0 -10
- package/dist/commands/compact.d.ts +0 -10
- package/dist/commands/compact.js +0 -22
- package/dist/commands/context.d.ts +0 -7
- package/dist/commands/context.js +0 -24
- package/dist/commands/edit.d.ts +0 -10
- package/dist/commands/edit.js +0 -36
- package/dist/commands/effort.d.ts +0 -25
- package/dist/commands/effort.js +0 -48
- package/dist/commands/expand.d.ts +0 -10
- package/dist/commands/expand.js +0 -8
- package/dist/commands/jobs.d.ts +0 -7
- package/dist/commands/jobs.js +0 -16
- package/dist/commands/kill.d.ts +0 -7
- package/dist/commands/kill.js +0 -11
- package/dist/commands/steer.d.ts +0 -11
- package/dist/commands/steer.js +0 -13
- package/dist/lib/model-meta.d.ts +0 -19
- package/dist/lib/model-meta.js +0 -113
- package/dist/lib/native-tools.d.ts +0 -12
- package/dist/lib/native-tools.js +0 -21
- package/dist/lib/net.d.ts +0 -33
- package/dist/lib/net.js +0 -168
- package/dist/patch/apply.d.ts +0 -15
- package/dist/patch/apply.js +0 -172
- package/dist/patch/parse.d.ts +0 -29
- package/dist/patch/parse.js +0 -112
- package/dist/patch/plan.d.ts +0 -49
- package/dist/patch/plan.js +0 -273
- package/dist/session/projection.d.ts +0 -43
- package/dist/session/projection.js +0 -257
- package/dist/ui/Picker.d.ts +0 -8
- package/dist/ui/Picker.js +0 -24
- package/src/agent/approval.ts +0 -47
- package/src/agent/compaction.ts +0 -276
- package/src/agent/context.ts +0 -97
- package/src/agent/ignore.ts +0 -147
- package/src/agent/process-tree.ts +0 -32
- package/src/agent/protocol-mode.ts +0 -59
- package/src/agent/registry.ts +0 -98
- package/src/agent/runner.ts +0 -443
- package/src/agent/shell-session.ts +0 -449
- package/src/agent/stream.ts +0 -143
- package/src/agent/tool-schemas.ts +0 -67
- package/src/agent/tools/append_file.ts +0 -28
- package/src/agent/tools/apply_patch.ts +0 -54
- package/src/agent/tools/copy_file.ts +0 -32
- package/src/agent/tools/delete_file.ts +0 -26
- package/src/agent/tools/grep.ts +0 -25
- package/src/agent/tools/index.ts +0 -34
- package/src/agent/tools/list_files.ts +0 -19
- package/src/agent/tools/make_dir.ts +0 -26
- package/src/agent/tools/move_path.ts +0 -50
- package/src/agent/tools/paths.ts +0 -92
- package/src/agent/tools/pwd.ts +0 -12
- package/src/agent/tools/read_file.ts +0 -23
- package/src/agent/tools/read_many.ts +0 -27
- package/src/agent/tools/readers.ts +0 -228
- package/src/agent/tools/replace_in_file.ts +0 -32
- package/src/agent/tools/shell.ts +0 -53
- package/src/agent/tools/stat_path.ts +0 -33
- package/src/agent/tools/types.ts +0 -42
- package/src/agent/tools/validate.ts +0 -35
- package/src/agent/tools/write_file.ts +0 -28
- package/src/commands/attach.ts +0 -16
- package/src/commands/bg.ts +0 -9
- package/src/commands/cancel.ts +0 -10
- package/src/commands/compact.ts +0 -23
- package/src/commands/context.ts +0 -25
- package/src/commands/edit.ts +0 -37
- package/src/commands/effort.ts +0 -49
- package/src/commands/expand.ts +0 -8
- package/src/commands/jobs.ts +0 -17
- package/src/commands/kill.ts +0 -12
- package/src/commands/steer.ts +0 -13
- package/src/lib/model-meta.ts +0 -138
- package/src/lib/native-tools.ts +0 -32
- package/src/lib/net.ts +0 -204
- package/src/patch/apply.ts +0 -194
- package/src/patch/parse.ts +0 -131
- package/src/patch/plan.ts +0 -317
- package/src/session/projection.ts +0 -308
- package/src/ui/Picker.ts +0 -24
package/dist/patch/plan.d.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { type PatchHunk } from "./parse.js";
|
|
2
|
-
export interface PlannedFile {
|
|
3
|
-
kind: "add" | "delete" | "update";
|
|
4
|
-
relPath: string;
|
|
5
|
-
path: string;
|
|
6
|
-
moveRel?: string;
|
|
7
|
-
moveTo?: string;
|
|
8
|
-
existed: boolean;
|
|
9
|
-
origMode: number;
|
|
10
|
-
eol: "\n" | "\r\n";
|
|
11
|
-
oldContent?: string;
|
|
12
|
-
newContent?: string;
|
|
13
|
-
diff: string;
|
|
14
|
-
changedLines: number;
|
|
15
|
-
}
|
|
16
|
-
export interface PatchPlan {
|
|
17
|
-
files: PlannedFile[];
|
|
18
|
-
warnings: string[];
|
|
19
|
-
}
|
|
20
|
-
export type PlanResult = {
|
|
21
|
-
ok: true;
|
|
22
|
-
plan: PatchPlan;
|
|
23
|
-
} | {
|
|
24
|
-
ok: false;
|
|
25
|
-
output: string;
|
|
26
|
-
};
|
|
27
|
-
export type PatchReview = {
|
|
28
|
-
kind: "allow";
|
|
29
|
-
} | {
|
|
30
|
-
kind: "deny";
|
|
31
|
-
reason: string;
|
|
32
|
-
} | {
|
|
33
|
-
kind: "pending";
|
|
34
|
-
reason: string;
|
|
35
|
-
changedLines: number;
|
|
36
|
-
};
|
|
37
|
-
export declare function planPatch(patch: string, cwd: string): PlanResult;
|
|
38
|
-
export declare function reviewPatchPlan(plan: PatchPlan): PatchReview;
|
|
39
|
-
export declare function detectEol(content: string): "\n" | "\r\n";
|
|
40
|
-
type ApplyHunksResult = {
|
|
41
|
-
ok: true;
|
|
42
|
-
content: string;
|
|
43
|
-
} | {
|
|
44
|
-
ok: false;
|
|
45
|
-
output: string;
|
|
46
|
-
};
|
|
47
|
-
export declare function applyHunks(oldContent: string, hunks: PatchHunk[]): ApplyHunksResult;
|
|
48
|
-
export declare function makeDiff(oldPath: string | null, newPath: string | null, oldContent: string, newContent: string, kind: "add" | "delete" | "update"): string;
|
|
49
|
-
export {};
|
package/dist/patch/plan.js
DELETED
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import { closeSync, constants as fsConstants, lstatSync, openSync, readFileSync } from "node:fs";
|
|
2
|
-
import { basename } from "node:path";
|
|
3
|
-
import { sandboxPath } from "../agent/tools/paths.js";
|
|
4
|
-
import { parsePatch } from "./parse.js";
|
|
5
|
-
export function planPatch(patch, cwd) {
|
|
6
|
-
const parsed = parsePatch(patch);
|
|
7
|
-
if (parsed.ok === false)
|
|
8
|
-
return { ok: false, output: parsed.output };
|
|
9
|
-
const files = [];
|
|
10
|
-
const warnings = [];
|
|
11
|
-
for (const op of parsed.ops) {
|
|
12
|
-
const planned = planOp(op, cwd, warnings);
|
|
13
|
-
if (planned.ok === false)
|
|
14
|
-
return { ok: false, output: planned.output };
|
|
15
|
-
files.push(planned.file);
|
|
16
|
-
}
|
|
17
|
-
return { ok: true, plan: { files, warnings } };
|
|
18
|
-
}
|
|
19
|
-
function planOp(op, cwd, warnings) {
|
|
20
|
-
try {
|
|
21
|
-
const path = sandboxPath(cwd, op.path);
|
|
22
|
-
if (op.kind === "add") {
|
|
23
|
-
if (lexists(path))
|
|
24
|
-
return { ok: false, output: `Add File target already exists: ${op.path}` };
|
|
25
|
-
const newContent = op.lines.length > 0 ? op.lines.join("\n") + "\n" : "";
|
|
26
|
-
return {
|
|
27
|
-
ok: true,
|
|
28
|
-
file: {
|
|
29
|
-
kind: "add",
|
|
30
|
-
relPath: op.path,
|
|
31
|
-
path,
|
|
32
|
-
existed: false,
|
|
33
|
-
origMode: 0o644,
|
|
34
|
-
eol: "\n",
|
|
35
|
-
newContent,
|
|
36
|
-
diff: makeDiff(null, op.path, "", newContent, "add"),
|
|
37
|
-
changedLines: newContent.split("\n").filter(Boolean).length,
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
if (!lexists(path))
|
|
42
|
-
return { ok: false, output: `file not found: ${op.path}` };
|
|
43
|
-
const info = lstatSync(path);
|
|
44
|
-
if (info.isSymbolicLink())
|
|
45
|
-
return { ok: false, output: `refusing to patch a symbolic link: ${op.path}` };
|
|
46
|
-
if (!info.isFile())
|
|
47
|
-
return { ok: false, output: `not a regular file: ${op.path}` };
|
|
48
|
-
const oldContent = readNoFollow(path);
|
|
49
|
-
if (op.kind === "delete") {
|
|
50
|
-
return {
|
|
51
|
-
ok: true,
|
|
52
|
-
file: {
|
|
53
|
-
kind: "delete",
|
|
54
|
-
relPath: op.path,
|
|
55
|
-
path,
|
|
56
|
-
existed: true,
|
|
57
|
-
origMode: info.mode & 0o777,
|
|
58
|
-
eol: detectEol(oldContent),
|
|
59
|
-
oldContent,
|
|
60
|
-
diff: makeDiff(op.path, null, oldContent, "", "delete"),
|
|
61
|
-
changedLines: oldContent.split(/\r?\n/).length,
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
const eol = detectEol(oldContent);
|
|
66
|
-
if (eol === "\n" && /\r\n/.test(oldContent))
|
|
67
|
-
warnings.push(`mixed line endings in ${op.path}; writing LF`);
|
|
68
|
-
let newContent = oldContent;
|
|
69
|
-
if (op.hunks.length > 0) {
|
|
70
|
-
const applied = applyHunks(oldContent, op.hunks);
|
|
71
|
-
if (applied.ok === false)
|
|
72
|
-
return applied;
|
|
73
|
-
newContent = applied.content;
|
|
74
|
-
if (eol === "\r\n")
|
|
75
|
-
newContent = newContent.replace(/\n/g, "\r\n");
|
|
76
|
-
}
|
|
77
|
-
let moveTo;
|
|
78
|
-
let moveRel;
|
|
79
|
-
if (op.moveTo) {
|
|
80
|
-
moveTo = sandboxPath(cwd, op.moveTo);
|
|
81
|
-
if (lexists(moveTo))
|
|
82
|
-
return { ok: false, output: `Move target already exists: ${op.moveTo}` };
|
|
83
|
-
moveRel = op.moveTo;
|
|
84
|
-
}
|
|
85
|
-
const diff = makeDiff(op.path, op.moveTo ?? op.path, oldContent, newContent, "update");
|
|
86
|
-
return {
|
|
87
|
-
ok: true,
|
|
88
|
-
file: {
|
|
89
|
-
kind: "update",
|
|
90
|
-
relPath: op.path,
|
|
91
|
-
path,
|
|
92
|
-
moveRel,
|
|
93
|
-
moveTo,
|
|
94
|
-
existed: true,
|
|
95
|
-
origMode: info.mode & 0o777,
|
|
96
|
-
eol,
|
|
97
|
-
oldContent,
|
|
98
|
-
newContent,
|
|
99
|
-
diff,
|
|
100
|
-
changedLines: countChangedLines(oldContent, newContent),
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
catch (err) {
|
|
105
|
-
return { ok: false, output: err?.message ?? String(err) };
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
export function reviewPatchPlan(plan) {
|
|
109
|
-
let changed = 0;
|
|
110
|
-
for (const file of plan.files) {
|
|
111
|
-
changed += file.changedLines;
|
|
112
|
-
if (isHardDenied(file.path))
|
|
113
|
-
return { kind: "deny", reason: `patch targets a protected path: ${file.relPath}` };
|
|
114
|
-
if (isSensitivePath(file.path))
|
|
115
|
-
return { kind: "pending", reason: `patch targets a sensitive path: ${file.relPath}`, changedLines: changed };
|
|
116
|
-
}
|
|
117
|
-
if (changed > 200)
|
|
118
|
-
return { kind: "pending", reason: `patch changes ${changed} lines (limit 200)`, changedLines: changed };
|
|
119
|
-
return { kind: "allow" };
|
|
120
|
-
}
|
|
121
|
-
function isHardDenied(path) {
|
|
122
|
-
const normalized = path.replace(/\\/g, "/").toLowerCase();
|
|
123
|
-
const segments = normalized.split("/");
|
|
124
|
-
if (segments.includes(".ssh") || segments.includes(".gnupg"))
|
|
125
|
-
return true;
|
|
126
|
-
if (normalized.includes("/.git/hooks/"))
|
|
127
|
-
return true;
|
|
128
|
-
if (normalized.includes("/android/data/"))
|
|
129
|
-
return true;
|
|
130
|
-
const name = basename(normalized);
|
|
131
|
-
return name === "auth.json" || name === "trust.json";
|
|
132
|
-
}
|
|
133
|
-
function isSensitivePath(path) {
|
|
134
|
-
const normalized = path.replace(/\\/g, "/").toLowerCase();
|
|
135
|
-
const name = basename(normalized);
|
|
136
|
-
return name === ".env" || name.startsWith(".env.") || /[.](pem|key|p12|pfx)$/.test(name) || normalized.includes("/.git/hooks/");
|
|
137
|
-
}
|
|
138
|
-
function lexists(path) {
|
|
139
|
-
try {
|
|
140
|
-
lstatSync(path);
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
function readNoFollow(path) {
|
|
148
|
-
const flags = fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0);
|
|
149
|
-
const fd = openSync(path, flags);
|
|
150
|
-
try {
|
|
151
|
-
return readFileSync(fd, "utf8");
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
closeSync(fd);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
export function detectEol(content) {
|
|
158
|
-
const crlf = (content.match(/\r\n/g) ?? []).length;
|
|
159
|
-
const lf = (content.match(/\n/g) ?? []).length;
|
|
160
|
-
return crlf > 0 && crlf === lf ? "\r\n" : "\n";
|
|
161
|
-
}
|
|
162
|
-
export function applyHunks(oldContent, hunks) {
|
|
163
|
-
const lines = oldContent.split(/\r?\n/);
|
|
164
|
-
let changed = false;
|
|
165
|
-
for (const hunk of hunks) {
|
|
166
|
-
const needle = hunk.lines.filter((line) => line.type !== "+").map((line) => line.text);
|
|
167
|
-
const replacement = hunk.lines.filter((line) => line.type !== "-").map((line) => line.text);
|
|
168
|
-
if (needle.length === 0) {
|
|
169
|
-
const at = lines.length > 0 && lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
170
|
-
lines.splice(at, 0, ...replacement);
|
|
171
|
-
changed = true;
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
const verdict = findHunk(lines, needle);
|
|
175
|
-
if (verdict.ok === false)
|
|
176
|
-
return verdict;
|
|
177
|
-
lines.splice(verdict.index, needle.length, ...replacement);
|
|
178
|
-
changed = true;
|
|
179
|
-
}
|
|
180
|
-
if (!changed)
|
|
181
|
-
return { ok: false, output: "patch made no changes" };
|
|
182
|
-
return { ok: true, content: lines.join("\n") };
|
|
183
|
-
}
|
|
184
|
-
function findHunk(lines, needle) {
|
|
185
|
-
for (let level = 0; level < 3; level += 1) {
|
|
186
|
-
const matches = matchAll(lines, needle, level);
|
|
187
|
-
if (matches.length === 0)
|
|
188
|
-
continue;
|
|
189
|
-
if (matches.length > 1) {
|
|
190
|
-
return {
|
|
191
|
-
ok: false,
|
|
192
|
-
output: `ambiguous patch context (${matches.length} candidates at lines ${matches.map((index) => index + 1).join(", ")}); add surrounding context`,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
return { ok: true, index: matches[0] };
|
|
196
|
-
}
|
|
197
|
-
return { ok: false, output: `patch context not found: ${JSON.stringify(needle.slice(0, 4))}` };
|
|
198
|
-
}
|
|
199
|
-
function matchAll(lines, needle, level) {
|
|
200
|
-
const found = [];
|
|
201
|
-
const wanted = needle.map((line) => normalizeLine(line, level));
|
|
202
|
-
for (let i = 0; i + needle.length <= lines.length; i += 1) {
|
|
203
|
-
let match = true;
|
|
204
|
-
for (let j = 0; j < needle.length; j += 1) {
|
|
205
|
-
if (normalizeLine(lines[i + j], level) !== wanted[j]) {
|
|
206
|
-
match = false;
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
if (match)
|
|
211
|
-
found.push(i);
|
|
212
|
-
}
|
|
213
|
-
return found;
|
|
214
|
-
}
|
|
215
|
-
function normalizeLine(line, level) {
|
|
216
|
-
if (level === 0)
|
|
217
|
-
return line;
|
|
218
|
-
if (level === 1)
|
|
219
|
-
return line.replace(/\s+$/, "");
|
|
220
|
-
return line.replace(/\s+/g, " ").trim();
|
|
221
|
-
}
|
|
222
|
-
export function makeDiff(oldPath, newPath, oldContent, newContent, kind) {
|
|
223
|
-
const oldLines = splitDiffLines(oldContent);
|
|
224
|
-
const newLines = splitDiffLines(newContent);
|
|
225
|
-
let prefix = 0;
|
|
226
|
-
while (prefix < oldLines.length && prefix < newLines.length && oldLines[prefix] === newLines[prefix])
|
|
227
|
-
prefix += 1;
|
|
228
|
-
let suffix = 0;
|
|
229
|
-
while (suffix < oldLines.length - prefix && suffix < newLines.length - prefix &&
|
|
230
|
-
oldLines[oldLines.length - 1 - suffix] === newLines[newLines.length - 1 - suffix])
|
|
231
|
-
suffix += 1;
|
|
232
|
-
const oldMiddle = oldLines.slice(prefix, oldLines.length - suffix);
|
|
233
|
-
const newMiddle = newLines.slice(prefix, newLines.length - suffix);
|
|
234
|
-
const oldStart = prefix + 1;
|
|
235
|
-
const newStart = prefix + 1;
|
|
236
|
-
const header = kind === "add"
|
|
237
|
-
? `--- /dev/null\n+++ b/${newPath ?? ""}`
|
|
238
|
-
: kind === "delete"
|
|
239
|
-
? `--- a/${oldPath ?? ""}\n+++ /dev/null`
|
|
240
|
-
: `--- a/${oldPath ?? ""}\n+++ b/${newPath ?? oldPath ?? ""}`;
|
|
241
|
-
const body = [
|
|
242
|
-
`@@ -${oldStart},${oldMiddle.length} +${newStart},${newMiddle.length} @@`,
|
|
243
|
-
...oldMiddle.map((line) => "-" + line),
|
|
244
|
-
...newMiddle.map((line) => "+" + line),
|
|
245
|
-
];
|
|
246
|
-
return [header, ...body].join("\n");
|
|
247
|
-
}
|
|
248
|
-
function splitDiffLines(content) {
|
|
249
|
-
if (content === "")
|
|
250
|
-
return [];
|
|
251
|
-
const lines = content.split(/\r?\n/);
|
|
252
|
-
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
253
|
-
lines.pop();
|
|
254
|
-
return lines;
|
|
255
|
-
}
|
|
256
|
-
function countChangedLines(oldContent, newContent) {
|
|
257
|
-
const oldLines = oldContent.split(/\r?\n/);
|
|
258
|
-
const newLines = newContent.split(/\r?\n/);
|
|
259
|
-
let changed = 0;
|
|
260
|
-
let i = 0;
|
|
261
|
-
let j = 0;
|
|
262
|
-
while (i < oldLines.length && j < newLines.length) {
|
|
263
|
-
if (oldLines[i] === newLines[j]) {
|
|
264
|
-
i += 1;
|
|
265
|
-
j += 1;
|
|
266
|
-
continue;
|
|
267
|
-
}
|
|
268
|
-
changed += 1;
|
|
269
|
-
i += 1;
|
|
270
|
-
j += 1;
|
|
271
|
-
}
|
|
272
|
-
return changed + Math.abs(oldLines.length - newLines.length);
|
|
273
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { ChatMessage } from "../types.js";
|
|
2
|
-
import { type ActiveFile } from "./log.js";
|
|
3
|
-
export interface SessionMeta {
|
|
4
|
-
cwd: string;
|
|
5
|
-
providerId: string;
|
|
6
|
-
model: string;
|
|
7
|
-
}
|
|
8
|
-
export interface ProjectionFingerprint {
|
|
9
|
-
toolCatalogHash: string;
|
|
10
|
-
modelId: string;
|
|
11
|
-
cwdDigest: string;
|
|
12
|
-
compactionSource: "local" | "server" | "none";
|
|
13
|
-
}
|
|
14
|
-
export interface Projection {
|
|
15
|
-
version: 1;
|
|
16
|
-
kind: "projection";
|
|
17
|
-
cwd: string;
|
|
18
|
-
providerId: string;
|
|
19
|
-
model: string;
|
|
20
|
-
fingerprint: ProjectionFingerprint;
|
|
21
|
-
messages: ChatMessage[];
|
|
22
|
-
activeFiles: ActiveFile[];
|
|
23
|
-
lastFactSeq: number;
|
|
24
|
-
}
|
|
25
|
-
export interface ResumeState {
|
|
26
|
-
projection: Projection;
|
|
27
|
-
messages: ChatMessage[];
|
|
28
|
-
meta: SessionMeta;
|
|
29
|
-
warnings: string[];
|
|
30
|
-
rebuilt: boolean;
|
|
31
|
-
}
|
|
32
|
-
export declare function unpairedToolNotice(tool: string): string;
|
|
33
|
-
export declare function cwdDigest(cwd: string): string;
|
|
34
|
-
export declare function projectionFingerprint(meta: SessionMeta, compactionSource?: ProjectionFingerprint["compactionSource"]): ProjectionFingerprint;
|
|
35
|
-
export declare function sameFingerprint(a: ProjectionFingerprint | undefined, b: ProjectionFingerprint): boolean;
|
|
36
|
-
export declare function sessionMetaFromFacts(facts: any[]): SessionMeta;
|
|
37
|
-
export declare function sessionMetaFromFile(file: string): SessionMeta;
|
|
38
|
-
export declare function factsToMessages(facts: any[]): ChatMessage[];
|
|
39
|
-
export declare function compactionSourceFromFacts(facts: any[]): ProjectionFingerprint["compactionSource"];
|
|
40
|
-
export declare function buildProjection(facts: any[], metaOverride?: Partial<SessionMeta>): Projection;
|
|
41
|
-
export declare function readProjection(file: string): Projection | undefined;
|
|
42
|
-
export declare function writeProjection(file: string, projection: Projection): void;
|
|
43
|
-
export declare function resumeSession(file: string, currentCwd?: string): ResumeState;
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { formatToolResults } from "../agent/protocol.js";
|
|
4
|
-
import { toolCatalogHash } from "../agent/registry.js";
|
|
5
|
-
import { projectionPath, readSession } from "./log.js";
|
|
6
|
-
const READ_ONLY_TOOLS = new Set(["read_file", "read_many", "grep", "list_files", "stat_path", "pwd"]);
|
|
7
|
-
const IDEMPOTENT_TOOLS = new Set(["write_file"]);
|
|
8
|
-
const MAX_MESSAGE_CHARS = 4000;
|
|
9
|
-
const MAX_PROJECTION_BYTES = 19_000;
|
|
10
|
-
export function unpairedToolNotice(tool) {
|
|
11
|
-
if (READ_ONLY_TOOLS.has(tool))
|
|
12
|
-
return "执行中断(只读工具,可安全重试)。";
|
|
13
|
-
if (IDEMPOTENT_TOOLS.has(tool))
|
|
14
|
-
return "执行中断,可安全重试(write_file 为全量写)。";
|
|
15
|
-
return "执行状态未知:进程中断时命令可能已完成。请先验证再操作(stat / git status / git diff),不要直接重做。";
|
|
16
|
-
}
|
|
17
|
-
export function cwdDigest(cwd) {
|
|
18
|
-
return createHash("sha1").update(cwd).digest("hex").slice(0, 8);
|
|
19
|
-
}
|
|
20
|
-
export function projectionFingerprint(meta, compactionSource = "none") {
|
|
21
|
-
return {
|
|
22
|
-
toolCatalogHash: toolCatalogHash(),
|
|
23
|
-
modelId: `${meta.providerId}::${meta.model}`,
|
|
24
|
-
cwdDigest: cwdDigest(meta.cwd),
|
|
25
|
-
compactionSource,
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
export function sameFingerprint(a, b) {
|
|
29
|
-
return Boolean(a) && a.toolCatalogHash === b.toolCatalogHash && a.modelId === b.modelId &&
|
|
30
|
-
a.cwdDigest === b.cwdDigest && a.compactionSource === b.compactionSource;
|
|
31
|
-
}
|
|
32
|
-
export function sessionMetaFromFacts(facts) {
|
|
33
|
-
let cwd = "";
|
|
34
|
-
let providerId = "";
|
|
35
|
-
let model = "";
|
|
36
|
-
for (const fact of facts) {
|
|
37
|
-
if (fact?.type === "session_start" || fact?.type === "checkpoint") {
|
|
38
|
-
if (typeof fact.cwd === "string" && fact.cwd)
|
|
39
|
-
cwd = fact.cwd;
|
|
40
|
-
if (typeof fact.providerId === "string" && fact.providerId)
|
|
41
|
-
providerId = fact.providerId;
|
|
42
|
-
if (typeof fact.model === "string" && fact.model)
|
|
43
|
-
model = fact.model;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return { cwd, providerId, model };
|
|
47
|
-
}
|
|
48
|
-
export function sessionMetaFromFile(file) {
|
|
49
|
-
return sessionMetaFromFacts(readSession(file));
|
|
50
|
-
}
|
|
51
|
-
function legacyMessages(facts) {
|
|
52
|
-
return facts
|
|
53
|
-
.filter((fact) => fact?.type === "message" && typeof fact.content === "string")
|
|
54
|
-
.map((fact) => ({ role: fact.role, content: fact.content }));
|
|
55
|
-
}
|
|
56
|
-
export function factsToMessages(facts) {
|
|
57
|
-
const calls = new Map();
|
|
58
|
-
const pending = [];
|
|
59
|
-
const messages = [];
|
|
60
|
-
let nativeTurn = false;
|
|
61
|
-
const flush = () => {
|
|
62
|
-
for (const [id, call] of calls) {
|
|
63
|
-
const output = unpairedToolNotice(String(call?.tool ?? "unknown"));
|
|
64
|
-
if (nativeTurn)
|
|
65
|
-
messages.push({ role: "tool", toolCallId: id, content: output });
|
|
66
|
-
else
|
|
67
|
-
pending.push({ call, ok: false, output });
|
|
68
|
-
}
|
|
69
|
-
calls.clear();
|
|
70
|
-
if (pending.length > 0)
|
|
71
|
-
messages.push({ role: "user", content: formatToolResults(pending.splice(0)) });
|
|
72
|
-
};
|
|
73
|
-
for (const fact of facts) {
|
|
74
|
-
if (!fact)
|
|
75
|
-
continue;
|
|
76
|
-
if (fact.type === "user") {
|
|
77
|
-
flush();
|
|
78
|
-
nativeTurn = false;
|
|
79
|
-
if (typeof fact.content === "string" && fact.content)
|
|
80
|
-
messages.push({ role: "user", content: fact.content });
|
|
81
|
-
}
|
|
82
|
-
else if (fact.type === "assistant_raw") {
|
|
83
|
-
flush();
|
|
84
|
-
const nativeCalls = Array.isArray(fact.toolCalls) ? fact.toolCalls : undefined;
|
|
85
|
-
nativeTurn = Boolean(nativeCalls && nativeCalls.length > 0);
|
|
86
|
-
if (typeof fact.content === "string")
|
|
87
|
-
messages.push({ role: "assistant", content: fact.content, ...(nativeTurn ? { toolCalls: nativeCalls } : {}) });
|
|
88
|
-
}
|
|
89
|
-
else if (fact.type === "tool_call") {
|
|
90
|
-
calls.set(String(fact.id ?? ""), fact.args ?? { tool: fact.tool });
|
|
91
|
-
}
|
|
92
|
-
else if (fact.type === "tool_result") {
|
|
93
|
-
const id = String(fact.id ?? "");
|
|
94
|
-
const call = calls.get(id) ?? { tool: fact.tool ?? "unknown" };
|
|
95
|
-
calls.delete(id);
|
|
96
|
-
if (nativeTurn) {
|
|
97
|
-
messages.push({ role: "tool", toolCallId: id, content: String(fact.output ?? "") });
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
pending.push({
|
|
101
|
-
call,
|
|
102
|
-
ok: Boolean(fact.ok),
|
|
103
|
-
output: String(fact.output ?? ""),
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
flush();
|
|
109
|
-
if (messages.length === 0)
|
|
110
|
-
return legacyMessages(facts);
|
|
111
|
-
return messages;
|
|
112
|
-
}
|
|
113
|
-
export function compactionSourceFromFacts(facts) {
|
|
114
|
-
let source = "none";
|
|
115
|
-
for (const fact of facts) {
|
|
116
|
-
if (fact?.type === "compacted" && (fact.source === "local" || fact.source === "server"))
|
|
117
|
-
source = fact.source;
|
|
118
|
-
}
|
|
119
|
-
return source;
|
|
120
|
-
}
|
|
121
|
-
function latestCompaction(facts) {
|
|
122
|
-
for (let i = facts.length - 1; i >= 0; i -= 1) {
|
|
123
|
-
if (facts[i]?.type === "compacted")
|
|
124
|
-
return facts[i];
|
|
125
|
-
}
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
|
-
function applySummary(messages, facts) {
|
|
129
|
-
const latest = latestCompaction(facts);
|
|
130
|
-
if (!latest || latest.source !== "local" || typeof latest.summaryRef !== "string")
|
|
131
|
-
return messages;
|
|
132
|
-
try {
|
|
133
|
-
const summary = readFileSync(latest.summaryRef, "utf8");
|
|
134
|
-
const keep = Number(latest.keepMessages);
|
|
135
|
-
const tail = Number.isFinite(keep) && keep > 0 ? messages.slice(-keep) : messages;
|
|
136
|
-
const first = messages[0]?.role === "user" ? messages[0] : undefined;
|
|
137
|
-
const marker = { role: "system", content: `[context summary]\n${summary}` };
|
|
138
|
-
if (first && tail[0] !== first)
|
|
139
|
-
return [first, marker, ...tail];
|
|
140
|
-
return [marker, ...tail];
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
return messages;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
export function buildProjection(facts, metaOverride) {
|
|
147
|
-
const detected = sessionMetaFromFacts(facts);
|
|
148
|
-
const meta = {
|
|
149
|
-
cwd: metaOverride?.cwd || detected.cwd || process.cwd(),
|
|
150
|
-
providerId: metaOverride?.providerId || detected.providerId,
|
|
151
|
-
model: metaOverride?.model || detected.model,
|
|
152
|
-
};
|
|
153
|
-
const messages = clampMessages(applySummary(factsToMessages(facts), facts));
|
|
154
|
-
const source = compactionSourceFromFacts(facts);
|
|
155
|
-
let activeFiles = [];
|
|
156
|
-
let lastFactSeq = 0;
|
|
157
|
-
for (const fact of facts) {
|
|
158
|
-
if (typeof fact?.seq === "number" && fact.seq > lastFactSeq)
|
|
159
|
-
lastFactSeq = fact.seq;
|
|
160
|
-
if (fact?.type === "checkpoint" && Array.isArray(fact.activeFiles))
|
|
161
|
-
activeFiles = fact.activeFiles;
|
|
162
|
-
}
|
|
163
|
-
const projection = {
|
|
164
|
-
version: 1,
|
|
165
|
-
kind: "projection",
|
|
166
|
-
cwd: meta.cwd,
|
|
167
|
-
providerId: meta.providerId,
|
|
168
|
-
model: meta.model,
|
|
169
|
-
fingerprint: projectionFingerprint(meta, source),
|
|
170
|
-
messages,
|
|
171
|
-
activeFiles: activeFiles.slice(0, 200),
|
|
172
|
-
lastFactSeq,
|
|
173
|
-
};
|
|
174
|
-
return projection;
|
|
175
|
-
}
|
|
176
|
-
function clampMessages(messages) {
|
|
177
|
-
const list = messages.map((message) => {
|
|
178
|
-
const content = message.content.length > MAX_MESSAGE_CHARS
|
|
179
|
-
? message.content.slice(0, MAX_MESSAGE_CHARS) + "\n[...truncated in projection...]"
|
|
180
|
-
: message.content;
|
|
181
|
-
return {
|
|
182
|
-
role: message.role,
|
|
183
|
-
content,
|
|
184
|
-
...(message.toolCalls ? { toolCalls: message.toolCalls } : {}),
|
|
185
|
-
...(message.toolCallId ? { toolCallId: message.toolCallId } : {}),
|
|
186
|
-
};
|
|
187
|
-
});
|
|
188
|
-
while (list.length > 1 && Buffer.byteLength(JSON.stringify(list), "utf8") > MAX_PROJECTION_BYTES) {
|
|
189
|
-
if (list.length > 2 && list[0].role === "user")
|
|
190
|
-
list.splice(1, 1);
|
|
191
|
-
else
|
|
192
|
-
list.shift();
|
|
193
|
-
}
|
|
194
|
-
return list;
|
|
195
|
-
}
|
|
196
|
-
export function readProjection(file) {
|
|
197
|
-
try {
|
|
198
|
-
const raw = JSON.parse(readFileSync(projectionPath(file), "utf8"));
|
|
199
|
-
if (!raw || raw.version !== 1 || !Array.isArray(raw.messages) || !raw.fingerprint)
|
|
200
|
-
return undefined;
|
|
201
|
-
return raw;
|
|
202
|
-
}
|
|
203
|
-
catch {
|
|
204
|
-
return undefined;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
export function writeProjection(file, projection) {
|
|
208
|
-
const target = projectionPath(file);
|
|
209
|
-
const tmp = target + "." + process.pid + ".tmp";
|
|
210
|
-
try {
|
|
211
|
-
writeFileSync(tmp, JSON.stringify(projection, null, 2), "utf8");
|
|
212
|
-
renameSync(tmp, target);
|
|
213
|
-
}
|
|
214
|
-
catch (err) {
|
|
215
|
-
try {
|
|
216
|
-
rmSync(tmp, { force: true });
|
|
217
|
-
}
|
|
218
|
-
catch {
|
|
219
|
-
// best effort
|
|
220
|
-
}
|
|
221
|
-
throw err;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
export function resumeSession(file, currentCwd = process.cwd()) {
|
|
225
|
-
const facts = readSession(file);
|
|
226
|
-
const detected = sessionMetaFromFacts(facts);
|
|
227
|
-
const meta = {
|
|
228
|
-
cwd: detected.cwd || currentCwd,
|
|
229
|
-
providerId: detected.providerId,
|
|
230
|
-
model: detected.model,
|
|
231
|
-
};
|
|
232
|
-
const expected = projectionFingerprint(meta, compactionSourceFromFacts(facts));
|
|
233
|
-
const stored = readProjection(file);
|
|
234
|
-
const warnings = [];
|
|
235
|
-
let projection;
|
|
236
|
-
let rebuilt = false;
|
|
237
|
-
if (stored && sameFingerprint(stored.fingerprint, expected)) {
|
|
238
|
-
projection = stored;
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
projection = buildProjection(facts, meta);
|
|
242
|
-
rebuilt = true;
|
|
243
|
-
if (stored)
|
|
244
|
-
warnings.push("Projection fingerprint changed (tools/model/cwd); rebuilt from facts.");
|
|
245
|
-
else
|
|
246
|
-
warnings.push("No projection found; rebuilt from facts.");
|
|
247
|
-
try {
|
|
248
|
-
writeProjection(file, projection);
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
warnings.push("Could not write the projection (filesystem is read-only?); using the in-memory rebuild.");
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (detected.cwd && detected.cwd !== currentCwd)
|
|
255
|
-
warnings.push(`Session was recorded in ${detected.cwd}; current directory is ${currentCwd}.`);
|
|
256
|
-
return { projection, messages: projection.messages, meta, warnings, rebuilt };
|
|
257
|
-
}
|
package/dist/ui/Picker.d.ts
DELETED
package/dist/ui/Picker.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* Presentational picker used by /model, /provider, and /effort.
|
|
4
|
-
*
|
|
5
|
-
* Keyboard handling lives in App so it can suspend the normal prompt input and
|
|
6
|
-
* so every picker behaves the same. This component only renders the window of
|
|
7
|
-
* items around the active index.
|
|
8
|
-
*/
|
|
9
|
-
import { Box, Text } from "ink";
|
|
10
|
-
const WINDOW = 10;
|
|
11
|
-
export const Picker = ({ theme, title, items, index, current, hint }) => {
|
|
12
|
-
const max = Math.max(0, items.length - 1);
|
|
13
|
-
const active = Math.max(0, Math.min(index, max));
|
|
14
|
-
const start = Math.max(0, Math.min(active - Math.floor(WINDOW / 2), Math.max(0, items.length - WINDOW)));
|
|
15
|
-
const visible = items.slice(start, start + WINDOW);
|
|
16
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [_jsx(Text, { color: theme.accent, bold: true, children: title }), ...visible.map((item, offset) => {
|
|
17
|
-
const itemIndex = start + offset;
|
|
18
|
-
const selected = itemIndex === active;
|
|
19
|
-
const marker = selected ? "\u276F" : " ";
|
|
20
|
-
const number = itemIndex < 9 ? String(itemIndex + 1) : " ";
|
|
21
|
-
const isCurrent = current !== undefined && item.value === current;
|
|
22
|
-
return (_jsxs(Text, { color: selected ? theme.accent : theme.muted, children: [marker, " ", number, ". ", isCurrent ? "\u25CF " : " ", item.label, item.hint ? _jsxs(Text, { color: theme.muted, children: [" ", item.hint] }) : null] }, item.value));
|
|
23
|
-
}), items.length > WINDOW ? (_jsx(Text, { color: theme.muted, children: ` … ${start + 1}-${start + visible.length} of ${items.length}` })) : null, _jsx(Text, { color: theme.muted, children: hint ?? " \u2191/\u2193 select \u00B7 1-9 jump \u00B7 Enter confirm \u00B7 Esc cancel" })] }));
|
|
24
|
-
};
|