kimiflare 0.24.0 → 0.26.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/dist/index.js +1883 -243
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18,8 +18,7 @@ function configPath() {
|
|
|
18
18
|
}
|
|
19
19
|
function readReasoningEffortEnv() {
|
|
20
20
|
const raw = process.env.KIMI_REASONING_EFFORT?.toLowerCase();
|
|
21
|
-
|
|
22
|
-
return void 0;
|
|
21
|
+
return EFFORTS.includes(raw ?? "") ? raw : void 0;
|
|
23
22
|
}
|
|
24
23
|
function readCoauthorEnv() {
|
|
25
24
|
const enabled = process.env.KIMIFLARE_COAUTHOR;
|
|
@@ -84,6 +83,7 @@ async function loadConfig() {
|
|
|
84
83
|
const envMemoryMaxAgeDays = readNumberEnv("KIMIFLARE_MEMORY_MAX_AGE_DAYS");
|
|
85
84
|
const envMemoryMaxEntries = readNumberEnv("KIMIFLARE_MEMORY_MAX_ENTRIES");
|
|
86
85
|
const envMemoryEmbeddingModel = process.env.KIMIFLARE_MEMORY_EMBEDDING_MODEL;
|
|
86
|
+
const envPlumbingModel = process.env.KIMIFLARE_PLUMBING_MODEL;
|
|
87
87
|
const envCodeMode = readBooleanEnv("KIMIFLARE_CODE_MODE");
|
|
88
88
|
if (envAccount && envToken) {
|
|
89
89
|
return {
|
|
@@ -108,6 +108,7 @@ async function loadConfig() {
|
|
|
108
108
|
memoryMaxAgeDays: envMemoryMaxAgeDays,
|
|
109
109
|
memoryMaxEntries: envMemoryMaxEntries,
|
|
110
110
|
memoryEmbeddingModel: envMemoryEmbeddingModel,
|
|
111
|
+
plumbingModel: envPlumbingModel,
|
|
111
112
|
codeMode: envCodeMode
|
|
112
113
|
};
|
|
113
114
|
}
|
|
@@ -138,6 +139,7 @@ async function loadConfig() {
|
|
|
138
139
|
memoryMaxAgeDays: envMemoryMaxAgeDays ?? parsed.memoryMaxAgeDays,
|
|
139
140
|
memoryMaxEntries: envMemoryMaxEntries ?? parsed.memoryMaxEntries,
|
|
140
141
|
memoryEmbeddingModel: envMemoryEmbeddingModel ?? parsed.memoryEmbeddingModel,
|
|
142
|
+
plumbingModel: envPlumbingModel ?? parsed.plumbingModel,
|
|
141
143
|
codeMode: envCodeMode ?? parsed.codeMode
|
|
142
144
|
};
|
|
143
145
|
}
|
|
@@ -152,10 +154,11 @@ async function saveConfig(cfg) {
|
|
|
152
154
|
await chmod(p, 384);
|
|
153
155
|
return p;
|
|
154
156
|
}
|
|
155
|
-
var DEFAULT_MODEL, DEFAULT_REASONING_EFFORT;
|
|
157
|
+
var EFFORTS, DEFAULT_MODEL, DEFAULT_REASONING_EFFORT;
|
|
156
158
|
var init_config = __esm({
|
|
157
159
|
"src/config.ts"() {
|
|
158
160
|
"use strict";
|
|
161
|
+
EFFORTS = ["low", "medium", "high"];
|
|
159
162
|
DEFAULT_MODEL = "@cf/moonshotai/kimi-k2.6";
|
|
160
163
|
DEFAULT_REASONING_EFFORT = "medium";
|
|
161
164
|
}
|
|
@@ -173,10 +176,10 @@ async function* readSSE(stream, signal) {
|
|
|
173
176
|
if (done) break;
|
|
174
177
|
buffer += decoder.decode(value, { stream: true });
|
|
175
178
|
buffer = buffer.replace(/\r\n/g, "\n");
|
|
176
|
-
let
|
|
177
|
-
while ((
|
|
178
|
-
const event = buffer.slice(0,
|
|
179
|
-
buffer = buffer.slice(
|
|
179
|
+
let sep3;
|
|
180
|
+
while ((sep3 = buffer.indexOf("\n\n")) !== -1) {
|
|
181
|
+
const event = buffer.slice(0, sep3);
|
|
182
|
+
buffer = buffer.slice(sep3 + 2);
|
|
180
183
|
const data = extractData(event);
|
|
181
184
|
if (data !== null) yield data;
|
|
182
185
|
}
|
|
@@ -209,8 +212,8 @@ var init_errors = __esm({
|
|
|
209
212
|
"src/util/errors.ts"() {
|
|
210
213
|
"use strict";
|
|
211
214
|
KimiApiError = class extends Error {
|
|
212
|
-
constructor(
|
|
213
|
-
super(
|
|
215
|
+
constructor(message2, code, httpStatus) {
|
|
216
|
+
super(message2);
|
|
214
217
|
this.code = code;
|
|
215
218
|
this.httpStatus = httpStatus;
|
|
216
219
|
this.name = "KimiApiError";
|
|
@@ -880,7 +883,7 @@ function schemaToTsType(prop, required = true) {
|
|
|
880
883
|
types.push(`${itemType}[]`);
|
|
881
884
|
} else if (prop.type === "object") {
|
|
882
885
|
if (prop.properties && Object.keys(prop.properties).length > 0) {
|
|
883
|
-
const entries = Object.entries(prop.properties).map(([key, val]) => {
|
|
886
|
+
const entries = Object.entries(prop.properties).sort(([a], [b]) => a.localeCompare(b)).map(([key, val]) => {
|
|
884
887
|
const isReq = prop.required?.includes(key) ?? false;
|
|
885
888
|
return ` ${key}${isReq ? "" : "?"}: ${schemaToTsType(val, isReq)};`;
|
|
886
889
|
}).join("\n");
|
|
@@ -901,7 +904,7 @@ ${entries}
|
|
|
901
904
|
return required ? result : `${result} | undefined`;
|
|
902
905
|
}
|
|
903
906
|
function generateInterface(name, properties, required = []) {
|
|
904
|
-
const entries = Object.entries(properties).map(([key, val]) => {
|
|
907
|
+
const entries = Object.entries(properties).sort(([a], [b]) => a.localeCompare(b)).map(([key, val]) => {
|
|
905
908
|
const isReq = required.includes(key);
|
|
906
909
|
return ` ${key}${isReq ? "" : "?"}: ${schemaToTsType(val, isReq)};`;
|
|
907
910
|
}).join("\n");
|
|
@@ -921,13 +924,14 @@ function generateTypeScriptApi(tools) {
|
|
|
921
924
|
const inputInterfaces = [];
|
|
922
925
|
const outputInterfaces = [];
|
|
923
926
|
const methodEntries = [];
|
|
924
|
-
for (const tool of tools) {
|
|
927
|
+
for (const tool of [...tools].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
925
928
|
const baseName = sanitizeTypeName(tool.name);
|
|
926
929
|
const inputName = `${baseName}_Input`;
|
|
927
930
|
const outputName = `${baseName}_Output`;
|
|
928
931
|
const params = tool.parameters;
|
|
929
|
-
|
|
930
|
-
|
|
932
|
+
const sortedPropKeys = params.properties ? Object.keys(params.properties).sort((a, b) => a.localeCompare(b)) : [];
|
|
933
|
+
if (sortedPropKeys.length > 0 && params.properties) {
|
|
934
|
+
inputInterfaces.push(generateInterface(inputName, params.properties, [...params.required ?? []].sort((a, b) => a.localeCompare(b))));
|
|
931
935
|
inputInterfaces.push("");
|
|
932
936
|
methodEntries.push(` /**`);
|
|
933
937
|
methodEntries.push(` * ${tool.description.replace(/\n/g, "\n * ")}`);
|
|
@@ -1027,8 +1031,8 @@ ${jsCode}
|
|
|
1027
1031
|
await script.run(context, { timeout, release: true });
|
|
1028
1032
|
await new Promise((r) => setTimeout(r, 10));
|
|
1029
1033
|
} catch (err) {
|
|
1030
|
-
const
|
|
1031
|
-
return { output: "", logs, error:
|
|
1034
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
1035
|
+
return { output: "", logs, error: message2, toolCalls };
|
|
1032
1036
|
} finally {
|
|
1033
1037
|
isolate.dispose();
|
|
1034
1038
|
}
|
|
@@ -1110,8 +1114,8 @@ ${jsCode}
|
|
|
1110
1114
|
await runInNewContext(wrapped, sandbox, { timeout, displayErrors: true });
|
|
1111
1115
|
await new Promise((r) => setTimeout(r, 10));
|
|
1112
1116
|
} catch (err) {
|
|
1113
|
-
const
|
|
1114
|
-
return { output: "", logs, error:
|
|
1117
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
1118
|
+
return { output: "", logs, error: message2, toolCalls };
|
|
1115
1119
|
}
|
|
1116
1120
|
return { output: logs.join("\n"), logs, toolCalls };
|
|
1117
1121
|
}
|
|
@@ -1119,8 +1123,8 @@ async function runInSandbox(opts2) {
|
|
|
1119
1123
|
try {
|
|
1120
1124
|
return await runWithIsolatedVm(opts2);
|
|
1121
1125
|
} catch (err) {
|
|
1122
|
-
const
|
|
1123
|
-
if (
|
|
1126
|
+
const message2 = err instanceof Error ? err.message : String(err);
|
|
1127
|
+
if (message2.includes("isolated-vm") || message2.includes("Cannot find module") || message2.includes("bindings")) {
|
|
1124
1128
|
return runWithNodeVm(opts2);
|
|
1125
1129
|
}
|
|
1126
1130
|
return runWithNodeVm(opts2);
|
|
@@ -1148,7 +1152,14 @@ async function runAgentTurn(opts2) {
|
|
|
1148
1152
|
let toolDefs;
|
|
1149
1153
|
let codeModeApiString = "";
|
|
1150
1154
|
if (codeMode) {
|
|
1151
|
-
|
|
1155
|
+
const toolsKey = stableStringify(opts2.tools);
|
|
1156
|
+
const cached = codeModeApiCache.get(toolsKey);
|
|
1157
|
+
if (cached) {
|
|
1158
|
+
codeModeApiString = cached;
|
|
1159
|
+
} else {
|
|
1160
|
+
codeModeApiString = generateTypeScriptApi(opts2.tools);
|
|
1161
|
+
codeModeApiCache.set(toolsKey, codeModeApiString);
|
|
1162
|
+
}
|
|
1152
1163
|
toolDefs = [
|
|
1153
1164
|
{
|
|
1154
1165
|
type: "function",
|
|
@@ -1183,6 +1194,9 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
1183
1194
|
}
|
|
1184
1195
|
let turn = 0;
|
|
1185
1196
|
let lastUsage = null;
|
|
1197
|
+
const recentToolCalls = [];
|
|
1198
|
+
const LOOP_WINDOW = 8;
|
|
1199
|
+
const LOOP_THRESHOLD = 2;
|
|
1186
1200
|
for (let iter = 0; iter < max; iter++) {
|
|
1187
1201
|
turn++;
|
|
1188
1202
|
const previousMessages = opts2.messages.slice();
|
|
@@ -1313,6 +1327,28 @@ Use console.log() to return results. Only console.log output will be sent back t
|
|
|
1313
1327
|
}
|
|
1314
1328
|
for (const tc of toolCalls) {
|
|
1315
1329
|
if (opts2.signal.aborted) throw new DOMException("aborted", "AbortError");
|
|
1330
|
+
const loopSignature = `${tc.function.name}:${stableStringify(tc.function.arguments)}`;
|
|
1331
|
+
const loopCount = recentToolCalls.filter((s) => s === loopSignature).length;
|
|
1332
|
+
if (loopCount >= LOOP_THRESHOLD) {
|
|
1333
|
+
const warning = `Loop detected: you have called ${tc.function.name} with the same arguments multiple times in a row. Consider a different approach.`;
|
|
1334
|
+
const loopResult = {
|
|
1335
|
+
tool_call_id: tc.id,
|
|
1336
|
+
name: tc.function.name,
|
|
1337
|
+
content: warning,
|
|
1338
|
+
ok: false
|
|
1339
|
+
};
|
|
1340
|
+
toolResults.push(loopResult);
|
|
1341
|
+
opts2.messages.push({
|
|
1342
|
+
role: "tool",
|
|
1343
|
+
tool_call_id: tc.id,
|
|
1344
|
+
content: sanitizeString(warning),
|
|
1345
|
+
name: tc.function.name
|
|
1346
|
+
});
|
|
1347
|
+
opts2.callbacks.onToolResult?.(loopResult);
|
|
1348
|
+
recentToolCalls.push(loopSignature);
|
|
1349
|
+
if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
|
|
1350
|
+
continue;
|
|
1351
|
+
}
|
|
1316
1352
|
if (codeMode && tc.function.name === "execute_code") {
|
|
1317
1353
|
const args = JSON.parse(tc.function.arguments || "{}");
|
|
1318
1354
|
const code = args.code || "";
|
|
@@ -1353,6 +1389,8 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
1353
1389
|
name: "execute_code"
|
|
1354
1390
|
});
|
|
1355
1391
|
opts2.callbacks.onToolResult?.(result);
|
|
1392
|
+
recentToolCalls.push(loopSignature);
|
|
1393
|
+
if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
|
|
1356
1394
|
} else {
|
|
1357
1395
|
const result = await opts2.executor.run(
|
|
1358
1396
|
{ id: tc.id, name: tc.function.name, arguments: tc.function.arguments },
|
|
@@ -1367,6 +1405,8 @@ ${sandboxResult.output}` : sandboxResult.output;
|
|
|
1367
1405
|
name: result.name
|
|
1368
1406
|
});
|
|
1369
1407
|
opts2.callbacks.onToolResult?.(result);
|
|
1408
|
+
recentToolCalls.push(loopSignature);
|
|
1409
|
+
if (recentToolCalls.length > LOOP_WINDOW) recentToolCalls.shift();
|
|
1370
1410
|
}
|
|
1371
1411
|
}
|
|
1372
1412
|
if (opts2.sessionId && lastUsage) {
|
|
@@ -1392,6 +1432,7 @@ function validateToolArguments(raw) {
|
|
|
1392
1432
|
return "{}";
|
|
1393
1433
|
}
|
|
1394
1434
|
}
|
|
1435
|
+
var codeModeApiCache;
|
|
1395
1436
|
var init_loop = __esm({
|
|
1396
1437
|
"src/agent/loop.ts"() {
|
|
1397
1438
|
"use strict";
|
|
@@ -1401,6 +1442,7 @@ var init_loop = __esm({
|
|
|
1401
1442
|
init_cost_debug();
|
|
1402
1443
|
init_strip_reasoning();
|
|
1403
1444
|
init_code_mode();
|
|
1445
|
+
codeModeApiCache = /* @__PURE__ */ new Map();
|
|
1404
1446
|
}
|
|
1405
1447
|
});
|
|
1406
1448
|
|
|
@@ -1711,6 +1753,9 @@ function resolvePath(cwd, input) {
|
|
|
1711
1753
|
}
|
|
1712
1754
|
return isAbsolute(input) ? input : resolve(cwd, input);
|
|
1713
1755
|
}
|
|
1756
|
+
function isPathOutside(relPath) {
|
|
1757
|
+
return relPath === ".." || relPath.startsWith(`..${sep}`) || isAbsolute(relPath);
|
|
1758
|
+
}
|
|
1714
1759
|
function collapsePath(input, cwd, maxLen = 40) {
|
|
1715
1760
|
if (!input) return input;
|
|
1716
1761
|
let abs;
|
|
@@ -2839,6 +2884,16 @@ var init_expand_artifact = __esm({
|
|
|
2839
2884
|
});
|
|
2840
2885
|
|
|
2841
2886
|
// src/tools/executor.ts
|
|
2887
|
+
function isDiffCommand(cmd) {
|
|
2888
|
+
const trimmed = cmd.trim();
|
|
2889
|
+
if (/^git\s+show(?:\s|$)/.test(trimmed)) return true;
|
|
2890
|
+
if (/^git\s+diff(?:\s|$)/.test(trimmed)) return true;
|
|
2891
|
+
if (/^git\s+format-patch(?:\s|$)/.test(trimmed)) return true;
|
|
2892
|
+
const hasPatchFlag = /(?:^|\s)(?:-p|--patch)(?:\s|$)/.test(trimmed);
|
|
2893
|
+
if (/^git\s+log(?:\s|$)/.test(trimmed) && hasPatchFlag) return true;
|
|
2894
|
+
if (/^git\s+stash\s+show(?:\s|$)/.test(trimmed) && hasPatchFlag) return true;
|
|
2895
|
+
return false;
|
|
2896
|
+
}
|
|
2842
2897
|
function normalizeToolOutput(result) {
|
|
2843
2898
|
if (typeof result === "string") {
|
|
2844
2899
|
const bytes = Buffer.byteLength(result, "utf8");
|
|
@@ -2941,6 +2996,20 @@ var init_executor = __esm({
|
|
|
2941
2996
|
try {
|
|
2942
2997
|
const result = await tool.run(args, ctx);
|
|
2943
2998
|
const normalized = normalizeToolOutput(result);
|
|
2999
|
+
const cmd = call.name === "bash" && typeof args.command === "string" ? args.command : "";
|
|
3000
|
+
if (isDiffCommand(cmd)) {
|
|
3001
|
+
const artifactId = this.artifactStore.store(normalized.content);
|
|
3002
|
+
const bytes = Buffer.byteLength(normalized.content, "utf8");
|
|
3003
|
+
return {
|
|
3004
|
+
tool_call_id: call.id,
|
|
3005
|
+
name: call.name,
|
|
3006
|
+
content: normalized.content,
|
|
3007
|
+
ok: true,
|
|
3008
|
+
rawBytes: bytes,
|
|
3009
|
+
reducedBytes: bytes,
|
|
3010
|
+
artifactId
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
2944
3013
|
const reduced = reduceToolOutput(
|
|
2945
3014
|
call.name,
|
|
2946
3015
|
normalized.content,
|
|
@@ -3087,13 +3156,17 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
3087
3156
|
import { dirname as dirname3, join as join7 } from "path";
|
|
3088
3157
|
function getAppVersion() {
|
|
3089
3158
|
if (cachedVersion !== null) return cachedVersion;
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3159
|
+
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
3160
|
+
const candidates = [join7(here, "..", "..", "package.json"), join7(here, "..", "package.json")];
|
|
3161
|
+
for (const path of candidates) {
|
|
3162
|
+
try {
|
|
3163
|
+
const pkg = JSON.parse(readFileSync2(path, "utf8"));
|
|
3164
|
+
cachedVersion = pkg.version ?? "0.0.0";
|
|
3165
|
+
return cachedVersion;
|
|
3166
|
+
} catch {
|
|
3167
|
+
}
|
|
3096
3168
|
}
|
|
3169
|
+
cachedVersion = "0.0.0";
|
|
3097
3170
|
return cachedVersion;
|
|
3098
3171
|
}
|
|
3099
3172
|
var cachedVersion;
|
|
@@ -3205,6 +3278,39 @@ function emptySessionState(task = "") {
|
|
|
3205
3278
|
artifact_index: {}
|
|
3206
3279
|
};
|
|
3207
3280
|
}
|
|
3281
|
+
function serializeArtifactStore(store) {
|
|
3282
|
+
const MAX_ARTIFACT_CHARS = 5e4;
|
|
3283
|
+
const out = [];
|
|
3284
|
+
for (const a of store.list()) {
|
|
3285
|
+
out.push({
|
|
3286
|
+
id: a.id,
|
|
3287
|
+
type: a.type,
|
|
3288
|
+
summary: a.summary,
|
|
3289
|
+
raw: a.raw.slice(0, MAX_ARTIFACT_CHARS),
|
|
3290
|
+
source: a.source,
|
|
3291
|
+
path: a.path,
|
|
3292
|
+
lineRange: a.lineRange,
|
|
3293
|
+
ts: a.ts
|
|
3294
|
+
});
|
|
3295
|
+
}
|
|
3296
|
+
return out;
|
|
3297
|
+
}
|
|
3298
|
+
function deserializeArtifactStore(data) {
|
|
3299
|
+
const store = new ArtifactStore();
|
|
3300
|
+
for (const a of data) {
|
|
3301
|
+
store.add({
|
|
3302
|
+
id: a.id,
|
|
3303
|
+
type: a.type,
|
|
3304
|
+
summary: a.summary,
|
|
3305
|
+
raw: a.raw,
|
|
3306
|
+
source: a.source,
|
|
3307
|
+
path: a.path,
|
|
3308
|
+
lineRange: a.lineRange,
|
|
3309
|
+
ts: a.ts
|
|
3310
|
+
});
|
|
3311
|
+
}
|
|
3312
|
+
return store;
|
|
3313
|
+
}
|
|
3208
3314
|
function formatRecalledArtifacts(recalled) {
|
|
3209
3315
|
if (recalled.length === 0) return "";
|
|
3210
3316
|
const lines = ["[recalled artifacts]"];
|
|
@@ -3577,11 +3683,12 @@ function recallArtifacts(messages, store, state) {
|
|
|
3577
3683
|
}
|
|
3578
3684
|
for (const failure of state.recent_failures) {
|
|
3579
3685
|
const keyword = failure.split(":")[0];
|
|
3580
|
-
if (keyword
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3686
|
+
if (!keyword) continue;
|
|
3687
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
3688
|
+
if (!text.toLowerCase().includes(lowerKeyword)) continue;
|
|
3689
|
+
for (const [id, meta] of Object.entries(state.artifact_index)) {
|
|
3690
|
+
if (meta.source === "bash" && !ids.includes(id) && meta.summary.toLowerCase().includes(lowerKeyword)) {
|
|
3691
|
+
ids.push(id);
|
|
3585
3692
|
}
|
|
3586
3693
|
}
|
|
3587
3694
|
}
|
|
@@ -4069,6 +4176,12 @@ var init_chat = __esm({
|
|
|
4069
4176
|
evt.text
|
|
4070
4177
|
] });
|
|
4071
4178
|
}
|
|
4179
|
+
if (evt.kind === "memory") {
|
|
4180
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: theme.info.color, dimColor: theme.info.dim, children: [
|
|
4181
|
+
"\u25C8 ",
|
|
4182
|
+
evt.text
|
|
4183
|
+
] });
|
|
4184
|
+
}
|
|
4072
4185
|
return /* @__PURE__ */ jsxs4(Text4, { color: theme.error, children: [
|
|
4073
4186
|
"! ",
|
|
4074
4187
|
evt.text
|
|
@@ -4153,14 +4266,14 @@ function buildRightParts(usage, contextLimit, sessionUsage, gatewayMeta) {
|
|
|
4153
4266
|
parts.push(`in ${sessionUsage.promptTokens}${cached ? ` (${cached} cached)` : ""}`);
|
|
4154
4267
|
parts.push(`out ${sessionUsage.completionTokens}`);
|
|
4155
4268
|
parts.push(`ctx ${pct}%`);
|
|
4156
|
-
parts.push(
|
|
4269
|
+
parts.push(`$${sessionUsage.cost.toFixed(5)}`);
|
|
4157
4270
|
} else {
|
|
4158
4271
|
const cached = usage.prompt_tokens_details?.cached_tokens ?? 0;
|
|
4159
4272
|
const cost = calculateCost(usage.prompt_tokens, usage.completion_tokens, cached);
|
|
4160
4273
|
parts.push(`in ${usage.prompt_tokens}${cached ? ` (${cached} cached)` : ""}`);
|
|
4161
4274
|
parts.push(`out ${usage.completion_tokens}`);
|
|
4162
4275
|
parts.push(`ctx ${pct}%`);
|
|
4163
|
-
parts.push(
|
|
4276
|
+
parts.push(`$${cost.total.toFixed(5)}`);
|
|
4164
4277
|
}
|
|
4165
4278
|
const gatewayCache = formatGatewayCacheStatus(gatewayMeta);
|
|
4166
4279
|
if (gatewayCache) parts.push(gatewayCache);
|
|
@@ -4910,18 +5023,18 @@ var init_source = __esm({
|
|
|
4910
5023
|
}
|
|
4911
5024
|
}
|
|
4912
5025
|
});
|
|
4913
|
-
createStyler = (
|
|
5026
|
+
createStyler = (open3, close, parent) => {
|
|
4914
5027
|
let openAll;
|
|
4915
5028
|
let closeAll;
|
|
4916
5029
|
if (parent === void 0) {
|
|
4917
|
-
openAll =
|
|
5030
|
+
openAll = open3;
|
|
4918
5031
|
closeAll = close;
|
|
4919
5032
|
} else {
|
|
4920
|
-
openAll = parent.openAll +
|
|
5033
|
+
openAll = parent.openAll + open3;
|
|
4921
5034
|
closeAll = close + parent.closeAll;
|
|
4922
5035
|
}
|
|
4923
5036
|
return {
|
|
4924
|
-
open,
|
|
5037
|
+
open: open3,
|
|
4925
5038
|
close,
|
|
4926
5039
|
openAll,
|
|
4927
5040
|
closeAll,
|
|
@@ -5367,6 +5480,245 @@ var init_welcome = __esm({
|
|
|
5367
5480
|
}
|
|
5368
5481
|
});
|
|
5369
5482
|
|
|
5483
|
+
// src/ui/help-menu.tsx
|
|
5484
|
+
import { useState as useState6 } from "react";
|
|
5485
|
+
import { Box as Box12, Text as Text13, useInput as useInput2 } from "ink";
|
|
5486
|
+
import SelectInput4 from "ink-select-input";
|
|
5487
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
5488
|
+
function HelpMenu({ theme, themes, currentThemeName, customCommands, onDone, onCommand }) {
|
|
5489
|
+
const [page, setPage] = useState6("main");
|
|
5490
|
+
const customs = customCommands ?? [];
|
|
5491
|
+
useInput2((_input, key) => {
|
|
5492
|
+
if (key.escape) {
|
|
5493
|
+
if (page !== "main") {
|
|
5494
|
+
setPage("main");
|
|
5495
|
+
} else {
|
|
5496
|
+
onDone();
|
|
5497
|
+
}
|
|
5498
|
+
}
|
|
5499
|
+
});
|
|
5500
|
+
const handleSelect = (command) => {
|
|
5501
|
+
onCommand(command);
|
|
5502
|
+
onDone();
|
|
5503
|
+
};
|
|
5504
|
+
if (page === "main") {
|
|
5505
|
+
const items2 = CATEGORIES.map((cat) => ({
|
|
5506
|
+
label: cat.label,
|
|
5507
|
+
value: cat.key,
|
|
5508
|
+
key: cat.key
|
|
5509
|
+
}));
|
|
5510
|
+
if (customs.length > 0) {
|
|
5511
|
+
items2.push({ label: "Run custom commands", value: "custom", key: "custom" });
|
|
5512
|
+
}
|
|
5513
|
+
items2.push({ label: "(close)", value: "__close__", key: "__close__" });
|
|
5514
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
5515
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.accent, bold: true, children: "Help" }),
|
|
5516
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select, Esc to close." }),
|
|
5517
|
+
/* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(
|
|
5518
|
+
SelectInput4,
|
|
5519
|
+
{
|
|
5520
|
+
items: items2,
|
|
5521
|
+
onSelect: (item) => {
|
|
5522
|
+
if (item.value === "__close__") {
|
|
5523
|
+
onDone();
|
|
5524
|
+
} else {
|
|
5525
|
+
setPage(item.value);
|
|
5526
|
+
}
|
|
5527
|
+
}
|
|
5528
|
+
}
|
|
5529
|
+
) }),
|
|
5530
|
+
/* @__PURE__ */ jsx13(Box12, { marginTop: 1, flexDirection: "column", children: SINGLE_COMMANDS.map((cmd) => /* @__PURE__ */ jsx13(Text13, { color: theme.info.color, dimColor: false, children: ` ${cmd.command.padEnd(20)} ${cmd.description}` }, cmd.command)) }),
|
|
5531
|
+
/* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { color: theme.info.color, dimColor: false, children: "keys: ctrl-c interrupt/exit \xB7 ctrl-r toggle reasoning \xB7 ctrl-o verbose \xB7 ctrl+t theme \xB7 shift+tab cycle mode \xB7 \u2191/\u2193 history" }) })
|
|
5532
|
+
] });
|
|
5533
|
+
}
|
|
5534
|
+
if (page === "theme") {
|
|
5535
|
+
const items2 = themes.map((t) => ({
|
|
5536
|
+
label: t.name === currentThemeName ? `${t.label} \xB7 current` : t.label,
|
|
5537
|
+
value: t.name,
|
|
5538
|
+
key: t.name
|
|
5539
|
+
}));
|
|
5540
|
+
items2.push({ label: "\u2190 Back", value: "__back__", key: "__back__" });
|
|
5541
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
5542
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.accent, bold: true, children: "Theme" }),
|
|
5543
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to apply, Esc to go back." }),
|
|
5544
|
+
/* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(
|
|
5545
|
+
SelectInput4,
|
|
5546
|
+
{
|
|
5547
|
+
items: items2,
|
|
5548
|
+
onSelect: (item) => {
|
|
5549
|
+
if (item.value === "__back__") {
|
|
5550
|
+
setPage("main");
|
|
5551
|
+
} else {
|
|
5552
|
+
handleSelect(`/theme ${item.value}`);
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
}
|
|
5556
|
+
) })
|
|
5557
|
+
] });
|
|
5558
|
+
}
|
|
5559
|
+
if (page === "custom") {
|
|
5560
|
+
const items2 = customs.map((c) => ({
|
|
5561
|
+
label: `${`/${c.name}`.padEnd(28)} ${c.description ?? ""}`.trimEnd(),
|
|
5562
|
+
value: `/${c.name}`,
|
|
5563
|
+
key: c.name
|
|
5564
|
+
}));
|
|
5565
|
+
items2.push({ label: "\u2190 Back", value: "__back__", key: "__back__" });
|
|
5566
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
5567
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.accent, bold: true, children: "Custom commands" }),
|
|
5568
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.info.color, dimColor: false, children: customs.length === 0 ? "no custom commands found in .kimiflare/commands/" : "Arrow keys to navigate, Enter to run, Esc to go back." }),
|
|
5569
|
+
/* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(
|
|
5570
|
+
SelectInput4,
|
|
5571
|
+
{
|
|
5572
|
+
items: items2,
|
|
5573
|
+
onSelect: (item) => {
|
|
5574
|
+
if (item.value === "__back__") {
|
|
5575
|
+
setPage("main");
|
|
5576
|
+
} else {
|
|
5577
|
+
handleSelect(item.value);
|
|
5578
|
+
}
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5581
|
+
) })
|
|
5582
|
+
] });
|
|
5583
|
+
}
|
|
5584
|
+
const category = CATEGORIES.find((c) => c.key === page);
|
|
5585
|
+
const selectable = category.commands.filter((cmd) => cmd.selectable !== false);
|
|
5586
|
+
const staticCmds = category.commands.filter((cmd) => cmd.selectable === false);
|
|
5587
|
+
const items = selectable.map((cmd) => ({
|
|
5588
|
+
label: `${cmd.command.padEnd(28)} ${cmd.description}`,
|
|
5589
|
+
value: cmd.command,
|
|
5590
|
+
key: cmd.command
|
|
5591
|
+
}));
|
|
5592
|
+
items.push({ label: "\u2190 Back", value: "__back__", key: "__back__" });
|
|
5593
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
5594
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.accent, bold: true, children: category.label }),
|
|
5595
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to execute, Esc to go back." }),
|
|
5596
|
+
/* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(
|
|
5597
|
+
SelectInput4,
|
|
5598
|
+
{
|
|
5599
|
+
items,
|
|
5600
|
+
onSelect: (item) => {
|
|
5601
|
+
if (item.value === "__back__") {
|
|
5602
|
+
setPage("main");
|
|
5603
|
+
} else {
|
|
5604
|
+
handleSelect(item.value);
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5608
|
+
) }),
|
|
5609
|
+
staticCmds.length > 0 && /* @__PURE__ */ jsx13(Box12, { marginTop: 1, flexDirection: "column", children: staticCmds.map((cmd) => /* @__PURE__ */ jsx13(Text13, { color: theme.info.color, dimColor: true, children: ` ${cmd.command.padEnd(28)} ${cmd.description}` }, cmd.command)) })
|
|
5610
|
+
] });
|
|
5611
|
+
}
|
|
5612
|
+
var CATEGORIES, SINGLE_COMMANDS;
|
|
5613
|
+
var init_help_menu = __esm({
|
|
5614
|
+
"src/ui/help-menu.tsx"() {
|
|
5615
|
+
"use strict";
|
|
5616
|
+
CATEGORIES = [
|
|
5617
|
+
{
|
|
5618
|
+
key: "mode",
|
|
5619
|
+
label: "Mode",
|
|
5620
|
+
commands: [
|
|
5621
|
+
{ command: "/mode edit", description: "switch to edit mode" },
|
|
5622
|
+
{ command: "/mode plan", description: "switch to plan mode" },
|
|
5623
|
+
{ command: "/mode auto", description: "switch to auto mode" }
|
|
5624
|
+
]
|
|
5625
|
+
},
|
|
5626
|
+
{
|
|
5627
|
+
key: "thinking",
|
|
5628
|
+
label: "Thinking",
|
|
5629
|
+
commands: [
|
|
5630
|
+
{ command: "/thinking low", description: "fast, lower quality" },
|
|
5631
|
+
{ command: "/thinking medium", description: "balanced" },
|
|
5632
|
+
{ command: "/thinking high", description: "slow, higher quality" }
|
|
5633
|
+
]
|
|
5634
|
+
},
|
|
5635
|
+
{
|
|
5636
|
+
key: "theme",
|
|
5637
|
+
label: "Theme",
|
|
5638
|
+
commands: []
|
|
5639
|
+
},
|
|
5640
|
+
{
|
|
5641
|
+
key: "session",
|
|
5642
|
+
label: "Session",
|
|
5643
|
+
commands: [
|
|
5644
|
+
{ command: "/resume", description: "pick a past conversation" },
|
|
5645
|
+
{ command: "/compact", description: "summarize old turns to free context" },
|
|
5646
|
+
{ command: "/clear", description: "clear current conversation" }
|
|
5647
|
+
]
|
|
5648
|
+
},
|
|
5649
|
+
{
|
|
5650
|
+
key: "memory",
|
|
5651
|
+
label: "Memory",
|
|
5652
|
+
commands: [
|
|
5653
|
+
{ command: "/memory", description: "show memory stats" },
|
|
5654
|
+
{ command: "/memory on", description: "enable memory" },
|
|
5655
|
+
{ command: "/memory off", description: "disable memory" },
|
|
5656
|
+
{ command: "/memory clear", description: "wipe memories for this repo" },
|
|
5657
|
+
{ command: "/memory search <query>", description: "search stored memories", selectable: false }
|
|
5658
|
+
]
|
|
5659
|
+
},
|
|
5660
|
+
{
|
|
5661
|
+
key: "mcp",
|
|
5662
|
+
label: "MCP",
|
|
5663
|
+
commands: [
|
|
5664
|
+
{ command: "/mcp list", description: "list connected MCP servers and tools" },
|
|
5665
|
+
{ command: "/mcp reload", description: "reconnect all configured MCP servers" }
|
|
5666
|
+
]
|
|
5667
|
+
},
|
|
5668
|
+
{
|
|
5669
|
+
key: "gateway",
|
|
5670
|
+
label: "Gateway",
|
|
5671
|
+
commands: [
|
|
5672
|
+
{ command: "/gateway", description: "show gateway status" },
|
|
5673
|
+
{ command: "/gateway off", description: "disable AI Gateway (direct Workers AI)" },
|
|
5674
|
+
{ command: "/gateway skip-cache true", description: "enable skip-cache" },
|
|
5675
|
+
{ command: "/gateway skip-cache false", description: "disable skip-cache" },
|
|
5676
|
+
{ command: "/gateway collect-logs true", description: "enable log collection" },
|
|
5677
|
+
{ command: "/gateway collect-logs false", description: "disable log collection" },
|
|
5678
|
+
{ command: "/gateway metadata clear", description: "remove all metadata" },
|
|
5679
|
+
{ command: "/gateway <id>", description: "enable AI Gateway", selectable: false },
|
|
5680
|
+
{ command: "/gateway cache-ttl <seconds>", description: "set cache TTL", selectable: false },
|
|
5681
|
+
{ command: "/gateway metadata <key>=<value>", description: "add metadata", selectable: false }
|
|
5682
|
+
]
|
|
5683
|
+
},
|
|
5684
|
+
{
|
|
5685
|
+
key: "info",
|
|
5686
|
+
label: "Info",
|
|
5687
|
+
commands: [
|
|
5688
|
+
{ command: "/cost", description: "show cost report" },
|
|
5689
|
+
{ command: "/model", description: "show current model" },
|
|
5690
|
+
{ command: "/update", description: "check for updates" },
|
|
5691
|
+
{ command: "/hello", description: "send a voice note to the creator" },
|
|
5692
|
+
{ command: "/community", description: "join our Discord server" }
|
|
5693
|
+
]
|
|
5694
|
+
},
|
|
5695
|
+
{
|
|
5696
|
+
key: "commands",
|
|
5697
|
+
label: "Commands",
|
|
5698
|
+
commands: [
|
|
5699
|
+
{ command: "/command create", description: "create a new custom slash command" },
|
|
5700
|
+
{ command: "/command edit", description: "edit an existing custom command" },
|
|
5701
|
+
{ command: "/command delete", description: "delete a custom command" },
|
|
5702
|
+
{ command: "/command list", description: "list all custom commands" }
|
|
5703
|
+
]
|
|
5704
|
+
},
|
|
5705
|
+
{
|
|
5706
|
+
key: "config",
|
|
5707
|
+
label: "Config",
|
|
5708
|
+
commands: [
|
|
5709
|
+
{ command: "/init", description: "scan this repo and write a KIMI.md" },
|
|
5710
|
+
{ command: "/logout", description: "clear credentials" }
|
|
5711
|
+
]
|
|
5712
|
+
}
|
|
5713
|
+
];
|
|
5714
|
+
SINGLE_COMMANDS = [
|
|
5715
|
+
{ command: "/reasoning", description: "toggle show/hide model reasoning" },
|
|
5716
|
+
{ command: "/help", description: "show this menu" },
|
|
5717
|
+
{ command: "/exit", description: "exit kimiflare" }
|
|
5718
|
+
];
|
|
5719
|
+
}
|
|
5720
|
+
});
|
|
5721
|
+
|
|
5370
5722
|
// src/ui/theme.ts
|
|
5371
5723
|
function resolveTheme(name) {
|
|
5372
5724
|
if (!name) return THEMES[DEFAULT_THEME_NAME];
|
|
@@ -6164,11 +6516,11 @@ function updateAccessedAt(db, ids) {
|
|
|
6164
6516
|
function searchMemoriesFts(db, query, repoPath, limit = 50) {
|
|
6165
6517
|
const sql = repoPath ? `SELECT m.*, rank FROM memories m
|
|
6166
6518
|
JOIN memories_fts fts ON m.rowid = fts.rowid
|
|
6167
|
-
WHERE memories_fts MATCH ? AND m.repo_path = ? AND m.forgotten = 0 AND m.superseded_by IS NULL
|
|
6519
|
+
WHERE memories_fts MATCH ? AND m.repo_path = ? AND m.forgotten = 0 AND m.superseded_by IS NULL AND m.category != 'task'
|
|
6168
6520
|
ORDER BY rank
|
|
6169
6521
|
LIMIT ?` : `SELECT m.*, rank FROM memories m
|
|
6170
6522
|
JOIN memories_fts fts ON m.rowid = fts.rowid
|
|
6171
|
-
WHERE memories_fts MATCH ? AND m.forgotten = 0 AND m.superseded_by IS NULL
|
|
6523
|
+
WHERE memories_fts MATCH ? AND m.forgotten = 0 AND m.superseded_by IS NULL AND m.category != 'task'
|
|
6172
6524
|
ORDER BY rank
|
|
6173
6525
|
LIMIT ?`;
|
|
6174
6526
|
const params = repoPath ? [`${query}*`, repoPath, limit] : [`${query}*`, limit];
|
|
@@ -6181,7 +6533,7 @@ function searchMemoriesFts(db, query, repoPath, limit = 50) {
|
|
|
6181
6533
|
function listMemoriesForVectorSearch(db, repoPath, since, limit = 2e3) {
|
|
6182
6534
|
const rows = db.prepare(
|
|
6183
6535
|
`SELECT * FROM memories
|
|
6184
|
-
WHERE repo_path = ? AND created_at >= ? AND forgotten = 0 AND superseded_by IS NULL
|
|
6536
|
+
WHERE repo_path = ? AND created_at >= ? AND forgotten = 0 AND superseded_by IS NULL AND category != 'task'
|
|
6185
6537
|
ORDER BY accessed_at DESC
|
|
6186
6538
|
LIMIT ?`
|
|
6187
6539
|
).all(repoPath, since, limit);
|
|
@@ -6628,7 +6980,20 @@ function redactSecrets(text) {
|
|
|
6628
6980
|
}
|
|
6629
6981
|
return result;
|
|
6630
6982
|
}
|
|
6631
|
-
|
|
6983
|
+
function deterministicTopicKey(content) {
|
|
6984
|
+
return content.toLowerCase().replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+/g, "_").slice(0, 60);
|
|
6985
|
+
}
|
|
6986
|
+
function pickTopicKey(content, existingKeys) {
|
|
6987
|
+
const normalized = deterministicTopicKey(content);
|
|
6988
|
+
if (!normalized) return null;
|
|
6989
|
+
for (const existing of existingKeys) {
|
|
6990
|
+
if (normalized.includes(existing) || existing.includes(normalized)) {
|
|
6991
|
+
return existing;
|
|
6992
|
+
}
|
|
6993
|
+
}
|
|
6994
|
+
return normalized;
|
|
6995
|
+
}
|
|
6996
|
+
var SECRET_PATTERNS, VERIFY_SYSTEM, HYPOTHETICAL_QUERIES_SYSTEM, MemoryManager;
|
|
6632
6997
|
var init_manager2 = __esm({
|
|
6633
6998
|
"src/memory/manager.ts"() {
|
|
6634
6999
|
"use strict";
|
|
@@ -6656,19 +7021,12 @@ Return a JSON object:
|
|
|
6656
7021
|
"confidence": "high" | "medium" | "low",
|
|
6657
7022
|
"corrected_content": string | null // if minor correction needed, provide it; otherwise null
|
|
6658
7023
|
}`;
|
|
6659
|
-
TOPIC_KEY_SYSTEM = `You are a topic normalization engine. Given a new memory and a list of existing topic keys for this project, decide whether the new memory belongs to an existing topic or needs a new one.
|
|
6660
|
-
|
|
6661
|
-
Rules:
|
|
6662
|
-
- Topic keys are lowercase snake_case, max 3 words.
|
|
6663
|
-
- If the new memory is about the same topic as an existing key, return the existing key.
|
|
6664
|
-
- If it's a genuinely new topic, generate a new normalized key.
|
|
6665
|
-
- Return ONLY the topic key string, nothing else.`;
|
|
6666
7024
|
HYPOTHETICAL_QUERIES_SYSTEM = `Given a memory, generate 3-5 short search queries a user might type to find it.
|
|
6667
7025
|
Cover different phrasings: declarative, interrogative, and keyword-based.
|
|
6668
7026
|
|
|
6669
7027
|
Return a JSON array of strings. Example:
|
|
6670
7028
|
["what package manager does this project use?", "pnpm preference", "user likes pnpm over npm"]`;
|
|
6671
|
-
MemoryManager = class {
|
|
7029
|
+
MemoryManager = class _MemoryManager {
|
|
6672
7030
|
db = null;
|
|
6673
7031
|
opts;
|
|
6674
7032
|
constructor(opts2) {
|
|
@@ -6696,6 +7054,14 @@ Return a JSON array of strings. Example:
|
|
|
6696
7054
|
gateway: this.opts.gateway
|
|
6697
7055
|
};
|
|
6698
7056
|
}
|
|
7057
|
+
get plumbingLlmOpts() {
|
|
7058
|
+
return {
|
|
7059
|
+
accountId: this.opts.accountId,
|
|
7060
|
+
apiToken: this.opts.apiToken,
|
|
7061
|
+
model: this.opts.plumbingModel ?? "@cf/meta/llama-4-scout-17b-16e-instruct",
|
|
7062
|
+
gateway: this.opts.gateway
|
|
7063
|
+
};
|
|
7064
|
+
}
|
|
6699
7065
|
shouldRedact() {
|
|
6700
7066
|
return this.opts.redactSecrets !== false;
|
|
6701
7067
|
}
|
|
@@ -6716,7 +7082,7 @@ Return a JSON array of strings. Example:
|
|
|
6716
7082
|
if (verified.corrected_content) {
|
|
6717
7083
|
safeContent = verified.corrected_content;
|
|
6718
7084
|
}
|
|
6719
|
-
const topicKey =
|
|
7085
|
+
const topicKey = this.normalizeTopicKey(safeContent, repoPath);
|
|
6720
7086
|
const supersededIds = [];
|
|
6721
7087
|
if (topicKey) {
|
|
6722
7088
|
const existing = findMemoriesByTopicKey(this.db, repoPath, topicKey);
|
|
@@ -6768,6 +7134,38 @@ Return a JSON array of strings. Example:
|
|
|
6768
7134
|
}
|
|
6769
7135
|
return retrieveMemories({ db: this.db, query });
|
|
6770
7136
|
}
|
|
7137
|
+
/**
|
|
7138
|
+
* Format recalled memories as a compact context block for injection into messages.
|
|
7139
|
+
*/
|
|
7140
|
+
static formatRecalled(results) {
|
|
7141
|
+
if (results.length === 0) return "";
|
|
7142
|
+
const lines = ["[recalled memories]"];
|
|
7143
|
+
for (const r of results) {
|
|
7144
|
+
const files = r.memory.relatedFiles.length > 0 ? ` [${r.memory.relatedFiles.join(", ")}]` : "";
|
|
7145
|
+
lines.push(`- [${r.memory.category}] ${r.memory.content}${files}`);
|
|
7146
|
+
}
|
|
7147
|
+
return lines.join("\n");
|
|
7148
|
+
}
|
|
7149
|
+
/**
|
|
7150
|
+
* Synthesize recalled memories into a dense prose paragraph.
|
|
7151
|
+
* Uses the lightweight plumbing model (Scout) to keep costs low.
|
|
7152
|
+
*/
|
|
7153
|
+
async synthesizeRecalled(results, signal) {
|
|
7154
|
+
if (results.length === 0) return "";
|
|
7155
|
+
const raw = _MemoryManager.formatRecalled(results);
|
|
7156
|
+
const text = await runKimiText({
|
|
7157
|
+
...this.plumbingLlmOpts,
|
|
7158
|
+
signal,
|
|
7159
|
+
messages: [
|
|
7160
|
+
{
|
|
7161
|
+
role: "system",
|
|
7162
|
+
content: "You are a context-synthesis engine. Given a list of recalled memories about a codebase, produce a single dense paragraph of context for a coding assistant. Preserve all facts, file paths, and decisions. Do not add information not present in the memories. Be terse."
|
|
7163
|
+
},
|
|
7164
|
+
{ role: "user", content: raw }
|
|
7165
|
+
]
|
|
7166
|
+
});
|
|
7167
|
+
return text || raw;
|
|
7168
|
+
}
|
|
6771
7169
|
/**
|
|
6772
7170
|
* Soft-delete a memory by ID.
|
|
6773
7171
|
*/
|
|
@@ -6827,7 +7225,7 @@ Return a JSON array of strings. Example:
|
|
|
6827
7225
|
}
|
|
6828
7226
|
async verifyMemory(content, signal) {
|
|
6829
7227
|
const text = await runKimiText({
|
|
6830
|
-
...this.
|
|
7228
|
+
...this.plumbingLlmOpts,
|
|
6831
7229
|
signal,
|
|
6832
7230
|
messages: [
|
|
6833
7231
|
{ role: "system", content: VERIFY_SYSTEM },
|
|
@@ -6851,31 +7249,13 @@ Context: This memory was explicitly provided by the user during a conversation.`
|
|
|
6851
7249
|
const corrected = typeof rec.corrected_content === "string" ? rec.corrected_content : null;
|
|
6852
7250
|
return { valid, corrected_content: corrected };
|
|
6853
7251
|
}
|
|
6854
|
-
|
|
7252
|
+
normalizeTopicKey(content, repoPath) {
|
|
6855
7253
|
const existingKeys = listTopicKeys(this.db, repoPath);
|
|
6856
|
-
|
|
6857
|
-
const text = await runKimiText({
|
|
6858
|
-
...this.llmOpts,
|
|
6859
|
-
signal,
|
|
6860
|
-
messages: [
|
|
6861
|
-
{ role: "system", content: TOPIC_KEY_SYSTEM },
|
|
6862
|
-
{
|
|
6863
|
-
role: "user",
|
|
6864
|
-
content: `Existing topic keys:
|
|
6865
|
-
${keysBlock}
|
|
6866
|
-
|
|
6867
|
-
New memory: "${content}"
|
|
6868
|
-
|
|
6869
|
-
Return only the topic key string.`
|
|
6870
|
-
}
|
|
6871
|
-
]
|
|
6872
|
-
});
|
|
6873
|
-
const key = text.trim().toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/g, "").slice(0, 60);
|
|
6874
|
-
return key || null;
|
|
7254
|
+
return pickTopicKey(content, existingKeys);
|
|
6875
7255
|
}
|
|
6876
7256
|
async generateHypotheticalQueries(content, signal) {
|
|
6877
7257
|
const text = await runKimiText({
|
|
6878
|
-
...this.
|
|
7258
|
+
...this.plumbingLlmOpts,
|
|
6879
7259
|
signal,
|
|
6880
7260
|
messages: [
|
|
6881
7261
|
{ role: "system", content: HYPOTHETICAL_QUERIES_SYSTEM },
|
|
@@ -6932,133 +7312,1036 @@ var init_state = __esm({
|
|
|
6932
7312
|
}
|
|
6933
7313
|
});
|
|
6934
7314
|
|
|
6935
|
-
// src/
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
function
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
7315
|
+
// src/commands/frontmatter.ts
|
|
7316
|
+
function serializeFrontmatter(data) {
|
|
7317
|
+
const lines = [];
|
|
7318
|
+
for (const [key, value] of Object.entries(data)) {
|
|
7319
|
+
if (value === void 0 || value === "") continue;
|
|
7320
|
+
lines.push(`${key}: ${value}`);
|
|
7321
|
+
}
|
|
7322
|
+
if (lines.length === 0) return "";
|
|
7323
|
+
return `---
|
|
7324
|
+
${lines.join("\n")}
|
|
7325
|
+
---
|
|
7326
|
+
`;
|
|
7327
|
+
}
|
|
7328
|
+
function parseFrontmatter(input) {
|
|
7329
|
+
const errors = [];
|
|
7330
|
+
if (!FENCE.test(input)) {
|
|
7331
|
+
return { data: {}, body: input, errors };
|
|
7332
|
+
}
|
|
7333
|
+
const afterOpen = input.replace(FENCE, "");
|
|
7334
|
+
const closeIdx = afterOpen.search(/\r?\n---\s*(\r?\n|$)/);
|
|
7335
|
+
if (closeIdx === -1) {
|
|
7336
|
+
errors.push("frontmatter not closed with ---");
|
|
7337
|
+
return { data: {}, body: input, errors };
|
|
7338
|
+
}
|
|
7339
|
+
const yaml = afterOpen.slice(0, closeIdx);
|
|
7340
|
+
const closeMatch = afterOpen.slice(closeIdx).match(/\r?\n---\s*(\r?\n|$)/);
|
|
7341
|
+
const body = closeMatch ? afterOpen.slice(closeIdx + closeMatch[0].length) : "";
|
|
7342
|
+
const data = {};
|
|
7343
|
+
const lines = yaml.split(/\r?\n/);
|
|
7344
|
+
for (const line of lines) {
|
|
7345
|
+
if (line.trim() === "" || line.trim().startsWith("#")) continue;
|
|
7346
|
+
const m = line.match(KV);
|
|
7347
|
+
if (!m) {
|
|
7348
|
+
errors.push(`unparseable line: ${line.trim()}`);
|
|
7349
|
+
continue;
|
|
7350
|
+
}
|
|
7351
|
+
const key = m[1];
|
|
7352
|
+
let value = m[2] ?? "";
|
|
7353
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2 || value.startsWith("'") && value.endsWith("'") && value.length >= 2) {
|
|
7354
|
+
value = value.slice(1, -1);
|
|
7355
|
+
}
|
|
7356
|
+
data[key] = value;
|
|
7357
|
+
}
|
|
7358
|
+
return { data, body, errors };
|
|
6957
7359
|
}
|
|
6958
|
-
|
|
6959
|
-
|
|
7360
|
+
var FENCE, KV;
|
|
7361
|
+
var init_frontmatter = __esm({
|
|
7362
|
+
"src/commands/frontmatter.ts"() {
|
|
7363
|
+
"use strict";
|
|
7364
|
+
FENCE = /^---\s*\r?\n/;
|
|
7365
|
+
KV = /^([A-Za-z][\w-]*)\s*:\s*(.*?)\s*$/;
|
|
7366
|
+
}
|
|
7367
|
+
});
|
|
7368
|
+
|
|
7369
|
+
// src/commands/loader.ts
|
|
7370
|
+
import { open, realpath } from "fs/promises";
|
|
7371
|
+
import { homedir as homedir9 } from "os";
|
|
7372
|
+
import { join as join12, relative as relative2, sep as sep2 } from "path";
|
|
7373
|
+
import fg3 from "fast-glob";
|
|
7374
|
+
function projectCommandsDir(cwd = process.cwd()) {
|
|
7375
|
+
return join12(cwd, ".kimiflare", "commands");
|
|
7376
|
+
}
|
|
7377
|
+
function globalCommandsDir() {
|
|
7378
|
+
const xdg = process.env.XDG_CONFIG_HOME || join12(homedir9(), ".config");
|
|
7379
|
+
return join12(xdg, "kimiflare", "commands");
|
|
7380
|
+
}
|
|
7381
|
+
async function loadCustomCommands(cwd = process.cwd()) {
|
|
7382
|
+
const warnings = [];
|
|
7383
|
+
const byName = /* @__PURE__ */ new Map();
|
|
7384
|
+
const sources = [
|
|
7385
|
+
{ dir: globalCommandsDir(), source: "global" },
|
|
7386
|
+
{ dir: projectCommandsDir(cwd), source: "project" }
|
|
7387
|
+
];
|
|
7388
|
+
const perSource = await Promise.all(
|
|
7389
|
+
sources.map(async ({ dir, source }) => {
|
|
7390
|
+
const safeDir = await resolveSafeDir(dir, source, cwd, warnings);
|
|
7391
|
+
if (safeDir === null) return [];
|
|
7392
|
+
const files = await fg3("**/*.md", {
|
|
7393
|
+
cwd: safeDir,
|
|
7394
|
+
absolute: true,
|
|
7395
|
+
onlyFiles: true,
|
|
7396
|
+
followSymbolicLinks: false,
|
|
7397
|
+
suppressErrors: true
|
|
7398
|
+
});
|
|
7399
|
+
return Promise.all(files.map((file) => loadOne(file, safeDir, source, warnings)));
|
|
7400
|
+
})
|
|
7401
|
+
);
|
|
7402
|
+
for (const loaded of perSource) {
|
|
7403
|
+
for (const cmd of loaded) {
|
|
7404
|
+
if (cmd) byName.set(cmd.name, cmd);
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
6960
7407
|
return {
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
gatewayId: cfg.aiGatewayId,
|
|
6964
|
-
meta
|
|
7408
|
+
commands: [...byName.values()].sort((a, b) => a.name.localeCompare(b.name)),
|
|
7409
|
+
warnings
|
|
6965
7410
|
};
|
|
6966
7411
|
}
|
|
6967
|
-
function
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
}
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
7412
|
+
async function resolveSafeDir(dir, source, cwd, warnings) {
|
|
7413
|
+
let realDir;
|
|
7414
|
+
try {
|
|
7415
|
+
realDir = await realpath(dir);
|
|
7416
|
+
} catch (err) {
|
|
7417
|
+
const code = err.code;
|
|
7418
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
7419
|
+
warnings.push(`commands dir ${dir} unreadable: ${err.message}`);
|
|
7420
|
+
}
|
|
7421
|
+
return null;
|
|
7422
|
+
}
|
|
7423
|
+
if (source === "project") {
|
|
7424
|
+
let realCwd;
|
|
7425
|
+
try {
|
|
7426
|
+
realCwd = await realpath(cwd);
|
|
7427
|
+
} catch {
|
|
7428
|
+
return null;
|
|
7429
|
+
}
|
|
7430
|
+
const rel = relative2(realCwd, realDir);
|
|
7431
|
+
if (rel !== "" && isPathOutside(rel)) {
|
|
7432
|
+
warnings.push(`commands dir ${dir} escapes workspace via symlink \u2014 skipped`);
|
|
7433
|
+
return null;
|
|
7434
|
+
}
|
|
7435
|
+
}
|
|
7436
|
+
return realDir;
|
|
6975
7437
|
}
|
|
6976
|
-
function
|
|
6977
|
-
let
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
if (
|
|
6983
|
-
|
|
6984
|
-
|
|
7438
|
+
async function loadOne(file, rootDir, source, warnings) {
|
|
7439
|
+
let content;
|
|
7440
|
+
try {
|
|
7441
|
+
const handle = await open(file, "r");
|
|
7442
|
+
try {
|
|
7443
|
+
const stats = await handle.stat();
|
|
7444
|
+
if (stats.size > MAX_COMMAND_FILE_BYTES) {
|
|
7445
|
+
warnings.push(`command file ${file} exceeds ${MAX_COMMAND_FILE_BYTES} bytes \u2014 skipped`);
|
|
7446
|
+
return null;
|
|
6985
7447
|
}
|
|
7448
|
+
content = await handle.readFile("utf8");
|
|
7449
|
+
} finally {
|
|
7450
|
+
await handle.close();
|
|
6986
7451
|
}
|
|
7452
|
+
} catch (e) {
|
|
7453
|
+
warnings.push(`failed to read command file ${file}: ${e.message}`);
|
|
7454
|
+
return null;
|
|
6987
7455
|
}
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
...kept
|
|
6993
|
-
];
|
|
6994
|
-
}
|
|
6995
|
-
function makePrefixMessages(cacheStable, model, mode, tools) {
|
|
6996
|
-
if (cacheStable) {
|
|
6997
|
-
return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
|
|
7456
|
+
const name = filenameToCommandName(file, rootDir);
|
|
7457
|
+
if (!name) {
|
|
7458
|
+
warnings.push(`invalid command name from ${file}`);
|
|
7459
|
+
return null;
|
|
6998
7460
|
}
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7461
|
+
const { data, body, errors } = parseFrontmatter(content);
|
|
7462
|
+
if (errors.length > 0) {
|
|
7463
|
+
warnings.push(`frontmatter errors in ${file}: ${errors.join("; ")} \u2014 skipped`);
|
|
7464
|
+
return null;
|
|
7465
|
+
}
|
|
7466
|
+
const cmd = {
|
|
7467
|
+
name,
|
|
7468
|
+
template: body,
|
|
7469
|
+
source,
|
|
7470
|
+
filepath: file
|
|
7471
|
+
};
|
|
7472
|
+
if (data.description) cmd.description = data.description;
|
|
7473
|
+
const modeRaw = data.mode ?? data.agent;
|
|
7474
|
+
if (modeRaw !== void 0) {
|
|
7475
|
+
const normalized = modeRaw === "build" ? "edit" : modeRaw;
|
|
7476
|
+
if (MODES.includes(normalized)) {
|
|
7477
|
+
cmd.mode = normalized;
|
|
7478
|
+
} else {
|
|
7479
|
+
warnings.push(`unknown mode "${modeRaw}" in ${file} \u2014 ignored`);
|
|
7003
7480
|
}
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7481
|
+
}
|
|
7482
|
+
if (data.model !== void 0 && data.model !== "") {
|
|
7483
|
+
cmd.model = data.model;
|
|
7484
|
+
}
|
|
7485
|
+
if (data.effort !== void 0) {
|
|
7486
|
+
if (EFFORTS.includes(data.effort)) {
|
|
7487
|
+
cmd.effort = data.effort;
|
|
7488
|
+
} else {
|
|
7489
|
+
warnings.push(`unknown effort "${data.effort}" in ${file} \u2014 ignored`);
|
|
7012
7490
|
}
|
|
7013
7491
|
}
|
|
7014
|
-
return
|
|
7492
|
+
return cmd;
|
|
7015
7493
|
}
|
|
7016
|
-
function
|
|
7017
|
-
const
|
|
7018
|
-
|
|
7019
|
-
const
|
|
7020
|
-
const
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
const
|
|
7053
|
-
const
|
|
7054
|
-
const
|
|
7055
|
-
const
|
|
7056
|
-
const
|
|
7057
|
-
const
|
|
7058
|
-
|
|
7059
|
-
);
|
|
7060
|
-
|
|
7061
|
-
|
|
7494
|
+
function filenameToCommandName(file, rootDir) {
|
|
7495
|
+
const rel = relative2(rootDir, file);
|
|
7496
|
+
if (!rel || isPathOutside(rel)) return null;
|
|
7497
|
+
const noExt = rel.replace(/\.md$/i, "");
|
|
7498
|
+
const parts = noExt.split(sep2).filter((p) => p.length > 0);
|
|
7499
|
+
if (parts.length === 0) return null;
|
|
7500
|
+
if (parts.some((p) => !/^[\w.-]+$/.test(p))) return null;
|
|
7501
|
+
return parts.join("/");
|
|
7502
|
+
}
|
|
7503
|
+
var MAX_COMMAND_FILE_BYTES;
|
|
7504
|
+
var init_loader = __esm({
|
|
7505
|
+
"src/commands/loader.ts"() {
|
|
7506
|
+
"use strict";
|
|
7507
|
+
init_mode();
|
|
7508
|
+
init_config();
|
|
7509
|
+
init_paths();
|
|
7510
|
+
init_frontmatter();
|
|
7511
|
+
MAX_COMMAND_FILE_BYTES = 256 * 1024;
|
|
7512
|
+
}
|
|
7513
|
+
});
|
|
7514
|
+
|
|
7515
|
+
// src/commands/renderer.ts
|
|
7516
|
+
import { exec } from "child_process";
|
|
7517
|
+
import { open as open2, realpath as realpath2 } from "fs/promises";
|
|
7518
|
+
import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolvePathJoin } from "path";
|
|
7519
|
+
import { promisify as promisify2 } from "util";
|
|
7520
|
+
function tokenizeArgs(s) {
|
|
7521
|
+
return [...s.matchAll(ARG_TOKEN_RE)].map((match) => {
|
|
7522
|
+
const token = match[0];
|
|
7523
|
+
if (token.length >= 2 && (token.startsWith('"') && token.endsWith('"') || token.startsWith("'") && token.endsWith("'"))) {
|
|
7524
|
+
return token.slice(1, -1);
|
|
7525
|
+
}
|
|
7526
|
+
return token;
|
|
7527
|
+
});
|
|
7528
|
+
}
|
|
7529
|
+
async function renderCommand(cmd, rawInput, opts2 = {}) {
|
|
7530
|
+
const warnings = [];
|
|
7531
|
+
const cwd = opts2.cwd ?? process.cwd();
|
|
7532
|
+
const maxFileBytes = opts2.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
7533
|
+
const argsString = stripCommandName(rawInput);
|
|
7534
|
+
const args = tokenizeArgs(argsString);
|
|
7535
|
+
const originalTemplate = cmd.template;
|
|
7536
|
+
const hadArguments = originalTemplate.includes("$ARGUMENTS");
|
|
7537
|
+
const hadPositionals = HAS_POSITIONAL.test(originalTemplate);
|
|
7538
|
+
let prompt = replacePositionals(originalTemplate, args);
|
|
7539
|
+
prompt = prompt.replaceAll("$ARGUMENTS", argsString);
|
|
7540
|
+
if (!hadArguments && !hadPositionals && argsString !== "") {
|
|
7541
|
+
prompt += `
|
|
7542
|
+
|
|
7543
|
+
${argsString}`;
|
|
7544
|
+
}
|
|
7545
|
+
prompt = await replaceShell(prompt, warnings, opts2.shellTimeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS);
|
|
7546
|
+
prompt = await replaceFiles(prompt, warnings, cwd, maxFileBytes);
|
|
7547
|
+
if (prompt.trim() === "") {
|
|
7548
|
+
warnings.push("rendered prompt is empty");
|
|
7549
|
+
}
|
|
7550
|
+
return { prompt, warnings };
|
|
7551
|
+
}
|
|
7552
|
+
function stripCommandName(rawInput) {
|
|
7553
|
+
if (!rawInput.startsWith("/")) {
|
|
7554
|
+
return rawInput;
|
|
7555
|
+
}
|
|
7556
|
+
return rawInput.replace(/^\/\S+\s*/, "");
|
|
7557
|
+
}
|
|
7558
|
+
function replacePositionals(template, args) {
|
|
7559
|
+
const indexes = [...template.matchAll(POSITIONAL_RE)].map(
|
|
7560
|
+
(match) => Number(match[1])
|
|
7561
|
+
);
|
|
7562
|
+
const highest = indexes.length === 0 ? -1 : Math.max(...indexes);
|
|
7563
|
+
return template.replace(POSITIONAL_RE, (_match, n) => {
|
|
7564
|
+
const index = Number(n);
|
|
7565
|
+
if (index <= 0) {
|
|
7566
|
+
return "";
|
|
7567
|
+
}
|
|
7568
|
+
if (index === highest) {
|
|
7569
|
+
return args.slice(index - 1).join(" ");
|
|
7570
|
+
}
|
|
7571
|
+
return args[index - 1] ?? "";
|
|
7572
|
+
});
|
|
7573
|
+
}
|
|
7574
|
+
async function replaceShell(prompt, warnings, shellTimeoutMs) {
|
|
7575
|
+
const matches = [...prompt.matchAll(SHELL_RE)];
|
|
7576
|
+
const replacements = await Promise.all(
|
|
7577
|
+
matches.map(async (match) => {
|
|
7578
|
+
const command = match[1] ?? "";
|
|
7579
|
+
try {
|
|
7580
|
+
const { stdout } = await execAsync(command, {
|
|
7581
|
+
timeout: shellTimeoutMs,
|
|
7582
|
+
maxBuffer: 1024 * 1024
|
|
7583
|
+
});
|
|
7584
|
+
return String(stdout).trimEnd();
|
|
7585
|
+
} catch (error) {
|
|
7586
|
+
warnings.push(`shell command failed: \`${command}\` \u2014 ${message(error)}`);
|
|
7587
|
+
return "";
|
|
7588
|
+
}
|
|
7589
|
+
})
|
|
7590
|
+
);
|
|
7591
|
+
let index = 0;
|
|
7592
|
+
return prompt.replace(SHELL_RE, () => replacements[index++] ?? "");
|
|
7593
|
+
}
|
|
7594
|
+
async function replaceFiles(prompt, warnings, cwd, maxFileBytes) {
|
|
7595
|
+
const matches = [...prompt.matchAll(FILE_RE)];
|
|
7596
|
+
if (matches.length === 0) return prompt;
|
|
7597
|
+
const realCwd = await realpath2(cwd).catch(() => cwd);
|
|
7598
|
+
const replacements = await Promise.all(
|
|
7599
|
+
matches.map(async (match) => {
|
|
7600
|
+
const rawPath = match[1] ?? "";
|
|
7601
|
+
if (isAbsolute2(rawPath) || rawPath.startsWith("~")) {
|
|
7602
|
+
warnings.push(`file inclusion skipped: @${rawPath} \u2014 outside workspace`);
|
|
7603
|
+
return "";
|
|
7604
|
+
}
|
|
7605
|
+
const resolved = resolvePathJoin(cwd, rawPath);
|
|
7606
|
+
if (isPathOutside(relative3(cwd, resolved))) {
|
|
7607
|
+
warnings.push(`file inclusion skipped: @${rawPath} \u2014 outside workspace`);
|
|
7608
|
+
return "";
|
|
7609
|
+
}
|
|
7610
|
+
let real;
|
|
7611
|
+
try {
|
|
7612
|
+
real = await realpath2(resolved);
|
|
7613
|
+
} catch (error) {
|
|
7614
|
+
warnings.push(`file inclusion failed: @${rawPath} \u2014 ${message(error)}`);
|
|
7615
|
+
return "";
|
|
7616
|
+
}
|
|
7617
|
+
if (isPathOutside(relative3(realCwd, real))) {
|
|
7618
|
+
warnings.push(`file inclusion skipped: @${rawPath} \u2014 symlink escapes workspace`);
|
|
7619
|
+
return "";
|
|
7620
|
+
}
|
|
7621
|
+
try {
|
|
7622
|
+
const handle = await open2(real, "r");
|
|
7623
|
+
try {
|
|
7624
|
+
const stats = await handle.stat();
|
|
7625
|
+
if (stats.size > maxFileBytes) {
|
|
7626
|
+
warnings.push(
|
|
7627
|
+
`file inclusion skipped: @${rawPath} \u2014 exceeds ${maxFileBytes} bytes`
|
|
7628
|
+
);
|
|
7629
|
+
return "";
|
|
7630
|
+
}
|
|
7631
|
+
return await handle.readFile("utf8");
|
|
7632
|
+
} finally {
|
|
7633
|
+
await handle.close();
|
|
7634
|
+
}
|
|
7635
|
+
} catch (error) {
|
|
7636
|
+
warnings.push(`file inclusion failed: @${rawPath} \u2014 ${message(error)}`);
|
|
7637
|
+
return "";
|
|
7638
|
+
}
|
|
7639
|
+
})
|
|
7640
|
+
);
|
|
7641
|
+
let index = 0;
|
|
7642
|
+
return prompt.replace(FILE_RE, (_match, _path, trailing = "") => {
|
|
7643
|
+
const replacement = replacements[index++] ?? "";
|
|
7644
|
+
return replacement + trailing;
|
|
7645
|
+
});
|
|
7646
|
+
}
|
|
7647
|
+
function message(error) {
|
|
7648
|
+
return error instanceof Error ? error.message : String(error);
|
|
7649
|
+
}
|
|
7650
|
+
var execAsync, ARG_TOKEN_RE, POSITIONAL_RE, HAS_POSITIONAL, SHELL_RE, FILE_RE, DEFAULT_MAX_FILE_BYTES, DEFAULT_SHELL_TIMEOUT_MS;
|
|
7651
|
+
var init_renderer = __esm({
|
|
7652
|
+
"src/commands/renderer.ts"() {
|
|
7653
|
+
"use strict";
|
|
7654
|
+
init_paths();
|
|
7655
|
+
execAsync = promisify2(exec);
|
|
7656
|
+
ARG_TOKEN_RE = /(?:"[^"]*"|'[^']*'|[^\s"']+)/g;
|
|
7657
|
+
POSITIONAL_RE = /\$(\d+)/g;
|
|
7658
|
+
HAS_POSITIONAL = /\$\d+/;
|
|
7659
|
+
SHELL_RE = /!`([^`]+)`/g;
|
|
7660
|
+
FILE_RE = /(?<![\w`])@(\.?[^\s`,]+?)([.,;:!?)\]}]*)(?=[\s`,]|$)/g;
|
|
7661
|
+
DEFAULT_MAX_FILE_BYTES = 100 * 1024;
|
|
7662
|
+
DEFAULT_SHELL_TIMEOUT_MS = 5e3;
|
|
7663
|
+
}
|
|
7664
|
+
});
|
|
7665
|
+
|
|
7666
|
+
// src/commands/save.ts
|
|
7667
|
+
import { mkdir as mkdir8, writeFile as writeFile8, unlink as unlink2 } from "fs/promises";
|
|
7668
|
+
import { dirname as dirname5 } from "path";
|
|
7669
|
+
async function saveCustomCommand(opts2) {
|
|
7670
|
+
const dir = opts2.source === "project" ? projectCommandsDir(opts2.cwd) : globalCommandsDir();
|
|
7671
|
+
const filepath = `${dir}/${opts2.name}.md`;
|
|
7672
|
+
const data = {};
|
|
7673
|
+
if (opts2.description) data.description = opts2.description;
|
|
7674
|
+
if (opts2.mode) data.mode = opts2.mode;
|
|
7675
|
+
if (opts2.model) data.model = opts2.model;
|
|
7676
|
+
if (opts2.effort) data.effort = opts2.effort;
|
|
7677
|
+
const frontmatter = serializeFrontmatter(data);
|
|
7678
|
+
const content = frontmatter + opts2.template;
|
|
7679
|
+
await mkdir8(dirname5(filepath), { recursive: true });
|
|
7680
|
+
await writeFile8(filepath, content, "utf8");
|
|
7681
|
+
return { filepath };
|
|
7682
|
+
}
|
|
7683
|
+
async function deleteCustomCommand(cmd) {
|
|
7684
|
+
await unlink2(cmd.filepath);
|
|
7685
|
+
}
|
|
7686
|
+
var init_save = __esm({
|
|
7687
|
+
"src/commands/save.ts"() {
|
|
7688
|
+
"use strict";
|
|
7689
|
+
init_frontmatter();
|
|
7690
|
+
init_loader();
|
|
7691
|
+
}
|
|
7692
|
+
});
|
|
7693
|
+
|
|
7694
|
+
// src/ui/command-wizard.tsx
|
|
7695
|
+
import { useState as useState7 } from "react";
|
|
7696
|
+
import { Box as Box13, Text as Text14, useInput as useInput3, useWindowSize as useWindowSize2 } from "ink";
|
|
7697
|
+
import SelectInput5 from "ink-select-input";
|
|
7698
|
+
import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
7699
|
+
function CommandWizard({ theme, mode, initial, existingNames, builtinNames, onDone, onSave }) {
|
|
7700
|
+
const [step, setStep] = useState7("name");
|
|
7701
|
+
const [name, setName] = useState7(initial?.name ?? "");
|
|
7702
|
+
const [description, setDescription] = useState7(initial?.description ?? "");
|
|
7703
|
+
const [template, setTemplate] = useState7(initial?.template ?? "");
|
|
7704
|
+
const [cmdMode, setCmdMode] = useState7(initial?.mode);
|
|
7705
|
+
const [cmdEffort, setCmdEffort] = useState7(initial?.effort);
|
|
7706
|
+
const [cmdModel, setCmdModel] = useState7(initial?.model);
|
|
7707
|
+
const [source, setSource] = useState7(initial?.source ?? "project");
|
|
7708
|
+
const [error, setError] = useState7(null);
|
|
7709
|
+
const { columns } = useWindowSize2();
|
|
7710
|
+
const totalSteps = 5;
|
|
7711
|
+
const stepIndex = step === "name" ? 1 : step === "description" ? 2 : step === "template" ? 3 : step === "advanced" || step === "mode" || step === "effort" || step === "model" ? 4 : step === "location" ? 4 : 5;
|
|
7712
|
+
const isEditingSelf = (n) => initial !== void 0 && initial.name === n;
|
|
7713
|
+
const validateName = (n) => {
|
|
7714
|
+
const trimmed = n.trim();
|
|
7715
|
+
if (!trimmed) return "name is required";
|
|
7716
|
+
if (!NAME_RE.test(trimmed)) return "invalid name: use letters, numbers, _ - / only; must start with a letter";
|
|
7717
|
+
if (builtinNames.has(trimmed.toLowerCase())) return `/${trimmed} is a built-in command`;
|
|
7718
|
+
if (existingNames.includes(trimmed) && !isEditingSelf(trimmed)) return `/${trimmed} already exists`;
|
|
7719
|
+
return null;
|
|
7720
|
+
};
|
|
7721
|
+
useInput3((_input, key) => {
|
|
7722
|
+
if (key.escape) {
|
|
7723
|
+
onDone();
|
|
7724
|
+
}
|
|
7725
|
+
});
|
|
7726
|
+
const handleNameSubmit = (value) => {
|
|
7727
|
+
const trimmed = value.trim();
|
|
7728
|
+
const err = validateName(trimmed);
|
|
7729
|
+
if (err) {
|
|
7730
|
+
setError(err);
|
|
7731
|
+
return;
|
|
7732
|
+
}
|
|
7733
|
+
setError(null);
|
|
7734
|
+
setName(trimmed);
|
|
7735
|
+
setStep("description");
|
|
7736
|
+
};
|
|
7737
|
+
const handleDescriptionSubmit = (value) => {
|
|
7738
|
+
setDescription(value.trim());
|
|
7739
|
+
setStep("template");
|
|
7740
|
+
};
|
|
7741
|
+
const handleTemplateSubmit = (value) => {
|
|
7742
|
+
const trimmed = value.trim();
|
|
7743
|
+
if (!trimmed) {
|
|
7744
|
+
setError("template cannot be empty");
|
|
7745
|
+
return;
|
|
7746
|
+
}
|
|
7747
|
+
setError(null);
|
|
7748
|
+
setTemplate(trimmed);
|
|
7749
|
+
setStep("advanced");
|
|
7750
|
+
};
|
|
7751
|
+
const handleAdvancedChoice = (choice) => {
|
|
7752
|
+
if (choice === "skip") {
|
|
7753
|
+
setCmdMode(void 0);
|
|
7754
|
+
setCmdEffort(void 0);
|
|
7755
|
+
setCmdModel("");
|
|
7756
|
+
if (mode === "edit" && initial) {
|
|
7757
|
+
setSource(initial.source);
|
|
7758
|
+
setStep("confirm");
|
|
7759
|
+
} else {
|
|
7760
|
+
setStep("location");
|
|
7761
|
+
}
|
|
7762
|
+
} else {
|
|
7763
|
+
setStep("mode");
|
|
7764
|
+
}
|
|
7765
|
+
};
|
|
7766
|
+
const handleModeChoice = (m) => {
|
|
7767
|
+
setCmdMode(m === "none" ? void 0 : m);
|
|
7768
|
+
setStep("effort");
|
|
7769
|
+
};
|
|
7770
|
+
const handleEffortChoice = (e) => {
|
|
7771
|
+
setCmdEffort(e === "none" ? void 0 : e);
|
|
7772
|
+
setStep("model");
|
|
7773
|
+
};
|
|
7774
|
+
const handleModelSubmit = (value) => {
|
|
7775
|
+
const trimmed = value.trim();
|
|
7776
|
+
setCmdModel(trimmed || void 0);
|
|
7777
|
+
if (mode === "edit" && initial) {
|
|
7778
|
+
setSource(initial.source);
|
|
7779
|
+
setStep("confirm");
|
|
7780
|
+
} else {
|
|
7781
|
+
setStep("location");
|
|
7782
|
+
}
|
|
7783
|
+
};
|
|
7784
|
+
const handleLocationChoice = (s) => {
|
|
7785
|
+
setSource(s);
|
|
7786
|
+
setStep("confirm");
|
|
7787
|
+
};
|
|
7788
|
+
const handleConfirm = (choice) => {
|
|
7789
|
+
if (choice === "cancel") {
|
|
7790
|
+
onDone();
|
|
7791
|
+
return;
|
|
7792
|
+
}
|
|
7793
|
+
onSave({
|
|
7794
|
+
name,
|
|
7795
|
+
description: description || void 0,
|
|
7796
|
+
template,
|
|
7797
|
+
source,
|
|
7798
|
+
mode: cmdMode,
|
|
7799
|
+
model: cmdModel || void 0,
|
|
7800
|
+
effort: cmdEffort
|
|
7801
|
+
});
|
|
7802
|
+
};
|
|
7803
|
+
const previewContent = () => {
|
|
7804
|
+
const data = {};
|
|
7805
|
+
if (description) data.description = description;
|
|
7806
|
+
if (cmdMode) data.mode = cmdMode;
|
|
7807
|
+
if (cmdModel) data.model = cmdModel;
|
|
7808
|
+
if (cmdEffort) data.effort = cmdEffort;
|
|
7809
|
+
const fm = Object.entries(data).map(([k, v]) => `${k}: ${v}`).join("\n");
|
|
7810
|
+
if (fm) {
|
|
7811
|
+
return `---
|
|
7812
|
+
${fm}
|
|
7813
|
+
---
|
|
7814
|
+
${template}`;
|
|
7815
|
+
}
|
|
7816
|
+
return template;
|
|
7817
|
+
};
|
|
7818
|
+
const renderStep = () => {
|
|
7819
|
+
switch (step) {
|
|
7820
|
+
case "name":
|
|
7821
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
7822
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
7823
|
+
mode === "create" ? "Create" : "Edit",
|
|
7824
|
+
" custom command \u2014 Name (",
|
|
7825
|
+
stepIndex,
|
|
7826
|
+
"/",
|
|
7827
|
+
totalSteps,
|
|
7828
|
+
")"
|
|
7829
|
+
] }),
|
|
7830
|
+
error && /* @__PURE__ */ jsx14(Text14, { color: theme.error, children: error }),
|
|
7831
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
7832
|
+
CustomTextInput,
|
|
7833
|
+
{
|
|
7834
|
+
value: name,
|
|
7835
|
+
onChange: setName,
|
|
7836
|
+
onSubmit: handleNameSubmit,
|
|
7837
|
+
focus: true
|
|
7838
|
+
}
|
|
7839
|
+
) }),
|
|
7840
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "letters, numbers, _ - / only; must start with a letter" })
|
|
7841
|
+
] });
|
|
7842
|
+
case "description":
|
|
7843
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
7844
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
7845
|
+
mode === "create" ? "Create" : "Edit",
|
|
7846
|
+
" custom command \u2014 Description (",
|
|
7847
|
+
stepIndex,
|
|
7848
|
+
"/",
|
|
7849
|
+
totalSteps,
|
|
7850
|
+
")"
|
|
7851
|
+
] }),
|
|
7852
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
7853
|
+
CustomTextInput,
|
|
7854
|
+
{
|
|
7855
|
+
value: description,
|
|
7856
|
+
onChange: setDescription,
|
|
7857
|
+
onSubmit: handleDescriptionSubmit,
|
|
7858
|
+
focus: true
|
|
7859
|
+
}
|
|
7860
|
+
) }),
|
|
7861
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Press Enter to skip" })
|
|
7862
|
+
] });
|
|
7863
|
+
case "template": {
|
|
7864
|
+
const guide = /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingLeft: 1, children: [
|
|
7865
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: "What is this?" }),
|
|
7866
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "A prompt template \u2014 instructions to the AI." }),
|
|
7867
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
|
|
7868
|
+
"When you type /",
|
|
7869
|
+
name || "yourcommand",
|
|
7870
|
+
" later, this gets sent to the model."
|
|
7871
|
+
] }),
|
|
7872
|
+
/* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
7873
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: "Variables" }),
|
|
7874
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
|
|
7875
|
+
" ",
|
|
7876
|
+
"$1, $2 ... \u2192 arguments you type"
|
|
7877
|
+
] }),
|
|
7878
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
|
|
7879
|
+
" ",
|
|
7880
|
+
"$ARGUMENTS \u2192 everything after the command"
|
|
7881
|
+
] })
|
|
7882
|
+
] }),
|
|
7883
|
+
/* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
7884
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: "Dynamic inlines" }),
|
|
7885
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
|
|
7886
|
+
" ",
|
|
7887
|
+
"!`git diff` \u2192 shell output inlined"
|
|
7888
|
+
] }),
|
|
7889
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
|
|
7890
|
+
" ",
|
|
7891
|
+
"@README.md \u2192 file contents inlined"
|
|
7892
|
+
] })
|
|
7893
|
+
] }),
|
|
7894
|
+
/* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
7895
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: "Example" }),
|
|
7896
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Review this PR diff:" }),
|
|
7897
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "!`git diff main...HEAD`" }),
|
|
7898
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Focus on: $1" })
|
|
7899
|
+
] })
|
|
7900
|
+
] });
|
|
7901
|
+
const inputArea = /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, children: [
|
|
7902
|
+
error && /* @__PURE__ */ jsx14(Text14, { color: theme.error, children: error }),
|
|
7903
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
7904
|
+
CustomTextInput,
|
|
7905
|
+
{
|
|
7906
|
+
value: template,
|
|
7907
|
+
onChange: setTemplate,
|
|
7908
|
+
onSubmit: handleTemplateSubmit,
|
|
7909
|
+
focus: true,
|
|
7910
|
+
enablePaste: true
|
|
7911
|
+
}
|
|
7912
|
+
) }),
|
|
7913
|
+
columns < 100 && /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
7914
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Paste multi-line templates with Ctrl+V." }),
|
|
7915
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Variables: $1 $2 ... $ARGUMENTS Shell: !`cmd` File: @path" })
|
|
7916
|
+
] })
|
|
7917
|
+
] });
|
|
7918
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
7919
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
7920
|
+
mode === "create" ? "Create" : "Edit",
|
|
7921
|
+
" custom command \u2014 Template (",
|
|
7922
|
+
stepIndex,
|
|
7923
|
+
"/",
|
|
7924
|
+
totalSteps,
|
|
7925
|
+
")"
|
|
7926
|
+
] }),
|
|
7927
|
+
columns >= 100 ? /* @__PURE__ */ jsxs13(Box13, { flexDirection: "row", marginTop: 1, children: [
|
|
7928
|
+
/* @__PURE__ */ jsx14(Box13, { flexDirection: "column", width: "50%", children: inputArea }),
|
|
7929
|
+
/* @__PURE__ */ jsx14(Box13, { flexDirection: "column", width: "50%", children: guide })
|
|
7930
|
+
] }) : /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", marginTop: 1, children: inputArea })
|
|
7931
|
+
] });
|
|
7932
|
+
}
|
|
7933
|
+
case "advanced": {
|
|
7934
|
+
const items = [
|
|
7935
|
+
{ label: "Set advanced options", value: "set", key: "set" },
|
|
7936
|
+
{ label: "Skip", value: "skip", key: "skip" },
|
|
7937
|
+
{ label: "\u2190 Cancel", value: "cancel", key: "cancel" }
|
|
7938
|
+
];
|
|
7939
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
7940
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
7941
|
+
mode === "create" ? "Create" : "Edit",
|
|
7942
|
+
" custom command \u2014 Options (",
|
|
7943
|
+
stepIndex,
|
|
7944
|
+
"/",
|
|
7945
|
+
totalSteps,
|
|
7946
|
+
")"
|
|
7947
|
+
] }),
|
|
7948
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
7949
|
+
SelectInput5,
|
|
7950
|
+
{
|
|
7951
|
+
items,
|
|
7952
|
+
onSelect: (item) => {
|
|
7953
|
+
if (item.value === "cancel") onDone();
|
|
7954
|
+
else handleAdvancedChoice(item.value);
|
|
7955
|
+
}
|
|
7956
|
+
}
|
|
7957
|
+
) })
|
|
7958
|
+
] });
|
|
7959
|
+
}
|
|
7960
|
+
case "mode": {
|
|
7961
|
+
const items = [
|
|
7962
|
+
{ label: cmdMode === void 0 ? "none \xB7 current" : "none", value: "none", key: "none" },
|
|
7963
|
+
{ label: cmdMode === "edit" ? "edit \xB7 current" : "edit", value: "edit", key: "edit" },
|
|
7964
|
+
{ label: cmdMode === "plan" ? "plan \xB7 current" : "plan", value: "plan", key: "plan" },
|
|
7965
|
+
{ label: cmdMode === "auto" ? "auto \xB7 current" : "auto", value: "auto", key: "auto" },
|
|
7966
|
+
{ label: "\u2190 Back", value: "__back__", key: "__back__" }
|
|
7967
|
+
];
|
|
7968
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
7969
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
7970
|
+
"Mode override (",
|
|
7971
|
+
stepIndex,
|
|
7972
|
+
"/",
|
|
7973
|
+
totalSteps,
|
|
7974
|
+
")"
|
|
7975
|
+
] }),
|
|
7976
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Saved to file but not yet enforced at runtime" }),
|
|
7977
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
7978
|
+
SelectInput5,
|
|
7979
|
+
{
|
|
7980
|
+
items,
|
|
7981
|
+
onSelect: (item) => {
|
|
7982
|
+
if (item.value === "__back__") setStep("advanced");
|
|
7983
|
+
else handleModeChoice(item.value);
|
|
7984
|
+
}
|
|
7985
|
+
}
|
|
7986
|
+
) })
|
|
7987
|
+
] });
|
|
7988
|
+
}
|
|
7989
|
+
case "effort": {
|
|
7990
|
+
const items = [
|
|
7991
|
+
{ label: cmdEffort === void 0 ? "none \xB7 current" : "none", value: "none", key: "none" },
|
|
7992
|
+
{ label: cmdEffort === "low" ? "low \xB7 current" : "low", value: "low", key: "low" },
|
|
7993
|
+
{ label: cmdEffort === "medium" ? "medium \xB7 current" : "medium", value: "medium", key: "medium" },
|
|
7994
|
+
{ label: cmdEffort === "high" ? "high \xB7 current" : "high", value: "high", key: "high" },
|
|
7995
|
+
{ label: "\u2190 Back", value: "__back__", key: "__back__" }
|
|
7996
|
+
];
|
|
7997
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
7998
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
7999
|
+
"Reasoning effort (",
|
|
8000
|
+
stepIndex,
|
|
8001
|
+
"/",
|
|
8002
|
+
totalSteps,
|
|
8003
|
+
")"
|
|
8004
|
+
] }),
|
|
8005
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
8006
|
+
SelectInput5,
|
|
8007
|
+
{
|
|
8008
|
+
items,
|
|
8009
|
+
onSelect: (item) => {
|
|
8010
|
+
if (item.value === "__back__") setStep("mode");
|
|
8011
|
+
else handleEffortChoice(item.value);
|
|
8012
|
+
}
|
|
8013
|
+
}
|
|
8014
|
+
) })
|
|
8015
|
+
] });
|
|
8016
|
+
}
|
|
8017
|
+
case "model":
|
|
8018
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
8019
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
8020
|
+
"Model override (",
|
|
8021
|
+
stepIndex,
|
|
8022
|
+
"/",
|
|
8023
|
+
totalSteps,
|
|
8024
|
+
")"
|
|
8025
|
+
] }),
|
|
8026
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
8027
|
+
CustomTextInput,
|
|
8028
|
+
{
|
|
8029
|
+
value: cmdModel ?? "",
|
|
8030
|
+
onChange: setCmdModel,
|
|
8031
|
+
onSubmit: handleModelSubmit,
|
|
8032
|
+
focus: true
|
|
8033
|
+
}
|
|
8034
|
+
) }),
|
|
8035
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Press Enter to skip" })
|
|
8036
|
+
] });
|
|
8037
|
+
case "location": {
|
|
8038
|
+
const items = [
|
|
8039
|
+
{ label: source === "project" ? "Project \xB7 current" : "Project", value: "project", key: "project" },
|
|
8040
|
+
{ label: source === "global" ? "Global \xB7 current" : "Global", value: "global", key: "global" },
|
|
8041
|
+
{ label: "\u2190 Back", value: "__back__", key: "__back__" }
|
|
8042
|
+
];
|
|
8043
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
8044
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
8045
|
+
"Save location (",
|
|
8046
|
+
stepIndex,
|
|
8047
|
+
"/",
|
|
8048
|
+
totalSteps,
|
|
8049
|
+
")"
|
|
8050
|
+
] }),
|
|
8051
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
8052
|
+
SelectInput5,
|
|
8053
|
+
{
|
|
8054
|
+
items,
|
|
8055
|
+
onSelect: (item) => {
|
|
8056
|
+
if (item.value === "__back__") setStep("advanced");
|
|
8057
|
+
else handleLocationChoice(item.value);
|
|
8058
|
+
}
|
|
8059
|
+
}
|
|
8060
|
+
) }),
|
|
8061
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: "Project: .kimiflare/commands/ Global: ~/.config/kimiflare/commands/" })
|
|
8062
|
+
] });
|
|
8063
|
+
}
|
|
8064
|
+
case "confirm": {
|
|
8065
|
+
const items = [
|
|
8066
|
+
{ label: "Save", value: "save", key: "save" },
|
|
8067
|
+
{ label: "Cancel", value: "cancel", key: "cancel" }
|
|
8068
|
+
];
|
|
8069
|
+
return /* @__PURE__ */ jsxs13(Fragment2, { children: [
|
|
8070
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.accent, bold: true, children: [
|
|
8071
|
+
mode === "create" ? "Create" : "Edit",
|
|
8072
|
+
" custom command \u2014 Confirm (",
|
|
8073
|
+
stepIndex,
|
|
8074
|
+
"/",
|
|
8075
|
+
totalSteps,
|
|
8076
|
+
")"
|
|
8077
|
+
] }),
|
|
8078
|
+
/* @__PURE__ */ jsxs13(Text14, { color: theme.info.color, dimColor: true, children: [
|
|
8079
|
+
source === "project" ? ".kimiflare/commands/" : "~/.config/kimiflare/commands/",
|
|
8080
|
+
name,
|
|
8081
|
+
".md"
|
|
8082
|
+
] }),
|
|
8083
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, flexDirection: "column", children: previewContent().split("\n").map((line, i) => /* @__PURE__ */ jsx14(Text14, { color: theme.info.color, dimColor: true, children: line || " " }, i)) }),
|
|
8084
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(
|
|
8085
|
+
SelectInput5,
|
|
8086
|
+
{
|
|
8087
|
+
items,
|
|
8088
|
+
onSelect: (item) => handleConfirm(item.value)
|
|
8089
|
+
}
|
|
8090
|
+
) })
|
|
8091
|
+
] });
|
|
8092
|
+
}
|
|
8093
|
+
}
|
|
8094
|
+
};
|
|
8095
|
+
return /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: renderStep() });
|
|
8096
|
+
}
|
|
8097
|
+
var NAME_RE;
|
|
8098
|
+
var init_command_wizard = __esm({
|
|
8099
|
+
"src/ui/command-wizard.tsx"() {
|
|
8100
|
+
"use strict";
|
|
8101
|
+
init_text_input();
|
|
8102
|
+
NAME_RE = /^[a-zA-Z][a-zA-Z0-9_\-/]*$/;
|
|
8103
|
+
}
|
|
8104
|
+
});
|
|
8105
|
+
|
|
8106
|
+
// src/ui/command-picker.tsx
|
|
8107
|
+
import { Box as Box14, Text as Text15 } from "ink";
|
|
8108
|
+
import SelectInput6 from "ink-select-input";
|
|
8109
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
8110
|
+
function CommandPicker({ theme, commands, title, onPick }) {
|
|
8111
|
+
const items = commands.map((cmd) => ({
|
|
8112
|
+
label: `/${cmd.name.padEnd(20)} ${cmd.description ?? ""}`,
|
|
8113
|
+
value: cmd,
|
|
8114
|
+
key: cmd.name
|
|
8115
|
+
}));
|
|
8116
|
+
items.push({ label: "\u2190 Cancel", value: null, key: "__cancel__" });
|
|
8117
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
8118
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.accent, bold: true, children: title }),
|
|
8119
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select." }),
|
|
8120
|
+
/* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(
|
|
8121
|
+
SelectInput6,
|
|
8122
|
+
{
|
|
8123
|
+
items,
|
|
8124
|
+
onSelect: (item) => {
|
|
8125
|
+
if (item.key === "__cancel__") {
|
|
8126
|
+
onPick(null);
|
|
8127
|
+
} else {
|
|
8128
|
+
onPick(item.value);
|
|
8129
|
+
}
|
|
8130
|
+
}
|
|
8131
|
+
}
|
|
8132
|
+
) })
|
|
8133
|
+
] });
|
|
8134
|
+
}
|
|
8135
|
+
var init_command_picker = __esm({
|
|
8136
|
+
"src/ui/command-picker.tsx"() {
|
|
8137
|
+
"use strict";
|
|
8138
|
+
}
|
|
8139
|
+
});
|
|
8140
|
+
|
|
8141
|
+
// src/ui/command-list.tsx
|
|
8142
|
+
import { Box as Box15, Text as Text16, useInput as useInput4 } from "ink";
|
|
8143
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
8144
|
+
function CommandList({ theme, commands, onDone }) {
|
|
8145
|
+
useInput4((_input, key) => {
|
|
8146
|
+
if (key.escape) {
|
|
8147
|
+
onDone();
|
|
8148
|
+
}
|
|
8149
|
+
});
|
|
8150
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
8151
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.accent, bold: true, children: "Custom commands" }),
|
|
8152
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.info.color, dimColor: false, children: "Esc to close." }),
|
|
8153
|
+
/* @__PURE__ */ jsxs15(Box15, { marginTop: 1, flexDirection: "column", children: [
|
|
8154
|
+
commands.length === 0 && /* @__PURE__ */ jsx16(Text16, { color: theme.info.color, dimColor: true, children: "No custom commands found." }),
|
|
8155
|
+
commands.map((cmd) => /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", marginBottom: 1, children: [
|
|
8156
|
+
/* @__PURE__ */ jsxs15(Text16, { color: theme.accent, bold: true, children: [
|
|
8157
|
+
"/",
|
|
8158
|
+
cmd.name
|
|
8159
|
+
] }),
|
|
8160
|
+
/* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8161
|
+
" ",
|
|
8162
|
+
"source: ",
|
|
8163
|
+
cmd.source
|
|
8164
|
+
] }),
|
|
8165
|
+
/* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8166
|
+
" ",
|
|
8167
|
+
"path: ",
|
|
8168
|
+
cmd.filepath
|
|
8169
|
+
] }),
|
|
8170
|
+
cmd.description && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8171
|
+
" ",
|
|
8172
|
+
"desc: ",
|
|
8173
|
+
cmd.description
|
|
8174
|
+
] }),
|
|
8175
|
+
cmd.mode && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8176
|
+
" ",
|
|
8177
|
+
"mode: ",
|
|
8178
|
+
cmd.mode
|
|
8179
|
+
] }),
|
|
8180
|
+
cmd.effort && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8181
|
+
" ",
|
|
8182
|
+
"effort: ",
|
|
8183
|
+
cmd.effort
|
|
8184
|
+
] }),
|
|
8185
|
+
cmd.model && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8186
|
+
" ",
|
|
8187
|
+
"model: ",
|
|
8188
|
+
cmd.model
|
|
8189
|
+
] }),
|
|
8190
|
+
/* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8191
|
+
" ",
|
|
8192
|
+
"template:"
|
|
8193
|
+
] }),
|
|
8194
|
+
cmd.template.split("\n").slice(0, 5).map((line, i) => /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8195
|
+
" ",
|
|
8196
|
+
line || " "
|
|
8197
|
+
] }, i)),
|
|
8198
|
+
cmd.template.split("\n").length > 5 && /* @__PURE__ */ jsxs15(Text16, { color: theme.info.color, dimColor: true, children: [
|
|
8199
|
+
" ",
|
|
8200
|
+
"..."
|
|
8201
|
+
] })
|
|
8202
|
+
] }, cmd.name))
|
|
8203
|
+
] })
|
|
8204
|
+
] });
|
|
8205
|
+
}
|
|
8206
|
+
var init_command_list = __esm({
|
|
8207
|
+
"src/ui/command-list.tsx"() {
|
|
8208
|
+
"use strict";
|
|
8209
|
+
}
|
|
8210
|
+
});
|
|
8211
|
+
|
|
8212
|
+
// src/app.tsx
|
|
8213
|
+
var app_exports = {};
|
|
8214
|
+
__export(app_exports, {
|
|
8215
|
+
renderApp: () => renderApp
|
|
8216
|
+
});
|
|
8217
|
+
import { useState as useState8, useRef as useRef3, useEffect as useEffect4, useCallback } from "react";
|
|
8218
|
+
import { Box as Box16, Text as Text17, useApp, useInput as useInput5, render } from "ink";
|
|
8219
|
+
import SelectInput7 from "ink-select-input";
|
|
8220
|
+
import { existsSync } from "fs";
|
|
8221
|
+
import { join as join13 } from "path";
|
|
8222
|
+
import { unlink as unlink3 } from "fs/promises";
|
|
8223
|
+
import { spawn as spawn2 } from "child_process";
|
|
8224
|
+
import { platform as platform2 } from "os";
|
|
8225
|
+
import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
8226
|
+
function gatewayFromConfig(cfg) {
|
|
8227
|
+
if (!cfg.aiGatewayId) return void 0;
|
|
8228
|
+
return {
|
|
8229
|
+
id: cfg.aiGatewayId,
|
|
8230
|
+
cacheTtl: cfg.aiGatewayCacheTtl,
|
|
8231
|
+
skipCache: cfg.aiGatewaySkipCache,
|
|
8232
|
+
collectLogPayload: cfg.aiGatewayCollectLogPayload,
|
|
8233
|
+
metadata: cfg.aiGatewayMetadata
|
|
8234
|
+
};
|
|
8235
|
+
}
|
|
8236
|
+
function gatewayUsageLookupFromConfig(cfg, meta) {
|
|
8237
|
+
if (!cfg.aiGatewayId || !meta) return void 0;
|
|
8238
|
+
return {
|
|
8239
|
+
accountId: cfg.accountId,
|
|
8240
|
+
apiToken: cfg.apiToken,
|
|
8241
|
+
gatewayId: cfg.aiGatewayId,
|
|
8242
|
+
meta
|
|
8243
|
+
};
|
|
8244
|
+
}
|
|
8245
|
+
function openBrowser(url) {
|
|
8246
|
+
const cmd = platform2() === "darwin" ? "open" : platform2() === "win32" ? "start" : "xdg-open";
|
|
8247
|
+
const child = spawn2(cmd, [url], { detached: true, stdio: "ignore" });
|
|
8248
|
+
child.unref();
|
|
8249
|
+
}
|
|
8250
|
+
function capEvents(prev) {
|
|
8251
|
+
if (prev.length <= MAX_EVENTS) return prev;
|
|
8252
|
+
return prev.slice(prev.length - MAX_EVENTS);
|
|
8253
|
+
}
|
|
8254
|
+
function compactEventsVisual(prev, keepLastTurns) {
|
|
8255
|
+
let seen = 0;
|
|
8256
|
+
let cutoff = -1;
|
|
8257
|
+
for (let i = prev.length - 1; i >= 0; i--) {
|
|
8258
|
+
if (prev[i].kind === "user") {
|
|
8259
|
+
seen++;
|
|
8260
|
+
if (seen === keepLastTurns + 1) {
|
|
8261
|
+
cutoff = i;
|
|
8262
|
+
break;
|
|
8263
|
+
}
|
|
8264
|
+
}
|
|
8265
|
+
}
|
|
8266
|
+
if (cutoff <= 0) return prev;
|
|
8267
|
+
const kept = prev.slice(cutoff);
|
|
8268
|
+
return [
|
|
8269
|
+
{ kind: "info", key: mkKey(), text: `\xB7\xB7\xB7 ${cutoff} earlier messages compacted \xB7\xB7\xB7` },
|
|
8270
|
+
...kept
|
|
8271
|
+
];
|
|
8272
|
+
}
|
|
8273
|
+
function makePrefixMessages(cacheStable, model, mode, tools) {
|
|
8274
|
+
if (cacheStable) {
|
|
8275
|
+
return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
|
|
8276
|
+
}
|
|
8277
|
+
return [
|
|
8278
|
+
{
|
|
8279
|
+
role: "system",
|
|
8280
|
+
content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
|
|
8281
|
+
}
|
|
8282
|
+
];
|
|
8283
|
+
}
|
|
8284
|
+
function findImagePaths(text) {
|
|
8285
|
+
const paths = [];
|
|
8286
|
+
for (const token of text.split(/\s+/)) {
|
|
8287
|
+
const clean = token.replace(/^["']|["',;:!?]$/g, "").replace(/[.,;:!?]$/, "");
|
|
8288
|
+
if (isImagePath(clean) && existsSync(clean)) {
|
|
8289
|
+
paths.push(clean);
|
|
8290
|
+
}
|
|
8291
|
+
}
|
|
8292
|
+
return [...new Set(paths)];
|
|
8293
|
+
}
|
|
8294
|
+
function App({ initialCfg, initialUpdateResult }) {
|
|
8295
|
+
const { exit } = useApp();
|
|
8296
|
+
const [cfg, setCfg] = useState8(initialCfg);
|
|
8297
|
+
const [events, setRawEvents] = useState8([]);
|
|
8298
|
+
const setEvents = useCallback(
|
|
8299
|
+
(updater) => {
|
|
8300
|
+
setRawEvents((prev) => {
|
|
8301
|
+
const next = typeof updater === "function" ? updater(prev) : updater;
|
|
8302
|
+
return capEvents(next);
|
|
8303
|
+
});
|
|
8304
|
+
},
|
|
8305
|
+
[]
|
|
8306
|
+
);
|
|
8307
|
+
const [input, setInput] = useState8("");
|
|
8308
|
+
const [busy, setBusy] = useState8(false);
|
|
8309
|
+
const [usage, setUsage] = useState8(null);
|
|
8310
|
+
const [sessionUsage, setSessionUsage] = useState8(null);
|
|
8311
|
+
const [gatewayMeta, setGatewayMeta] = useState8(null);
|
|
8312
|
+
const [showReasoning, setShowReasoning] = useState8(false);
|
|
8313
|
+
const [perm, setPerm] = useState8(null);
|
|
8314
|
+
const [queue, setQueue] = useState8([]);
|
|
8315
|
+
const [history, setHistory] = useState8([]);
|
|
8316
|
+
const [historyIndex, setHistoryIndex] = useState8(-1);
|
|
8317
|
+
const [draftInput, setDraftInput] = useState8("");
|
|
8318
|
+
const [mode, setMode] = useState8("edit");
|
|
8319
|
+
const [codeMode, setCodeMode] = useState8(initialCfg?.codeMode ?? false);
|
|
8320
|
+
const [effort, setEffort] = useState8(
|
|
8321
|
+
initialCfg?.reasoningEffort ?? DEFAULT_REASONING_EFFORT
|
|
8322
|
+
);
|
|
8323
|
+
const [theme, setTheme] = useState8(resolveTheme(initialCfg?.theme));
|
|
8324
|
+
const [resumeSessions, setResumeSessions] = useState8(null);
|
|
8325
|
+
const [showThemePicker, setShowThemePicker] = useState8(false);
|
|
8326
|
+
const [showHelpMenu, setShowHelpMenu] = useState8(false);
|
|
8327
|
+
const [originalTheme, setOriginalTheme] = useState8(null);
|
|
8328
|
+
const [commandWizard, setCommandWizard] = useState8(null);
|
|
8329
|
+
const [commandPicker, setCommandPicker] = useState8(null);
|
|
8330
|
+
const [commandToDelete, setCommandToDelete] = useState8(null);
|
|
8331
|
+
const [showCommandList, setShowCommandList] = useState8(false);
|
|
8332
|
+
const [tasks, setTasks] = useState8([]);
|
|
8333
|
+
const [tasksStartedAt, setTasksStartedAt] = useState8(null);
|
|
8334
|
+
const [tasksStartTokens, setTasksStartTokens] = useState8(0);
|
|
8335
|
+
const [turnStartedAt, setTurnStartedAt] = useState8(null);
|
|
8336
|
+
const [verbose, setVerbose] = useState8(false);
|
|
8337
|
+
const [hasUpdate, setHasUpdate] = useState8(initialUpdateResult?.hasUpdate ?? false);
|
|
8338
|
+
const [latestVersion, setLatestVersion] = useState8(initialUpdateResult?.latestVersion ?? null);
|
|
8339
|
+
const cacheStableRef = useRef3(initialCfg?.cacheStablePrompts !== false);
|
|
8340
|
+
const messagesRef = useRef3(
|
|
8341
|
+
makePrefixMessages(cacheStableRef.current, cfg?.model ?? DEFAULT_MODEL, "edit", ALL_TOOLS)
|
|
8342
|
+
);
|
|
8343
|
+
const executorRef = useRef3(new ToolExecutor(ALL_TOOLS));
|
|
8344
|
+
const activeAsstIdRef = useRef3(null);
|
|
7062
8345
|
const activeControllerRef = useRef3(null);
|
|
7063
8346
|
const sessionIdRef = useRef3(null);
|
|
7064
8347
|
const modeRef = useRef3(mode);
|
|
@@ -7076,8 +8359,10 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
7076
8359
|
const mcpToolsRef = useRef3([]);
|
|
7077
8360
|
const mcpInitRef = useRef3(false);
|
|
7078
8361
|
const memoryManagerRef = useRef3(null);
|
|
8362
|
+
const sessionStartRecallRef = useRef3(null);
|
|
7079
8363
|
const pendingTextRef = useRef3(/* @__PURE__ */ new Map());
|
|
7080
8364
|
const flushTimeoutRef = useRef3(null);
|
|
8365
|
+
const customCommandsRef = useRef3([]);
|
|
7081
8366
|
useEffect4(() => {
|
|
7082
8367
|
if (!cfg) return;
|
|
7083
8368
|
void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(
|
|
@@ -7104,12 +8389,13 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
7104
8389
|
}
|
|
7105
8390
|
});
|
|
7106
8391
|
if (cfg.memoryEnabled) {
|
|
7107
|
-
const dbPath = cfg.memoryDbPath ??
|
|
8392
|
+
const dbPath = cfg.memoryDbPath ?? join13(process.cwd(), ".kimiflare", "memory.db");
|
|
7108
8393
|
const manager = new MemoryManager({
|
|
7109
8394
|
dbPath,
|
|
7110
8395
|
accountId: cfg.accountId,
|
|
7111
8396
|
apiToken: cfg.apiToken,
|
|
7112
8397
|
model: cfg.model,
|
|
8398
|
+
plumbingModel: cfg.plumbingModel,
|
|
7113
8399
|
embeddingModel: cfg.memoryEmbeddingModel,
|
|
7114
8400
|
gateway: gatewayFromConfig(cfg),
|
|
7115
8401
|
maxAgeDays: cfg.memoryMaxAgeDays ?? RETENTION.memoryMaxAgeDays,
|
|
@@ -7122,7 +8408,7 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
7122
8408
|
if (total > 0) {
|
|
7123
8409
|
setEvents((e) => [
|
|
7124
8410
|
...e,
|
|
7125
|
-
{ kind: "
|
|
8411
|
+
{ kind: "memory", key: mkKey(), text: `memory cleanup: removed ${total} stale entries` }
|
|
7126
8412
|
]);
|
|
7127
8413
|
}
|
|
7128
8414
|
});
|
|
@@ -7130,15 +8416,59 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
7130
8416
|
if (fixed > 0) {
|
|
7131
8417
|
setEvents((e) => [
|
|
7132
8418
|
...e,
|
|
7133
|
-
{ kind: "
|
|
8419
|
+
{ kind: "memory", key: mkKey(), text: `memory backfill: embedded ${fixed} un-vectorized entries` }
|
|
7134
8420
|
]);
|
|
7135
8421
|
}
|
|
7136
8422
|
});
|
|
8423
|
+
const cwd = process.cwd();
|
|
8424
|
+
sessionStartRecallRef.current = (async () => {
|
|
8425
|
+
try {
|
|
8426
|
+
const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
|
|
8427
|
+
if (results.length > 0) {
|
|
8428
|
+
const text = await manager.synthesizeRecalled(results);
|
|
8429
|
+
const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
|
|
8430
|
+
const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
|
|
8431
|
+
messagesRef.current.splice(insertIdx, 0, { role: "system", content: text });
|
|
8432
|
+
setEvents((e) => [
|
|
8433
|
+
...e,
|
|
8434
|
+
{ kind: "memory", key: mkKey(), text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} about this repo` }
|
|
8435
|
+
]);
|
|
8436
|
+
}
|
|
8437
|
+
} catch {
|
|
8438
|
+
}
|
|
8439
|
+
})();
|
|
7137
8440
|
} else {
|
|
7138
8441
|
memoryManagerRef.current?.close();
|
|
7139
8442
|
memoryManagerRef.current = null;
|
|
7140
8443
|
}
|
|
7141
|
-
|
|
8444
|
+
void loadCustomCommands(process.cwd()).then(({ commands, warnings }) => {
|
|
8445
|
+
customCommandsRef.current = commands;
|
|
8446
|
+
for (const w of warnings) {
|
|
8447
|
+
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `commands: ${w}` }]);
|
|
8448
|
+
}
|
|
8449
|
+
const shadowed = commands.filter((c) => BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase()));
|
|
8450
|
+
for (const c of shadowed) {
|
|
8451
|
+
setEvents((e) => [
|
|
8452
|
+
...e,
|
|
8453
|
+
{ kind: "info", key: mkKey(), text: `commands: /${c.name} (${c.filepath}) shadowed by built-in \u2014 will not run` }
|
|
8454
|
+
]);
|
|
8455
|
+
}
|
|
8456
|
+
});
|
|
8457
|
+
}, [cfg, setEvents]);
|
|
8458
|
+
const reloadCustomCommands = useCallback(async () => {
|
|
8459
|
+
const { commands, warnings } = await loadCustomCommands(process.cwd());
|
|
8460
|
+
customCommandsRef.current = commands;
|
|
8461
|
+
for (const w of warnings) {
|
|
8462
|
+
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `commands: ${w}` }]);
|
|
8463
|
+
}
|
|
8464
|
+
const shadowed = commands.filter((c) => BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase()));
|
|
8465
|
+
for (const c of shadowed) {
|
|
8466
|
+
setEvents((e) => [
|
|
8467
|
+
...e,
|
|
8468
|
+
{ kind: "info", key: mkKey(), text: `commands: /${c.name} (${c.filepath}) shadowed by built-in \u2014 will not run` }
|
|
8469
|
+
]);
|
|
8470
|
+
}
|
|
8471
|
+
}, [setEvents]);
|
|
7142
8472
|
useEffect4(() => {
|
|
7143
8473
|
if (!cfg || updateCheckedRef.current) return;
|
|
7144
8474
|
updateCheckedRef.current = true;
|
|
@@ -7341,12 +8671,13 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
7341
8671
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7342
8672
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7343
8673
|
messages: messagesRef.current,
|
|
7344
|
-
sessionState: compiledContextRef.current ? sessionStateRef.current : void 0
|
|
8674
|
+
sessionState: compiledContextRef.current ? sessionStateRef.current : void 0,
|
|
8675
|
+
artifactStore: serializeArtifactStore(artifactStoreRef.current)
|
|
7345
8676
|
});
|
|
7346
8677
|
} catch {
|
|
7347
8678
|
}
|
|
7348
8679
|
}, [cfg, ensureSessionId]);
|
|
7349
|
-
|
|
8680
|
+
useInput5((inputChar, key) => {
|
|
7350
8681
|
if (key.ctrl && inputChar === "c") {
|
|
7351
8682
|
if (busy && activeControllerRef.current) {
|
|
7352
8683
|
activeControllerRef.current.abort();
|
|
@@ -7537,13 +8868,13 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
7537
8868
|
}
|
|
7538
8869
|
const cwd = process.cwd();
|
|
7539
8870
|
for (const name of ["KIMI.md", "KIMIFLARE.md", "AGENT.md"]) {
|
|
7540
|
-
if (existsSync(
|
|
8871
|
+
if (existsSync(join13(cwd, name))) {
|
|
7541
8872
|
setEvents((e) => [
|
|
7542
8873
|
...e,
|
|
7543
8874
|
{
|
|
7544
8875
|
kind: "info",
|
|
7545
8876
|
key: mkKey(),
|
|
7546
|
-
text: `${name} already exists at ${
|
|
8877
|
+
text: `${name} already exists at ${join13(cwd, name)} \u2014 delete it first if you want to regenerate`
|
|
7547
8878
|
}
|
|
7548
8879
|
]);
|
|
7549
8880
|
return;
|
|
@@ -7671,7 +9002,7 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
7671
9002
|
})
|
|
7672
9003
|
}
|
|
7673
9004
|
});
|
|
7674
|
-
if (existsSync(
|
|
9005
|
+
if (existsSync(join13(cwd, "KIMI.md"))) {
|
|
7675
9006
|
if (cacheStableRef.current) {
|
|
7676
9007
|
messagesRef.current[1] = {
|
|
7677
9008
|
role: "system",
|
|
@@ -7722,8 +9053,26 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
7722
9053
|
sessionIdRef.current = file.id;
|
|
7723
9054
|
if (file.sessionState && compiledContextRef.current) {
|
|
7724
9055
|
sessionStateRef.current = file.sessionState;
|
|
9056
|
+
}
|
|
9057
|
+
if (file.artifactStore) {
|
|
9058
|
+
artifactStoreRef.current = deserializeArtifactStore(file.artifactStore);
|
|
9059
|
+
} else {
|
|
7725
9060
|
artifactStoreRef.current = new ArtifactStore();
|
|
7726
9061
|
}
|
|
9062
|
+
const manager = memoryManagerRef.current;
|
|
9063
|
+
if (manager) {
|
|
9064
|
+
try {
|
|
9065
|
+
const cwd = process.cwd();
|
|
9066
|
+
const results = await manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
|
|
9067
|
+
if (results.length > 0) {
|
|
9068
|
+
const text = await manager.synthesizeRecalled(results);
|
|
9069
|
+
const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
|
|
9070
|
+
const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
|
|
9071
|
+
messagesRef.current.splice(insertIdx, 0, { role: "system", content: text });
|
|
9072
|
+
}
|
|
9073
|
+
} catch {
|
|
9074
|
+
}
|
|
9075
|
+
}
|
|
7727
9076
|
setEvents([
|
|
7728
9077
|
{
|
|
7729
9078
|
kind: "info",
|
|
@@ -8035,13 +9384,30 @@ use: /thinking low | medium | high`
|
|
|
8035
9384
|
return true;
|
|
8036
9385
|
}
|
|
8037
9386
|
if (c === "/memory") {
|
|
8038
|
-
if (!cfg
|
|
8039
|
-
|
|
9387
|
+
if (!cfg) return true;
|
|
9388
|
+
if (arg === "on") {
|
|
9389
|
+
const next = { ...cfg, memoryEnabled: true };
|
|
9390
|
+
setCfg(next);
|
|
9391
|
+
void saveConfig(next).catch(() => {
|
|
9392
|
+
});
|
|
9393
|
+
setEvents((e) => [...e, { kind: "memory", key: mkKey(), text: "memory enabled" }]);
|
|
9394
|
+
return true;
|
|
9395
|
+
}
|
|
9396
|
+
if (arg === "off") {
|
|
9397
|
+
const next = { ...cfg, memoryEnabled: false };
|
|
9398
|
+
setCfg(next);
|
|
9399
|
+
void saveConfig(next).catch(() => {
|
|
9400
|
+
});
|
|
9401
|
+
setEvents((e) => [...e, { kind: "memory", key: mkKey(), text: "memory disabled" }]);
|
|
9402
|
+
return true;
|
|
9403
|
+
}
|
|
9404
|
+
if (!cfg.memoryEnabled) {
|
|
9405
|
+
setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "memory is disabled. Use /memory on to enable it, or set KIMIFLARE_MEMORY_ENABLED=1" }]);
|
|
8040
9406
|
return true;
|
|
8041
9407
|
}
|
|
8042
9408
|
if (arg === "clear") {
|
|
8043
9409
|
const cleared = memoryManagerRef.current?.clearRepo(process.cwd()) ?? 0;
|
|
8044
|
-
setEvents((e) => [...e, { kind: "
|
|
9410
|
+
setEvents((e) => [...e, { kind: "memory", key: mkKey(), text: `cleared ${cleared} memories for this repo` }]);
|
|
8045
9411
|
return true;
|
|
8046
9412
|
}
|
|
8047
9413
|
if (arg.startsWith("search ")) {
|
|
@@ -8176,7 +9542,7 @@ ${lines.join("\n")}` }]);
|
|
|
8176
9542
|
return true;
|
|
8177
9543
|
}
|
|
8178
9544
|
if (c === "/logout") {
|
|
8179
|
-
|
|
9545
|
+
unlink3(configPath()).catch(() => {
|
|
8180
9546
|
});
|
|
8181
9547
|
setEvents((e) => [
|
|
8182
9548
|
...e,
|
|
@@ -8185,28 +9551,123 @@ ${lines.join("\n")}` }]);
|
|
|
8185
9551
|
setCfg(null);
|
|
8186
9552
|
return true;
|
|
8187
9553
|
}
|
|
8188
|
-
if (c === "/
|
|
9554
|
+
if (c === "/command") {
|
|
9555
|
+
const sub = rest[0]?.toLowerCase() ?? "";
|
|
9556
|
+
if (sub === "create") {
|
|
9557
|
+
setCommandWizard({ mode: "create" });
|
|
9558
|
+
return true;
|
|
9559
|
+
}
|
|
9560
|
+
if (sub === "edit") {
|
|
9561
|
+
setCommandPicker({ mode: "edit" });
|
|
9562
|
+
return true;
|
|
9563
|
+
}
|
|
9564
|
+
if (sub === "delete") {
|
|
9565
|
+
setCommandPicker({ mode: "delete" });
|
|
9566
|
+
return true;
|
|
9567
|
+
}
|
|
9568
|
+
if (sub === "list") {
|
|
9569
|
+
setShowCommandList(true);
|
|
9570
|
+
return true;
|
|
9571
|
+
}
|
|
8189
9572
|
setEvents((e) => [
|
|
8190
9573
|
...e,
|
|
8191
|
-
{
|
|
8192
|
-
kind: "info",
|
|
8193
|
-
key: mkKey(),
|
|
8194
|
-
text: "commands:\n /mode edit|plan|auto switch mode (or shift+tab to cycle)\n /plan /auto /edit shortcuts for /mode\n /thinking low|med|high set reasoning effort (quality vs speed)\n /theme interactive theme picker (or ctrl+t)\n /theme NAME set theme by name\n /resume pick a past conversation\n /compact summarize old turns to free context\n /init scan this repo and write a KIMI.md for future agents\n /memory show memory stats\n /memory search <query> search stored memories\n /memory clear wipe memories for this repo\n /mcp list list connected MCP servers and tools\n /mcp reload reconnect all configured MCP servers\n /reasoning toggle show/hide model reasoning\n /clear clear current conversation\n /hello send a voice note to the creator\n /community join our Discord server\n /gateway show gateway status\n /gateway ID enable AI Gateway\n /gateway off disable AI Gateway (direct Workers AI)\n /gateway cache-ttl N set gateway cache TTL in seconds\n /gateway skip-cache T|F set gateway skip-cache flag\n /gateway collect-logs T|F include payload in gateway logs\n /gateway metadata K=V add metadata key-value pair\n /gateway metadata clear remove all metadata\n /cost /model /update /logout /help /exit\nkeys: ctrl-c interrupt/exit \xB7 ctrl-r toggle reasoning \xB7 ctrl-o verbose \xB7 ctrl+t theme \xB7 shift+tab cycle mode \xB7 \u2191/\u2193 history"
|
|
8195
|
-
}
|
|
9574
|
+
{ kind: "info", key: mkKey(), text: "usage: /command create | edit | delete | list" }
|
|
8196
9575
|
]);
|
|
8197
9576
|
return true;
|
|
8198
9577
|
}
|
|
9578
|
+
if (c === "/help") {
|
|
9579
|
+
setShowHelpMenu(true);
|
|
9580
|
+
return true;
|
|
9581
|
+
}
|
|
8199
9582
|
return false;
|
|
8200
9583
|
},
|
|
8201
9584
|
[cfg, exit, usage, effort, theme, mode, openResumePicker, runCompact, runInit, initMcp, setCfg]
|
|
8202
9585
|
);
|
|
9586
|
+
const handleHelpCommand = useCallback(
|
|
9587
|
+
(command) => {
|
|
9588
|
+
setShowHelpMenu(false);
|
|
9589
|
+
const executed = handleSlash(command);
|
|
9590
|
+
if (!executed) {
|
|
9591
|
+
setEvents((e) => [...e, { kind: "error", key: mkKey(), text: `unknown command: ${command}` }]);
|
|
9592
|
+
}
|
|
9593
|
+
},
|
|
9594
|
+
[handleSlash]
|
|
9595
|
+
);
|
|
9596
|
+
const handleCommandSave = useCallback(
|
|
9597
|
+
async (opts2) => {
|
|
9598
|
+
setCommandWizard(null);
|
|
9599
|
+
try {
|
|
9600
|
+
if (commandWizard?.mode === "edit" && commandWizard.initial && commandWizard.initial.name !== opts2.name) {
|
|
9601
|
+
await deleteCustomCommand(commandWizard.initial);
|
|
9602
|
+
}
|
|
9603
|
+
const result = await saveCustomCommand(opts2);
|
|
9604
|
+
await reloadCustomCommands();
|
|
9605
|
+
setEvents((e) => [
|
|
9606
|
+
...e,
|
|
9607
|
+
{ kind: "info", key: mkKey(), text: `saved /${opts2.name} \u2192 ${result.filepath}` }
|
|
9608
|
+
]);
|
|
9609
|
+
} catch (err) {
|
|
9610
|
+
setEvents((e) => [
|
|
9611
|
+
...e,
|
|
9612
|
+
{ kind: "error", key: mkKey(), text: `failed to save /${opts2.name}: ${err.message}` }
|
|
9613
|
+
]);
|
|
9614
|
+
}
|
|
9615
|
+
},
|
|
9616
|
+
[commandWizard, reloadCustomCommands, setEvents]
|
|
9617
|
+
);
|
|
9618
|
+
const handleCommandDelete = useCallback(
|
|
9619
|
+
async (cmd) => {
|
|
9620
|
+
setCommandToDelete(null);
|
|
9621
|
+
try {
|
|
9622
|
+
await deleteCustomCommand(cmd);
|
|
9623
|
+
await reloadCustomCommands();
|
|
9624
|
+
setEvents((e) => [
|
|
9625
|
+
...e,
|
|
9626
|
+
{ kind: "info", key: mkKey(), text: `deleted /${cmd.name} (${cmd.filepath})` }
|
|
9627
|
+
]);
|
|
9628
|
+
} catch (err) {
|
|
9629
|
+
setEvents((e) => [
|
|
9630
|
+
...e,
|
|
9631
|
+
{ kind: "error", key: mkKey(), text: `failed to delete /${cmd.name}: ${err.message}` }
|
|
9632
|
+
]);
|
|
9633
|
+
}
|
|
9634
|
+
},
|
|
9635
|
+
[reloadCustomCommands, setEvents]
|
|
9636
|
+
);
|
|
8203
9637
|
const processMessage = useCallback(
|
|
8204
9638
|
async (text, displayText) => {
|
|
8205
9639
|
if (!cfg) return;
|
|
8206
|
-
|
|
9640
|
+
let trimmed = text.trim();
|
|
8207
9641
|
if (!trimmed) return;
|
|
8208
|
-
|
|
8209
|
-
|
|
9642
|
+
let overrideModel;
|
|
9643
|
+
let overrideEffort;
|
|
9644
|
+
let display = displayText?.trim() || trimmed;
|
|
9645
|
+
if (trimmed.startsWith("/")) {
|
|
9646
|
+
if (handleSlash(trimmed)) return;
|
|
9647
|
+
const head = trimmed.split(/\s+/)[0].slice(1);
|
|
9648
|
+
const custom = customCommandsRef.current.find((c) => c.name === head);
|
|
9649
|
+
if (custom) {
|
|
9650
|
+
const info = (text2) => setEvents((e) => [...e, { kind: "info", key: mkKey(), text: text2 }]);
|
|
9651
|
+
const { prompt: rendered, warnings } = await renderCommand(custom, trimmed, {
|
|
9652
|
+
cwd: process.cwd()
|
|
9653
|
+
});
|
|
9654
|
+
for (const w of warnings) info(`${custom.name}: ${w}`);
|
|
9655
|
+
if (!rendered.trim()) return;
|
|
9656
|
+
const parts = [];
|
|
9657
|
+
if (custom.model) {
|
|
9658
|
+
overrideModel = custom.model;
|
|
9659
|
+
parts.push(`model=${custom.model}`);
|
|
9660
|
+
}
|
|
9661
|
+
if (custom.effort) {
|
|
9662
|
+
overrideEffort = custom.effort;
|
|
9663
|
+
parts.push(`effort=${custom.effort}`);
|
|
9664
|
+
}
|
|
9665
|
+
if (parts.length > 0) info(`command '${custom.name}' \u2192 ${parts.join(", ")} (this turn)`);
|
|
9666
|
+
if (custom.mode) info(`note: mode override (${custom.mode}) is not yet wired; current mode applies`);
|
|
9667
|
+
display = trimmed;
|
|
9668
|
+
trimmed = rendered;
|
|
9669
|
+
}
|
|
9670
|
+
}
|
|
8210
9671
|
const imagePaths = findImagePaths(trimmed).slice(0, MAX_IMAGES_PER_MESSAGE);
|
|
8211
9672
|
let images = [];
|
|
8212
9673
|
let content = sanitizeString(trimmed);
|
|
@@ -8235,6 +9696,10 @@ ${lines.join("\n")}` }]);
|
|
|
8235
9696
|
content = parts;
|
|
8236
9697
|
}
|
|
8237
9698
|
}
|
|
9699
|
+
if (sessionStartRecallRef.current) {
|
|
9700
|
+
await sessionStartRecallRef.current;
|
|
9701
|
+
sessionStartRecallRef.current = null;
|
|
9702
|
+
}
|
|
8238
9703
|
setEvents((e) => [...e, { kind: "user", key: mkKey(), text: display, images: images.length > 0 ? images : void 0 }]);
|
|
8239
9704
|
messagesRef.current.push({ role: "user", content });
|
|
8240
9705
|
if (compiledContextRef.current) {
|
|
@@ -8258,14 +9723,14 @@ ${lines.join("\n")}` }]);
|
|
|
8258
9723
|
await runAgentTurn({
|
|
8259
9724
|
accountId: cfg.accountId,
|
|
8260
9725
|
apiToken: cfg.apiToken,
|
|
8261
|
-
model: cfg.model,
|
|
9726
|
+
model: overrideModel ?? cfg.model,
|
|
8262
9727
|
gateway: gatewayFromConfig(cfg),
|
|
8263
9728
|
messages: messagesRef.current,
|
|
8264
9729
|
tools: [...ALL_TOOLS, ...mcpToolsRef.current],
|
|
8265
9730
|
executor: executorRef.current,
|
|
8266
9731
|
cwd: process.cwd(),
|
|
8267
9732
|
signal: controller.signal,
|
|
8268
|
-
reasoningEffort: effortRef.current,
|
|
9733
|
+
reasoningEffort: overrideEffort ?? effortRef.current,
|
|
8269
9734
|
coauthor: cfg.coauthor !== false ? { name: cfg.coauthorName || "kimiflare", email: cfg.coauthorEmail || "kimiflare@proton.me" } : void 0,
|
|
8270
9735
|
sessionId: ensureSessionId(),
|
|
8271
9736
|
memoryManager: memoryManagerRef.current,
|
|
@@ -8370,24 +9835,84 @@ ${lines.join("\n")}` }]);
|
|
|
8370
9835
|
}
|
|
8371
9836
|
});
|
|
8372
9837
|
await saveSessionSafe();
|
|
8373
|
-
if (
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
9838
|
+
if (shouldCompact({ messages: messagesRef.current })) {
|
|
9839
|
+
if (compiledContextRef.current) {
|
|
9840
|
+
const result = compactMessages2({
|
|
9841
|
+
messages: messagesRef.current,
|
|
9842
|
+
state: sessionStateRef.current,
|
|
9843
|
+
store: artifactStoreRef.current
|
|
9844
|
+
});
|
|
9845
|
+
if (result.metrics.rawTurnsRemoved > 0) {
|
|
9846
|
+
messagesRef.current = result.newMessages;
|
|
9847
|
+
sessionStateRef.current = result.newState;
|
|
9848
|
+
setEvents((e) => [
|
|
9849
|
+
...e,
|
|
9850
|
+
{
|
|
9851
|
+
kind: "info",
|
|
9852
|
+
key: mkKey(),
|
|
9853
|
+
text: `auto-compacted: ${result.metrics.estimatedTokensBefore} \u2192 ${result.metrics.estimatedTokensAfter} tokens (${result.metrics.archivedArtifacts} artifacts)`
|
|
9854
|
+
}
|
|
9855
|
+
]);
|
|
9856
|
+
await saveSessionSafe();
|
|
9857
|
+
}
|
|
9858
|
+
} else {
|
|
9859
|
+
try {
|
|
9860
|
+
const result = await compactMessages({
|
|
9861
|
+
accountId: cfg.accountId,
|
|
9862
|
+
apiToken: cfg.apiToken,
|
|
9863
|
+
model: cfg.model,
|
|
9864
|
+
messages: messagesRef.current,
|
|
9865
|
+
signal: controller.signal,
|
|
9866
|
+
gateway: gatewayFromConfig(cfg)
|
|
9867
|
+
});
|
|
9868
|
+
if (result.replacedCount > 0) {
|
|
9869
|
+
messagesRef.current = result.newMessages;
|
|
9870
|
+
setEvents((e) => [
|
|
9871
|
+
...e,
|
|
9872
|
+
{
|
|
9873
|
+
kind: "info",
|
|
9874
|
+
key: mkKey(),
|
|
9875
|
+
text: `auto-compacted: ${result.replacedCount} messages summarized`
|
|
9876
|
+
}
|
|
9877
|
+
]);
|
|
9878
|
+
await saveSessionSafe();
|
|
8388
9879
|
}
|
|
8389
|
-
|
|
8390
|
-
|
|
9880
|
+
} catch (compactErr) {
|
|
9881
|
+
if (compactErr.name !== "AbortError") {
|
|
9882
|
+
setEvents((es) => [
|
|
9883
|
+
...es,
|
|
9884
|
+
{
|
|
9885
|
+
kind: "info",
|
|
9886
|
+
key: mkKey(),
|
|
9887
|
+
text: `auto-compact failed: ${compactErr.message ?? String(compactErr)}`
|
|
9888
|
+
}
|
|
9889
|
+
]);
|
|
9890
|
+
}
|
|
9891
|
+
}
|
|
9892
|
+
}
|
|
9893
|
+
}
|
|
9894
|
+
const manager = memoryManagerRef.current;
|
|
9895
|
+
if (manager) {
|
|
9896
|
+
try {
|
|
9897
|
+
const cwd = process.cwd();
|
|
9898
|
+
const queryText = sessionStateRef.current.task || cwd;
|
|
9899
|
+
const results = await manager.recall({ text: queryText, repoPath: cwd, limit: 5 });
|
|
9900
|
+
if (results.length > 0) {
|
|
9901
|
+
const text2 = await manager.synthesizeRecalled(results);
|
|
9902
|
+
const lastSystemIdx = messagesRef.current.findLastIndex((m) => m.role === "system");
|
|
9903
|
+
const insertIdx = lastSystemIdx >= 0 ? lastSystemIdx + 1 : messagesRef.current.length;
|
|
9904
|
+
messagesRef.current.splice(insertIdx, 0, { role: "system", content: text2 });
|
|
9905
|
+
setEvents((e) => [
|
|
9906
|
+
...e,
|
|
9907
|
+
{
|
|
9908
|
+
kind: "memory",
|
|
9909
|
+
key: mkKey(),
|
|
9910
|
+
text: `recalled ${results.length} memory${results.length === 1 ? "" : "ies"} after compaction`
|
|
9911
|
+
}
|
|
9912
|
+
]);
|
|
9913
|
+
await saveSessionSafe();
|
|
9914
|
+
}
|
|
9915
|
+
} catch {
|
|
8391
9916
|
}
|
|
8392
9917
|
}
|
|
8393
9918
|
} catch (e) {
|
|
@@ -8463,7 +9988,7 @@ ${lines.join("\n")}` }]);
|
|
|
8463
9988
|
}
|
|
8464
9989
|
}, [usage]);
|
|
8465
9990
|
if (!cfg) {
|
|
8466
|
-
return /* @__PURE__ */
|
|
9991
|
+
return /* @__PURE__ */ jsx17(
|
|
8467
9992
|
Onboarding,
|
|
8468
9993
|
{
|
|
8469
9994
|
onDone: (newCfg) => {
|
|
@@ -8477,15 +10002,97 @@ ${lines.join("\n")}` }]);
|
|
|
8477
10002
|
);
|
|
8478
10003
|
}
|
|
8479
10004
|
if (resumeSessions !== null) {
|
|
8480
|
-
return /* @__PURE__ */
|
|
10005
|
+
return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(ResumePicker, { sessions: resumeSessions, onPick: handleResumePick, theme }) });
|
|
8481
10006
|
}
|
|
8482
10007
|
if (showThemePicker) {
|
|
8483
|
-
return /* @__PURE__ */
|
|
10008
|
+
return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(ThemePicker, { themes: themeList(), current: theme, onPick: handleThemePick, onPreview: (t) => setTheme(t) }) });
|
|
10009
|
+
}
|
|
10010
|
+
if (showHelpMenu) {
|
|
10011
|
+
return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(
|
|
10012
|
+
HelpMenu,
|
|
10013
|
+
{
|
|
10014
|
+
theme,
|
|
10015
|
+
themes: themeList().map((t) => ({ name: t.name, label: t.label })),
|
|
10016
|
+
currentThemeName: theme.name,
|
|
10017
|
+
customCommands: customCommandsRef.current.filter((c) => !BUILTIN_COMMAND_NAMES.has(c.name.toLowerCase())).map((c) => ({ name: c.name, description: c.description })),
|
|
10018
|
+
onDone: () => setShowHelpMenu(false),
|
|
10019
|
+
onCommand: handleHelpCommand
|
|
10020
|
+
}
|
|
10021
|
+
) });
|
|
10022
|
+
}
|
|
10023
|
+
if (commandWizard) {
|
|
10024
|
+
return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(
|
|
10025
|
+
CommandWizard,
|
|
10026
|
+
{
|
|
10027
|
+
theme,
|
|
10028
|
+
mode: commandWizard.mode,
|
|
10029
|
+
initial: commandWizard.initial,
|
|
10030
|
+
existingNames: customCommandsRef.current.map((c) => c.name),
|
|
10031
|
+
builtinNames: BUILTIN_COMMAND_NAMES,
|
|
10032
|
+
onDone: () => setCommandWizard(null),
|
|
10033
|
+
onSave: handleCommandSave
|
|
10034
|
+
}
|
|
10035
|
+
) });
|
|
10036
|
+
}
|
|
10037
|
+
if (commandPicker) {
|
|
10038
|
+
return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(
|
|
10039
|
+
CommandPicker,
|
|
10040
|
+
{
|
|
10041
|
+
theme,
|
|
10042
|
+
commands: customCommandsRef.current,
|
|
10043
|
+
title: commandPicker.mode === "edit" ? "Edit custom command" : "Delete custom command",
|
|
10044
|
+
onPick: (cmd) => {
|
|
10045
|
+
setCommandPicker(null);
|
|
10046
|
+
if (!cmd) return;
|
|
10047
|
+
if (commandPicker.mode === "edit") {
|
|
10048
|
+
setCommandWizard({ mode: "edit", initial: cmd });
|
|
10049
|
+
} else {
|
|
10050
|
+
setCommandToDelete(cmd);
|
|
10051
|
+
}
|
|
10052
|
+
}
|
|
10053
|
+
}
|
|
10054
|
+
) });
|
|
10055
|
+
}
|
|
10056
|
+
if (commandToDelete) {
|
|
10057
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
10058
|
+
/* @__PURE__ */ jsxs16(Text17, { color: theme.accent, bold: true, children: [
|
|
10059
|
+
"Delete /",
|
|
10060
|
+
commandToDelete.name,
|
|
10061
|
+
"?"
|
|
10062
|
+
] }),
|
|
10063
|
+
/* @__PURE__ */ jsx17(Text17, { color: theme.info.color, dimColor: true, children: commandToDelete.filepath }),
|
|
10064
|
+
/* @__PURE__ */ jsx17(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx17(
|
|
10065
|
+
SelectInput7,
|
|
10066
|
+
{
|
|
10067
|
+
items: [
|
|
10068
|
+
{ label: "Yes, delete", value: "yes", key: "yes" },
|
|
10069
|
+
{ label: "Cancel", value: "cancel", key: "cancel" }
|
|
10070
|
+
],
|
|
10071
|
+
onSelect: (item) => {
|
|
10072
|
+
if (item.value === "yes") {
|
|
10073
|
+
void handleCommandDelete(commandToDelete);
|
|
10074
|
+
} else {
|
|
10075
|
+
setCommandToDelete(null);
|
|
10076
|
+
}
|
|
10077
|
+
}
|
|
10078
|
+
}
|
|
10079
|
+
) })
|
|
10080
|
+
] });
|
|
10081
|
+
}
|
|
10082
|
+
if (showCommandList) {
|
|
10083
|
+
return /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", children: /* @__PURE__ */ jsx17(
|
|
10084
|
+
CommandList,
|
|
10085
|
+
{
|
|
10086
|
+
theme,
|
|
10087
|
+
commands: customCommandsRef.current,
|
|
10088
|
+
onDone: () => setShowCommandList(false)
|
|
10089
|
+
}
|
|
10090
|
+
) });
|
|
8484
10091
|
}
|
|
8485
10092
|
const hasConversation = events.some((e) => e.kind === "user" || e.kind === "assistant");
|
|
8486
|
-
return /* @__PURE__ */
|
|
8487
|
-
!hasConversation && events.length === 0 ? /* @__PURE__ */
|
|
8488
|
-
perm ? /* @__PURE__ */
|
|
10093
|
+
return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", children: [
|
|
10094
|
+
!hasConversation && events.length === 0 ? /* @__PURE__ */ jsx17(Welcome, { theme, accountId: cfg.accountId }) : /* @__PURE__ */ jsx17(ChatView, { events, showReasoning, theme, verbose }),
|
|
10095
|
+
perm ? /* @__PURE__ */ jsx17(
|
|
8489
10096
|
PermissionModal,
|
|
8490
10097
|
{
|
|
8491
10098
|
tool: perm.tool,
|
|
@@ -8496,8 +10103,8 @@ ${lines.join("\n")}` }]);
|
|
|
8496
10103
|
setPerm(null);
|
|
8497
10104
|
}
|
|
8498
10105
|
}
|
|
8499
|
-
) : /* @__PURE__ */
|
|
8500
|
-
tasks.length > 0 && /* @__PURE__ */
|
|
10106
|
+
) : /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginTop: 1, children: [
|
|
10107
|
+
tasks.length > 0 && /* @__PURE__ */ jsx17(
|
|
8501
10108
|
TaskList,
|
|
8502
10109
|
{
|
|
8503
10110
|
tasks,
|
|
@@ -8506,11 +10113,11 @@ ${lines.join("\n")}` }]);
|
|
|
8506
10113
|
tokensDelta: Math.max(0, (usage?.prompt_tokens ?? 0) - tasksStartTokens)
|
|
8507
10114
|
}
|
|
8508
10115
|
),
|
|
8509
|
-
queue.length > 0 && /* @__PURE__ */
|
|
10116
|
+
queue.length > 0 && /* @__PURE__ */ jsx17(Box16, { flexDirection: "column", marginBottom: 1, children: queue.map((q, i) => /* @__PURE__ */ jsxs16(Text17, { color: theme.queue.color, dimColor: theme.queue.dim, children: [
|
|
8510
10117
|
"\u23F3 ",
|
|
8511
10118
|
q.display
|
|
8512
10119
|
] }, `queue_${i}`)) }),
|
|
8513
|
-
/* @__PURE__ */
|
|
10120
|
+
/* @__PURE__ */ jsx17(
|
|
8514
10121
|
StatusBar,
|
|
8515
10122
|
{
|
|
8516
10123
|
model: cfg.model,
|
|
@@ -8528,9 +10135,9 @@ ${lines.join("\n")}` }]);
|
|
|
8528
10135
|
codeMode
|
|
8529
10136
|
}
|
|
8530
10137
|
),
|
|
8531
|
-
/* @__PURE__ */
|
|
8532
|
-
/* @__PURE__ */
|
|
8533
|
-
/* @__PURE__ */
|
|
10138
|
+
/* @__PURE__ */ jsxs16(Box16, { marginTop: 1, children: [
|
|
10139
|
+
/* @__PURE__ */ jsx17(Text17, { color: theme.accent, children: "\u203A " }),
|
|
10140
|
+
/* @__PURE__ */ jsx17(
|
|
8534
10141
|
CustomTextInput,
|
|
8535
10142
|
{
|
|
8536
10143
|
value: input,
|
|
@@ -8579,12 +10186,12 @@ ${lines.join("\n")}` }]);
|
|
|
8579
10186
|
] });
|
|
8580
10187
|
}
|
|
8581
10188
|
async function renderApp(cfg, updateResult) {
|
|
8582
|
-
const instance = render(/* @__PURE__ */
|
|
10189
|
+
const instance = render(/* @__PURE__ */ jsx17(App, { initialCfg: cfg, initialUpdateResult: updateResult }), {
|
|
8583
10190
|
incrementalRendering: true
|
|
8584
10191
|
});
|
|
8585
10192
|
await instance.waitUntilExit();
|
|
8586
10193
|
}
|
|
8587
|
-
var FEEDBACK_WORKER_URL, CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, EFFORT_DESCRIPTIONS;
|
|
10194
|
+
var FEEDBACK_WORKER_URL, CONTEXT_LIMIT, AUTO_COMPACT_SUGGEST_PCT, MAX_EVENTS, nextAssistantId, nextKey, mkKey, MAX_IMAGES_PER_MESSAGE, BUILTIN_COMMAND_NAMES, EFFORT_DESCRIPTIONS;
|
|
8588
10195
|
var init_app = __esm({
|
|
8589
10196
|
"src/app.tsx"() {
|
|
8590
10197
|
"use strict";
|
|
@@ -8607,6 +10214,7 @@ var init_app = __esm({
|
|
|
8607
10214
|
init_update_check();
|
|
8608
10215
|
init_onboarding();
|
|
8609
10216
|
init_welcome();
|
|
10217
|
+
init_help_menu();
|
|
8610
10218
|
init_config();
|
|
8611
10219
|
init_theme();
|
|
8612
10220
|
init_mode();
|
|
@@ -8617,6 +10225,12 @@ var init_app = __esm({
|
|
|
8617
10225
|
init_storage_limits();
|
|
8618
10226
|
init_state();
|
|
8619
10227
|
init_version();
|
|
10228
|
+
init_loader();
|
|
10229
|
+
init_renderer();
|
|
10230
|
+
init_save();
|
|
10231
|
+
init_command_wizard();
|
|
10232
|
+
init_command_picker();
|
|
10233
|
+
init_command_list();
|
|
8620
10234
|
FEEDBACK_WORKER_URL = "https://kimiflare-feedback.sina-b35.workers.dev";
|
|
8621
10235
|
CONTEXT_LIMIT = 262e3;
|
|
8622
10236
|
AUTO_COMPACT_SUGGEST_PCT = 0.8;
|
|
@@ -8625,6 +10239,32 @@ var init_app = __esm({
|
|
|
8625
10239
|
nextKey = 1;
|
|
8626
10240
|
mkKey = () => `evt_${nextKey++}`;
|
|
8627
10241
|
MAX_IMAGES_PER_MESSAGE = 10;
|
|
10242
|
+
BUILTIN_COMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
10243
|
+
"exit",
|
|
10244
|
+
"quit",
|
|
10245
|
+
"clear",
|
|
10246
|
+
"reasoning",
|
|
10247
|
+
"cost",
|
|
10248
|
+
"model",
|
|
10249
|
+
"thinking",
|
|
10250
|
+
"effort",
|
|
10251
|
+
"theme",
|
|
10252
|
+
"mode",
|
|
10253
|
+
"plan",
|
|
10254
|
+
"auto",
|
|
10255
|
+
"edit",
|
|
10256
|
+
"resume",
|
|
10257
|
+
"compact",
|
|
10258
|
+
"init",
|
|
10259
|
+
"update",
|
|
10260
|
+
"mcp",
|
|
10261
|
+
"logout",
|
|
10262
|
+
"help",
|
|
10263
|
+
"memory",
|
|
10264
|
+
"gateway",
|
|
10265
|
+
"hello",
|
|
10266
|
+
"community"
|
|
10267
|
+
]);
|
|
8628
10268
|
EFFORT_DESCRIPTIONS = {
|
|
8629
10269
|
low: "low \u2014 fastest; lightest reasoning. Best for simple Q&A, small edits, quick coordination.",
|
|
8630
10270
|
medium: "medium \u2014 balanced (default). Solid quality on most edits, fast on trivial prompts.",
|