jinzd-ai-cli 0.4.241 → 0.4.243
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{batch-K66BLTLJ.js → batch-CUVZUPRK.js} +2 -2
- package/dist/{chat-index-7HXBWQFH.js → chat-index-ALUHFJKI.js} +3 -2
- package/dist/{chat-index-WYXYD7YP.js → chat-index-ML5FF5L6.js} +2 -1
- package/dist/{chunk-SMQHEJSH.js → chunk-66CE3E6X.js} +1 -1
- package/dist/chunk-B5TYJO7V.js +123 -0
- package/dist/{chunk-PGHTAKIA.js → chunk-DJGCG7SF.js} +1 -1
- package/dist/{chunk-2QQKS56X.js → chunk-DT77O33T.js} +1 -1
- package/dist/{chunk-2LLQOMMW.js → chunk-EMFCHE2B.js} +1 -1
- package/dist/chunk-GEWPLNKA.js +164 -0
- package/dist/{chunk-LXSUPLCK.js → chunk-H3ARWBYR.js} +2 -2
- package/dist/{chunk-3AIR7N5M.js → chunk-HNJX3N2V.js} +5 -5
- package/dist/{chunk-5MYPIQ3Y.js → chunk-IGPFYNZA.js} +4 -2
- package/dist/{chunk-GORPFFV4.js → chunk-KILKF2VN.js} +97 -254
- package/dist/{chunk-JMP3LJC2.js → chunk-KWQKQVPH.js} +4 -2
- package/dist/{chunk-NAQPP3GZ.js → chunk-L5IRDRWG.js} +1 -1
- package/dist/{chunk-UO3XWZFE.js → chunk-NV63EYFL.js} +3 -119
- package/dist/{chunk-KCEO2XJ4.js → chunk-PJ3ONBWZ.js} +0 -119
- package/dist/{chunk-ZPXMT2B6.js → chunk-PLEXBFCR.js} +4 -163
- package/dist/chunk-PNNZRJ67.js +523 -0
- package/dist/{chunk-CHFFEVVT.js → chunk-QNK7GUUB.js} +3 -3
- package/dist/chunk-TY4FSEZS.js +432 -0
- package/dist/{chunk-IV6GCDVR.js → chunk-X73Y4JZI.js} +7 -1
- package/dist/chunk-YVTASHS5.js +121 -0
- package/dist/{ci-T2ABDMMQ.js → ci-3O3JR6FX.js} +4 -4
- package/dist/{ci-format-S2PPU27O.js → ci-format-KGWF6KOU.js} +2 -2
- package/dist/{constants-RS7OUU7X.js → constants-NMFRZ5LW.js} +1 -1
- package/dist/{doctor-cli-TGAW6RAP.js → doctor-cli-4QUHTJZ4.js} +4 -4
- package/dist/electron-server.js +446 -579
- package/dist/{hub-GL5ZU6WS.js → hub-2GXFPWOR.js} +2 -2
- package/dist/index.js +273 -62
- package/dist/{persist-AMH5SQ4W.js → persist-WGIJ6RXY.js} +4 -3
- package/dist/persistent-memory-VFBRMKGG.js +65 -0
- package/dist/persistent-memory-ZCQKLIC5.js +63 -0
- package/dist/{pr-TJFKUZ4B.js → pr-GIDFO4KP.js} +5 -5
- package/dist/{run-tests-KEI7IXTB.js → run-tests-BMH6QJN2.js} +2 -2
- package/dist/{run-tests-EWMU34A4.js → run-tests-WCZBWQFS.js} +2 -1
- package/dist/{server-UVS2D4M6.js → server-A5NGGDMP.js} +130 -47
- package/dist/{server-E545UIX7.js → server-FEFIFJDA.js} +8 -6
- package/dist/{task-orchestrator-PWXV2EBN.js → task-orchestrator-J2H6OV6R.js} +8 -6
- package/dist/{usage-EDJ6QOWB.js → usage-7WJC234G.js} +2 -2
- package/package.json +1 -1
package/dist/electron-server.js
CHANGED
|
@@ -1,9 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listFromIndex,
|
|
3
|
+
loadChatIndex,
|
|
4
|
+
readSessionMeta,
|
|
5
|
+
removeSessionFromIndex,
|
|
6
|
+
safeDate,
|
|
7
|
+
searchCandidates,
|
|
8
|
+
searchChatMemory,
|
|
9
|
+
updateSessionInIndex
|
|
10
|
+
} from "./chunk-NV63EYFL.js";
|
|
1
11
|
import {
|
|
2
12
|
fileCheckpoints
|
|
3
13
|
} from "./chunk-SKET65WZ.js";
|
|
4
14
|
import {
|
|
5
15
|
indexProject
|
|
6
16
|
} from "./chunk-BNERFXVO.js";
|
|
17
|
+
import {
|
|
18
|
+
addMemoryEntry,
|
|
19
|
+
clearMemoryEntries,
|
|
20
|
+
deleteMemoryEntry,
|
|
21
|
+
expireMemoryEntry,
|
|
22
|
+
exportMemoryEntries,
|
|
23
|
+
findSimilarMemory,
|
|
24
|
+
formatGitContextForPrompt,
|
|
25
|
+
formatMemoryForPrompt,
|
|
26
|
+
getGitContext,
|
|
27
|
+
getGitRoot,
|
|
28
|
+
isMemoryExpired,
|
|
29
|
+
listMemoryEntries,
|
|
30
|
+
loadMemoryEntries,
|
|
31
|
+
memoryStorePath,
|
|
32
|
+
parseWritableScope,
|
|
33
|
+
searchPersistentMemories,
|
|
34
|
+
touchMemoryReferences,
|
|
35
|
+
updateMemoryApproval,
|
|
36
|
+
updateMemoryEntry
|
|
37
|
+
} from "./chunk-PNNZRJ67.js";
|
|
38
|
+
import {
|
|
39
|
+
redactJson,
|
|
40
|
+
scanString
|
|
41
|
+
} from "./chunk-YVTASHS5.js";
|
|
42
|
+
import {
|
|
43
|
+
runTestsTool
|
|
44
|
+
} from "./chunk-PLEXBFCR.js";
|
|
7
45
|
import {
|
|
8
46
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
9
47
|
APP_NAME,
|
|
@@ -26,8 +64,6 @@ import {
|
|
|
26
64
|
MCP_PROTOCOL_VERSION,
|
|
27
65
|
MCP_TOOL_PREFIX,
|
|
28
66
|
MEMORY_FILE_NAME,
|
|
29
|
-
MEMORY_MAX_CHARS,
|
|
30
|
-
MEMORY_STORE_FILE_NAME,
|
|
31
67
|
PLAN_MODE_READONLY_TOOLS,
|
|
32
68
|
PLAN_MODE_SYSTEM_ADDON,
|
|
33
69
|
PLUGINS_DIR_NAME,
|
|
@@ -36,9 +72,8 @@ import {
|
|
|
36
72
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
37
73
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
38
74
|
VERSION,
|
|
39
|
-
buildUserIdentityPrompt
|
|
40
|
-
|
|
41
|
-
} from "./chunk-ZPXMT2B6.js";
|
|
75
|
+
buildUserIdentityPrompt
|
|
76
|
+
} from "./chunk-GEWPLNKA.js";
|
|
42
77
|
import {
|
|
43
78
|
hasSemanticIndex,
|
|
44
79
|
semanticSearch
|
|
@@ -47,20 +82,6 @@ import {
|
|
|
47
82
|
loadIndex
|
|
48
83
|
} from "./chunk-RWM2GFRC.js";
|
|
49
84
|
import "./chunk-IEQAE3QG.js";
|
|
50
|
-
import {
|
|
51
|
-
DEFAULT_PATTERNS,
|
|
52
|
-
listFromIndex,
|
|
53
|
-
loadChatIndex,
|
|
54
|
-
readSessionMeta,
|
|
55
|
-
redactJson,
|
|
56
|
-
redactString,
|
|
57
|
-
removeSessionFromIndex,
|
|
58
|
-
safeDate,
|
|
59
|
-
scanString,
|
|
60
|
-
searchCandidates,
|
|
61
|
-
searchChatMemory,
|
|
62
|
-
updateSessionInIndex
|
|
63
|
-
} from "./chunk-UO3XWZFE.js";
|
|
64
85
|
import "./chunk-JV5N65KN.js";
|
|
65
86
|
import {
|
|
66
87
|
atomicWriteFileSync
|
|
@@ -70,8 +91,8 @@ import {
|
|
|
70
91
|
import express from "express";
|
|
71
92
|
import { createServer } from "http";
|
|
72
93
|
import { WebSocketServer } from "ws";
|
|
73
|
-
import { join as
|
|
74
|
-
import { existsSync as
|
|
94
|
+
import { join as join27, dirname as dirname10, resolve as resolve12, relative as relative6, sep as sep4 } from "path";
|
|
95
|
+
import { existsSync as existsSync31, readFileSync as readFileSync24, readdirSync as readdirSync16, statSync as statSync14, realpathSync as realpathSync3 } from "fs";
|
|
75
96
|
import { networkInterfaces } from "os";
|
|
76
97
|
|
|
77
98
|
// src/config/config-manager.ts
|
|
@@ -192,6 +213,12 @@ var ConfigSchema = z.object({
|
|
|
192
213
|
projectDocMaxBytes: z.number().int().positive().default(32 * 1024),
|
|
193
214
|
showLoadedContextSources: z.boolean().default(false)
|
|
194
215
|
}).default({}),
|
|
216
|
+
// 持久记忆注入配置(MEM-005)
|
|
217
|
+
// maxChars: 注入 system prompt 的字符预算,超出丢最老条目并显式标注截断。
|
|
218
|
+
// 注意 CJK ≈ 1 字符/token——中文记忆的 token 开销约等于字符数。
|
|
219
|
+
memory: z.object({
|
|
220
|
+
maxChars: z.number().int().positive().max(2e5).default(1e4)
|
|
221
|
+
}).default({}),
|
|
195
222
|
// Google Custom Search API 的 Search Engine ID (cx 参数)
|
|
196
223
|
// API Key 通过 apiKeys['google-search'] 或 AICLI_API_KEY_GOOGLESEARCH 环境变量配置
|
|
197
224
|
// CX 也可通过 AICLI_GOOGLE_CX 环境变量覆盖
|
|
@@ -4927,7 +4954,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
|
|
|
4927
4954
|
const markerStart = `AICLI_MARKER_START:${id}`;
|
|
4928
4955
|
const markerEnd = `AICLI_MARKER_END:${id}`;
|
|
4929
4956
|
const wrappedCmd = IS_WINDOWS2 ? `${WIN_UTF8_PREAMBLE} echo "${markerStart}"; ${command}; echo "${markerEnd}:0"` : `echo "${markerStart}"; ${command}; echo "${markerEnd}:$?"`;
|
|
4930
|
-
return new Promise((
|
|
4957
|
+
return new Promise((resolve13) => {
|
|
4931
4958
|
let stdout = "";
|
|
4932
4959
|
let stderr = "";
|
|
4933
4960
|
let timedOut = false;
|
|
@@ -4943,7 +4970,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
|
|
|
4943
4970
|
stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
|
|
4944
4971
|
resolved = true;
|
|
4945
4972
|
cleanup();
|
|
4946
|
-
|
|
4973
|
+
resolve13({ stdout, stderr, exitCode, timedOut, aborted });
|
|
4947
4974
|
}
|
|
4948
4975
|
};
|
|
4949
4976
|
const onErr = (chunk) => {
|
|
@@ -4958,7 +4985,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
|
|
|
4958
4985
|
resolved = true;
|
|
4959
4986
|
stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
|
|
4960
4987
|
cleanup();
|
|
4961
|
-
|
|
4988
|
+
resolve13({ stdout, stderr, exitCode: null, timedOut, aborted });
|
|
4962
4989
|
try {
|
|
4963
4990
|
proc.kill();
|
|
4964
4991
|
} catch {
|
|
@@ -4971,7 +4998,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
|
|
|
4971
4998
|
resolved = true;
|
|
4972
4999
|
cleanup();
|
|
4973
5000
|
stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
|
|
4974
|
-
|
|
5001
|
+
resolve13({ stdout, stderr, exitCode: null, timedOut, aborted });
|
|
4975
5002
|
try {
|
|
4976
5003
|
proc.kill();
|
|
4977
5004
|
} catch {
|
|
@@ -4989,7 +5016,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
|
|
|
4989
5016
|
stderr += `
|
|
4990
5017
|
[pool error] ${err.message}`;
|
|
4991
5018
|
cleanup();
|
|
4992
|
-
|
|
5019
|
+
resolve13({ stdout, stderr, exitCode: null, timedOut, aborted });
|
|
4993
5020
|
pool.delete(sessionKey);
|
|
4994
5021
|
};
|
|
4995
5022
|
proc.on("error", onError);
|
|
@@ -7384,7 +7411,7 @@ var ToolExecutor = class {
|
|
|
7384
7411
|
rl.resume();
|
|
7385
7412
|
process.stdout.write(prompt);
|
|
7386
7413
|
this.confirming = true;
|
|
7387
|
-
return new Promise((
|
|
7414
|
+
return new Promise((resolve13) => {
|
|
7388
7415
|
let completed = false;
|
|
7389
7416
|
const cleanup = (result) => {
|
|
7390
7417
|
if (completed) return;
|
|
@@ -7394,7 +7421,7 @@ var ToolExecutor = class {
|
|
|
7394
7421
|
rl.pause();
|
|
7395
7422
|
rlAny.output = savedOutput;
|
|
7396
7423
|
this.confirming = false;
|
|
7397
|
-
|
|
7424
|
+
resolve13(result);
|
|
7398
7425
|
};
|
|
7399
7426
|
const onLine = (line) => {
|
|
7400
7427
|
const trimmed = line.trim();
|
|
@@ -7568,7 +7595,7 @@ var ToolExecutor = class {
|
|
|
7568
7595
|
rl.resume();
|
|
7569
7596
|
process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
|
|
7570
7597
|
this.confirming = true;
|
|
7571
|
-
return new Promise((
|
|
7598
|
+
return new Promise((resolve13) => {
|
|
7572
7599
|
let completed = false;
|
|
7573
7600
|
const cleanup = (answer) => {
|
|
7574
7601
|
if (completed) return;
|
|
@@ -7578,7 +7605,7 @@ var ToolExecutor = class {
|
|
|
7578
7605
|
rl.pause();
|
|
7579
7606
|
rlAny.output = savedOutput;
|
|
7580
7607
|
this.confirming = false;
|
|
7581
|
-
|
|
7608
|
+
resolve13(answer === "y");
|
|
7582
7609
|
};
|
|
7583
7610
|
const onLine = (line) => {
|
|
7584
7611
|
const trimmed = line.trim();
|
|
@@ -8827,7 +8854,7 @@ var runInteractiveTool = {
|
|
|
8827
8854
|
PYTHONDONTWRITEBYTECODE: "1"
|
|
8828
8855
|
};
|
|
8829
8856
|
const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
|
|
8830
|
-
return new Promise((
|
|
8857
|
+
return new Promise((resolve13) => {
|
|
8831
8858
|
const child = spawn3(executable, cmdArgs.map(String), {
|
|
8832
8859
|
cwd: process.cwd(),
|
|
8833
8860
|
env,
|
|
@@ -8860,22 +8887,22 @@ var runInteractiveTool = {
|
|
|
8860
8887
|
setTimeout(writeNextLine, 400);
|
|
8861
8888
|
const timer = setTimeout(() => {
|
|
8862
8889
|
child.kill();
|
|
8863
|
-
|
|
8890
|
+
resolve13(`${prefixWarnings}[Timeout after ${timeout}ms]
|
|
8864
8891
|
${buildOutput(stdout, stderr)}`);
|
|
8865
8892
|
}, timeout);
|
|
8866
8893
|
child.on("close", (code) => {
|
|
8867
8894
|
clearTimeout(timer);
|
|
8868
8895
|
const output = buildOutput(stdout, stderr);
|
|
8869
8896
|
if (code !== 0 && code !== null) {
|
|
8870
|
-
|
|
8897
|
+
resolve13(`${prefixWarnings}Exit code ${code}:
|
|
8871
8898
|
${output}`);
|
|
8872
8899
|
} else {
|
|
8873
|
-
|
|
8900
|
+
resolve13(`${prefixWarnings}${output || "(no output)"}`);
|
|
8874
8901
|
}
|
|
8875
8902
|
});
|
|
8876
8903
|
child.on("error", (err) => {
|
|
8877
8904
|
clearTimeout(timer);
|
|
8878
|
-
|
|
8905
|
+
resolve13(
|
|
8879
8906
|
`${prefixWarnings}Failed to start process "${executable}": ${err.message}
|
|
8880
8907
|
Hint: On Windows, use the full path to the executable, e.g.:
|
|
8881
8908
|
C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
|
|
@@ -9587,280 +9614,10 @@ Any of these triggers means use save_last_response, NOT write_file:
|
|
|
9587
9614
|
};
|
|
9588
9615
|
|
|
9589
9616
|
// src/tools/builtin/save-memory.ts
|
|
9590
|
-
import { join as join12 } from "path";
|
|
9591
|
-
import { homedir as homedir5 } from "os";
|
|
9592
|
-
|
|
9593
|
-
// src/memory/persistent-memory.ts
|
|
9594
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync12 } from "fs";
|
|
9595
|
-
import { randomUUID, createHash as createHash2 } from "crypto";
|
|
9596
|
-
import { dirname as dirname7, join as join11, resolve as resolve6 } from "path";
|
|
9597
|
-
|
|
9598
|
-
// src/tools/git-context.ts
|
|
9599
|
-
import { execSync as execSync2 } from "child_process";
|
|
9600
|
-
import { existsSync as existsSync16 } from "fs";
|
|
9601
9617
|
import { join as join10 } from "path";
|
|
9602
|
-
|
|
9603
|
-
try {
|
|
9604
|
-
return execSync2(`git ${cmd}`, {
|
|
9605
|
-
cwd,
|
|
9606
|
-
encoding: "utf-8",
|
|
9607
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
9608
|
-
timeout: 5e3
|
|
9609
|
-
}).trim();
|
|
9610
|
-
} catch {
|
|
9611
|
-
return null;
|
|
9612
|
-
}
|
|
9613
|
-
}
|
|
9614
|
-
function getGitRoot(cwd = process.cwd()) {
|
|
9615
|
-
return runGit("rev-parse --show-toplevel", cwd);
|
|
9616
|
-
}
|
|
9617
|
-
function getGitContext(cwd = process.cwd()) {
|
|
9618
|
-
if (!existsSync16(join10(cwd, ".git"))) {
|
|
9619
|
-
const result = runGit("rev-parse --git-dir", cwd);
|
|
9620
|
-
if (!result) return null;
|
|
9621
|
-
}
|
|
9622
|
-
const branch = runGit("rev-parse --abbrev-ref HEAD", cwd);
|
|
9623
|
-
if (!branch) return null;
|
|
9624
|
-
const statusOutput = runGit("status --porcelain", cwd) ?? "";
|
|
9625
|
-
const statusLines = statusOutput ? statusOutput.split("\n").filter(Boolean) : [];
|
|
9626
|
-
const stagedFiles = [];
|
|
9627
|
-
const changedFiles = [];
|
|
9628
|
-
for (const line of statusLines) {
|
|
9629
|
-
const xy = line.slice(0, 2);
|
|
9630
|
-
const file = line.slice(3).trim();
|
|
9631
|
-
const indexStatus = xy[0];
|
|
9632
|
-
const workStatus = xy[1];
|
|
9633
|
-
if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
|
|
9634
|
-
stagedFiles.push(`${indexStatus} ${file}`);
|
|
9635
|
-
}
|
|
9636
|
-
if (workStatus && workStatus !== " ") {
|
|
9637
|
-
changedFiles.push(`${workStatus} ${file}`);
|
|
9638
|
-
}
|
|
9639
|
-
}
|
|
9640
|
-
const logOutput = runGit("log --oneline -3", cwd) ?? "";
|
|
9641
|
-
const recentCommits = logOutput ? logOutput.split("\n").filter(Boolean) : [];
|
|
9642
|
-
const unpushedOutput = runGit("log @{u}..HEAD --oneline", cwd);
|
|
9643
|
-
const hasUnpushed = unpushedOutput !== null && unpushedOutput.trim().length > 0;
|
|
9644
|
-
return {
|
|
9645
|
-
branch,
|
|
9646
|
-
changedFiles,
|
|
9647
|
-
stagedFiles,
|
|
9648
|
-
recentCommits,
|
|
9649
|
-
hasUnpushed
|
|
9650
|
-
};
|
|
9651
|
-
}
|
|
9652
|
-
function formatGitContextForPrompt(ctx) {
|
|
9653
|
-
const lines = ["# Git Repository Status", ""];
|
|
9654
|
-
lines.push(`- **Branch**: \`${ctx.branch}\``);
|
|
9655
|
-
if (ctx.stagedFiles.length > 0) {
|
|
9656
|
-
lines.push(`- **Staged** (${ctx.stagedFiles.length} files):`);
|
|
9657
|
-
for (const f of ctx.stagedFiles.slice(0, 10)) {
|
|
9658
|
-
lines.push(` - ${f}`);
|
|
9659
|
-
}
|
|
9660
|
-
if (ctx.stagedFiles.length > 10) {
|
|
9661
|
-
lines.push(` - ... and ${ctx.stagedFiles.length - 10} more`);
|
|
9662
|
-
}
|
|
9663
|
-
}
|
|
9664
|
-
if (ctx.changedFiles.length > 0) {
|
|
9665
|
-
lines.push(`- **Modified** (${ctx.changedFiles.length} files):`);
|
|
9666
|
-
for (const f of ctx.changedFiles.slice(0, 10)) {
|
|
9667
|
-
lines.push(` - ${f}`);
|
|
9668
|
-
}
|
|
9669
|
-
if (ctx.changedFiles.length > 10) {
|
|
9670
|
-
lines.push(` - ... and ${ctx.changedFiles.length - 10} more`);
|
|
9671
|
-
}
|
|
9672
|
-
}
|
|
9673
|
-
if (ctx.stagedFiles.length === 0 && ctx.changedFiles.length === 0) {
|
|
9674
|
-
lines.push("- **Working tree**: clean");
|
|
9675
|
-
}
|
|
9676
|
-
if (ctx.recentCommits.length > 0) {
|
|
9677
|
-
lines.push("- **Recent commits**:");
|
|
9678
|
-
for (const c of ctx.recentCommits) {
|
|
9679
|
-
lines.push(` - ${c}`);
|
|
9680
|
-
}
|
|
9681
|
-
}
|
|
9682
|
-
if (ctx.hasUnpushed) {
|
|
9683
|
-
lines.push("- \u26A0\uFE0F Has unpushed commits");
|
|
9684
|
-
}
|
|
9685
|
-
return lines.join("\n");
|
|
9686
|
-
}
|
|
9687
|
-
|
|
9688
|
-
// src/memory/persistent-memory.ts
|
|
9689
|
-
function memoryStorePath(configDir) {
|
|
9690
|
-
return join11(configDir, MEMORY_STORE_FILE_NAME);
|
|
9691
|
-
}
|
|
9692
|
-
function memoryMarkdownPath(configDir) {
|
|
9693
|
-
return join11(configDir, MEMORY_FILE_NAME);
|
|
9694
|
-
}
|
|
9695
|
-
function getProjectKey(cwd = process.cwd()) {
|
|
9696
|
-
return resolve6(getGitRoot(cwd) ?? cwd);
|
|
9697
|
-
}
|
|
9698
|
-
function nowIso() {
|
|
9699
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9700
|
-
}
|
|
9701
|
-
function isValidScope(value) {
|
|
9702
|
-
return value === "personal" || value === "project" || value === "session" || value === "team";
|
|
9703
|
-
}
|
|
9704
|
-
function isValidSensitivity(value) {
|
|
9705
|
-
return value === "low" || value === "medium" || value === "high";
|
|
9706
|
-
}
|
|
9707
|
-
function normalizeEntry(value) {
|
|
9708
|
-
if (!value || typeof value !== "object") return null;
|
|
9709
|
-
const raw = value;
|
|
9710
|
-
const content = typeof raw.content === "string" ? raw.content.trim() : "";
|
|
9711
|
-
if (!content) return null;
|
|
9712
|
-
const createdAt = typeof raw.createdAt === "string" ? raw.createdAt : nowIso();
|
|
9713
|
-
return {
|
|
9714
|
-
id: typeof raw.id === "string" && raw.id ? raw.id : randomUUID(),
|
|
9715
|
-
content,
|
|
9716
|
-
scope: isValidScope(raw.scope) ? raw.scope : "personal",
|
|
9717
|
-
sourceSession: typeof raw.sourceSession === "string" && raw.sourceSession ? raw.sourceSession : void 0,
|
|
9718
|
-
sourceProject: typeof raw.sourceProject === "string" && raw.sourceProject ? raw.sourceProject : void 0,
|
|
9719
|
-
createdAt,
|
|
9720
|
-
updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : createdAt,
|
|
9721
|
-
expiresAt: typeof raw.expiresAt === "string" && raw.expiresAt ? raw.expiresAt : void 0,
|
|
9722
|
-
sensitivity: isValidSensitivity(raw.sensitivity) ? raw.sensitivity : "low",
|
|
9723
|
-
approved: raw.approved === true,
|
|
9724
|
-
redactedKinds: Array.isArray(raw.redactedKinds) ? raw.redactedKinds.filter((x) => typeof x === "string") : void 0
|
|
9725
|
-
};
|
|
9726
|
-
}
|
|
9727
|
-
function legacyEntriesFromMarkdown(configDir) {
|
|
9728
|
-
const file = memoryMarkdownPath(configDir);
|
|
9729
|
-
if (!existsSync17(file)) return [];
|
|
9730
|
-
const content = readFileSync12(file, "utf-8").trim();
|
|
9731
|
-
if (!content) return [];
|
|
9732
|
-
const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
|
|
9733
|
-
const chunks = parts.length > 1 ? parts : [content];
|
|
9734
|
-
return chunks.map((chunk, index) => {
|
|
9735
|
-
const match = chunk.match(/^##\s+([^\n]+)\n([\s\S]*)$/);
|
|
9736
|
-
const body = (match ? match[2] : chunk).trim();
|
|
9737
|
-
const parsed = match ? Date.parse(match[1].replace(" ", "T")) : NaN;
|
|
9738
|
-
const createdAt = Number.isFinite(parsed) ? new Date(parsed).toISOString() : nowIso();
|
|
9739
|
-
const id = "legacy-" + createHash2("sha1").update(`${index}
|
|
9740
|
-
${chunk}`).digest("hex").slice(0, 12);
|
|
9741
|
-
return normalizeEntry({ id, content: body, scope: "personal", createdAt, updatedAt: createdAt, sensitivity: "low", approved: true });
|
|
9742
|
-
}).filter((entry) => Boolean(entry));
|
|
9743
|
-
}
|
|
9744
|
-
function loadMemoryEntries(configDir) {
|
|
9745
|
-
const file = memoryStorePath(configDir);
|
|
9746
|
-
if (!existsSync17(file)) return legacyEntriesFromMarkdown(configDir);
|
|
9747
|
-
const text = readFileSync12(file, "utf-8").trim();
|
|
9748
|
-
if (!text) return [];
|
|
9749
|
-
const entries = [];
|
|
9750
|
-
for (const line of text.split("\n")) {
|
|
9751
|
-
const trimmed = line.trim();
|
|
9752
|
-
if (!trimmed) continue;
|
|
9753
|
-
try {
|
|
9754
|
-
const entry = normalizeEntry(JSON.parse(trimmed));
|
|
9755
|
-
if (entry) entries.push(entry);
|
|
9756
|
-
} catch {
|
|
9757
|
-
}
|
|
9758
|
-
}
|
|
9759
|
-
return entries;
|
|
9760
|
-
}
|
|
9761
|
-
function saveMemoryEntries(configDir, entries) {
|
|
9762
|
-
mkdirSync8(configDir, { recursive: true });
|
|
9763
|
-
const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
|
|
9764
|
-
atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
|
|
9765
|
-
}
|
|
9766
|
-
function isMemoryExpired(entry, at = /* @__PURE__ */ new Date()) {
|
|
9767
|
-
return Boolean(entry.expiresAt && Date.parse(entry.expiresAt) <= at.getTime());
|
|
9768
|
-
}
|
|
9769
|
-
function isMemoryVisibleInProject(entry, cwd = process.cwd()) {
|
|
9770
|
-
if (entry.scope !== "project") return true;
|
|
9771
|
-
return entry.sourceProject === getProjectKey(cwd);
|
|
9772
|
-
}
|
|
9773
|
-
function listMemoryEntries(configDir, options = {}) {
|
|
9774
|
-
const cwd = options.cwd ?? process.cwd();
|
|
9775
|
-
return loadMemoryEntries(configDir).filter((entry) => options.includeRejected || entry.approved).filter((entry) => options.includeExpired || !isMemoryExpired(entry)).filter((entry) => isMemoryVisibleInProject(entry, cwd));
|
|
9776
|
-
}
|
|
9777
|
-
function addMemoryEntry(configDir, content, options = {}) {
|
|
9778
|
-
const original = content.trim();
|
|
9779
|
-
if (!original) throw new Error("memory content is required");
|
|
9780
|
-
const redact = options.redact ?? true;
|
|
9781
|
-
const redacted = redact ? redactString(original, { enabled: true, patterns: options.patterns ?? DEFAULT_PATTERNS, customRegexes: options.customPatterns }) : { redacted: original, hits: [] };
|
|
9782
|
-
const sensitivity = options.sensitivity ?? (redacted.hits.length > 0 ? "high" : "low");
|
|
9783
|
-
const approved = options.approved ?? (sensitivity === "low" && redacted.hits.length === 0);
|
|
9784
|
-
const scope = options.scope ?? "personal";
|
|
9785
|
-
const timestamp = nowIso();
|
|
9786
|
-
const entry = {
|
|
9787
|
-
id: randomUUID(),
|
|
9788
|
-
content: redacted.redacted.trim(),
|
|
9789
|
-
scope,
|
|
9790
|
-
sourceSession: options.sourceSession,
|
|
9791
|
-
sourceProject: scope === "project" ? getProjectKey(options.cwd) : void 0,
|
|
9792
|
-
createdAt: timestamp,
|
|
9793
|
-
updatedAt: timestamp,
|
|
9794
|
-
expiresAt: options.expiresAt,
|
|
9795
|
-
sensitivity,
|
|
9796
|
-
approved,
|
|
9797
|
-
redactedKinds: [...new Set(redacted.hits.map((hit) => hit.kind))]
|
|
9798
|
-
};
|
|
9799
|
-
const entries = loadMemoryEntries(configDir);
|
|
9800
|
-
entries.push(entry);
|
|
9801
|
-
saveMemoryEntries(configDir, entries);
|
|
9802
|
-
return entry;
|
|
9803
|
-
}
|
|
9804
|
-
function updateMemoryApproval(configDir, idPrefix, approved) {
|
|
9805
|
-
const entries = loadMemoryEntries(configDir);
|
|
9806
|
-
const entry = findUnique(entries, idPrefix);
|
|
9807
|
-
entry.approved = approved;
|
|
9808
|
-
entry.updatedAt = nowIso();
|
|
9809
|
-
saveMemoryEntries(configDir, entries);
|
|
9810
|
-
return entry;
|
|
9811
|
-
}
|
|
9812
|
-
function deleteMemoryEntry(configDir, idPrefix) {
|
|
9813
|
-
const entries = loadMemoryEntries(configDir);
|
|
9814
|
-
const entry = findUnique(entries, idPrefix);
|
|
9815
|
-
saveMemoryEntries(configDir, entries.filter((candidate) => candidate.id !== entry.id));
|
|
9816
|
-
return entry;
|
|
9817
|
-
}
|
|
9818
|
-
function expireMemoryEntry(configDir, idPrefix, expiresAt = nowIso()) {
|
|
9819
|
-
const entries = loadMemoryEntries(configDir);
|
|
9820
|
-
const entry = findUnique(entries, idPrefix);
|
|
9821
|
-
entry.expiresAt = expiresAt;
|
|
9822
|
-
entry.updatedAt = nowIso();
|
|
9823
|
-
saveMemoryEntries(configDir, entries);
|
|
9824
|
-
return entry;
|
|
9825
|
-
}
|
|
9826
|
-
function exportMemoryEntries(configDir, cwd = process.cwd()) {
|
|
9827
|
-
return JSON.stringify(listMemoryEntries(configDir, { cwd, includeExpired: true, includeRejected: true }), null, 2);
|
|
9828
|
-
}
|
|
9829
|
-
function findUnique(entries, idPrefix) {
|
|
9830
|
-
const matches = entries.filter((entry) => entry.id.startsWith(idPrefix));
|
|
9831
|
-
if (matches.length === 0) throw new Error(`memory id not found: ${idPrefix}`);
|
|
9832
|
-
if (matches.length > 1) throw new Error(`memory id is ambiguous: ${idPrefix}`);
|
|
9833
|
-
return matches[0];
|
|
9834
|
-
}
|
|
9835
|
-
function formatMemoryForPrompt(configDir, cwd = process.cwd()) {
|
|
9836
|
-
const entries = listMemoryEntries(configDir, { cwd }).filter((entry) => entry.approved && !isMemoryExpired(entry));
|
|
9837
|
-
if (entries.length === 0) return null;
|
|
9838
|
-
let content = entries.map((entry) => {
|
|
9839
|
-
const source = [
|
|
9840
|
-
`id ${entry.id.slice(0, 8)}`,
|
|
9841
|
-
`scope ${entry.scope}`,
|
|
9842
|
-
entry.sourceSession ? `source session ${entry.sourceSession.slice(0, 8)}` : "source manual/tool",
|
|
9843
|
-
entry.sourceProject ? `project ${entry.sourceProject}` : "",
|
|
9844
|
-
`sensitivity ${entry.sensitivity}`
|
|
9845
|
-
].filter(Boolean).join(" \xB7 ");
|
|
9846
|
-
return `## ${entry.createdAt.replace("T", " ").slice(0, 19)}
|
|
9847
|
-
Source: ${source}
|
|
9848
|
-
${entry.content}`;
|
|
9849
|
-
}).join("\n\n");
|
|
9850
|
-
if (content.length > MEMORY_MAX_CHARS) {
|
|
9851
|
-
content = content.slice(-MEMORY_MAX_CHARS);
|
|
9852
|
-
const firstEntry = content.indexOf("\n## ");
|
|
9853
|
-
if (firstEntry !== -1) content = content.slice(firstEntry + 1);
|
|
9854
|
-
}
|
|
9855
|
-
return { content, entryCount: entries.length };
|
|
9856
|
-
}
|
|
9857
|
-
|
|
9858
|
-
// src/tools/builtin/save-memory.ts
|
|
9618
|
+
import { homedir as homedir5 } from "os";
|
|
9859
9619
|
function getConfigDir() {
|
|
9860
|
-
return
|
|
9861
|
-
}
|
|
9862
|
-
function parseScope(value) {
|
|
9863
|
-
return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
|
|
9620
|
+
return join10(homedir5(), CONFIG_DIR_NAME);
|
|
9864
9621
|
}
|
|
9865
9622
|
function parseSensitivity(value) {
|
|
9866
9623
|
return value === "low" || value === "medium" || value === "high" ? value : void 0;
|
|
@@ -9868,7 +9625,7 @@ function parseSensitivity(value) {
|
|
|
9868
9625
|
var saveMemoryTool = {
|
|
9869
9626
|
definition: {
|
|
9870
9627
|
name: "save_memory",
|
|
9871
|
-
description: "Save important information to governed persistent memory.
|
|
9628
|
+
description: "Save important information to governed persistent memory. Entries saved by this tool are stored as PENDING and only take effect after the user runs /memory approve <id> \u2014 tell the user the returned id so they can approve it. Use concise entries for user preferences, project decisions, recurring patterns, and key findings.",
|
|
9872
9629
|
parameters: {
|
|
9873
9630
|
content: {
|
|
9874
9631
|
type: "string",
|
|
@@ -9877,7 +9634,7 @@ var saveMemoryTool = {
|
|
|
9877
9634
|
},
|
|
9878
9635
|
scope: {
|
|
9879
9636
|
type: "string",
|
|
9880
|
-
description: "Memory scope: personal
|
|
9637
|
+
description: "Memory scope: personal (default, injected everywhere) or project (only injected inside the current project).",
|
|
9881
9638
|
required: false
|
|
9882
9639
|
},
|
|
9883
9640
|
sensitivity: {
|
|
@@ -9889,6 +9646,11 @@ var saveMemoryTool = {
|
|
|
9889
9646
|
type: "string",
|
|
9890
9647
|
description: "Optional ISO timestamp after which this memory should no longer be injected.",
|
|
9891
9648
|
required: false
|
|
9649
|
+
},
|
|
9650
|
+
updateId: {
|
|
9651
|
+
type: "string",
|
|
9652
|
+
description: "Optional id (or unique id prefix) of an existing memory entry to update in place instead of creating a new one. Use this when a previously saved memory is outdated \u2014 do not save a contradicting duplicate. Updated entries go back to pending and need user re-approval.",
|
|
9653
|
+
required: false
|
|
9892
9654
|
}
|
|
9893
9655
|
},
|
|
9894
9656
|
dangerous: false
|
|
@@ -9896,16 +9658,29 @@ var saveMemoryTool = {
|
|
|
9896
9658
|
async execute(args) {
|
|
9897
9659
|
const content = String(args["content"] ?? "").trim();
|
|
9898
9660
|
if (!content) throw new ToolError("save_memory", "content is required");
|
|
9661
|
+
const updateId = typeof args["updateId"] === "string" ? args["updateId"].trim() : "";
|
|
9662
|
+
if (updateId) {
|
|
9663
|
+
try {
|
|
9664
|
+
const updated = updateMemoryEntry(getConfigDir(), updateId, content, { source: "tool", redact: true });
|
|
9665
|
+
const redactedNote = updated.redactedKinds?.length ? ` Redacted: ${updated.redactedKinds.join(", ")}.` : "";
|
|
9666
|
+
return `Memory ${updated.id.slice(0, 8)} updated (pending approval \u2014 the user must run \`/memory approve ${updated.id.slice(0, 8)}\` before the new content is injected; mention this to the user). scope=${updated.scope} sensitivity=${updated.sensitivity}.${redactedNote}`;
|
|
9667
|
+
} catch (err) {
|
|
9668
|
+
throw new ToolError("save_memory", err instanceof Error ? err.message : String(err));
|
|
9669
|
+
}
|
|
9670
|
+
}
|
|
9671
|
+
const similar = findSimilarMemory(loadMemoryEntries(getConfigDir()), content);
|
|
9899
9672
|
const entry = addMemoryEntry(getConfigDir(), content, {
|
|
9900
|
-
scope:
|
|
9673
|
+
scope: parseWritableScope(args["scope"]) ?? "personal",
|
|
9674
|
+
source: "tool",
|
|
9901
9675
|
sensitivity: parseSensitivity(args["sensitivity"]),
|
|
9902
9676
|
expiresAt: typeof args["expiresAt"] === "string" ? args["expiresAt"] : void 0,
|
|
9903
9677
|
cwd: process.cwd(),
|
|
9904
9678
|
redact: true
|
|
9905
9679
|
});
|
|
9906
|
-
const status = entry.approved ? "approved and active" :
|
|
9680
|
+
const status = entry.approved ? "approved and active" : `pending approval \u2014 the user must run \`/memory approve ${entry.id.slice(0, 8)}\` before it is injected; mention this to the user`;
|
|
9907
9681
|
const redacted = entry.redactedKinds?.length ? ` Redacted: ${entry.redactedKinds.join(", ")}.` : "";
|
|
9908
|
-
|
|
9682
|
+
const similarNote = similar ? ` Warning: a similar entry already exists (id=${similar.id.slice(0, 8)}) \u2014 next time pass updateId to revise it instead of duplicating.` : "";
|
|
9683
|
+
return `Memory saved (${status}). id=${entry.id.slice(0, 8)} scope=${entry.scope} sensitivity=${entry.sensitivity}.${redacted}${similarNote}`;
|
|
9909
9684
|
}
|
|
9910
9685
|
};
|
|
9911
9686
|
|
|
@@ -9949,7 +9724,7 @@ function promptUser(rl, question) {
|
|
|
9949
9724
|
console.log();
|
|
9950
9725
|
console.log(chalk4.cyan("\u2753 ") + chalk4.bold(question));
|
|
9951
9726
|
process.stdout.write(chalk4.cyan("> "));
|
|
9952
|
-
return new Promise((
|
|
9727
|
+
return new Promise((resolve13) => {
|
|
9953
9728
|
let completed = false;
|
|
9954
9729
|
const cleanup = (answer) => {
|
|
9955
9730
|
if (completed) return;
|
|
@@ -9959,7 +9734,7 @@ function promptUser(rl, question) {
|
|
|
9959
9734
|
rl.pause();
|
|
9960
9735
|
rlAny.output = savedOutput;
|
|
9961
9736
|
askUserContext.prompting = false;
|
|
9962
|
-
|
|
9737
|
+
resolve13(answer);
|
|
9963
9738
|
};
|
|
9964
9739
|
const onLine = (line) => {
|
|
9965
9740
|
cleanup(line);
|
|
@@ -10191,14 +9966,14 @@ function formatResults2(query, data, _requested) {
|
|
|
10191
9966
|
}
|
|
10192
9967
|
|
|
10193
9968
|
// src/agents/agent-config.ts
|
|
10194
|
-
import { existsSync as
|
|
10195
|
-
import { join as
|
|
9969
|
+
import { existsSync as existsSync17, readdirSync as readdirSync8, readFileSync as readFileSync13 } from "fs";
|
|
9970
|
+
import { join as join12 } from "path";
|
|
10196
9971
|
import { homedir as homedir6 } from "os";
|
|
10197
9972
|
|
|
10198
9973
|
// src/plugins/plugin-manager.ts
|
|
10199
|
-
import { existsSync as
|
|
10200
|
-
import { basename as basename6, dirname as
|
|
10201
|
-
import { createHash as
|
|
9974
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync7, readFileSync as readFileSync12, realpathSync as realpathSync2, cpSync, rmSync, statSync as statSync7 } from "fs";
|
|
9975
|
+
import { basename as basename6, dirname as dirname7, join as join11, relative as relative3, resolve as resolve6, sep as sep3 } from "path";
|
|
9976
|
+
import { createHash as createHash2 } from "crypto";
|
|
10202
9977
|
import { z as z2 } from "zod";
|
|
10203
9978
|
var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
|
|
10204
9979
|
var STATE_FILE = "plugin-state.json";
|
|
@@ -10257,19 +10032,19 @@ var PluginManifestSchema = z2.object({
|
|
|
10257
10032
|
permissionHints: z2.array(z2.string()).default([])
|
|
10258
10033
|
}).strict();
|
|
10259
10034
|
function pluginRoot(configDir) {
|
|
10260
|
-
return
|
|
10035
|
+
return join11(configDir, PLUGINS_DIR_NAME);
|
|
10261
10036
|
}
|
|
10262
10037
|
function pluginManifestPath(pluginDir) {
|
|
10263
|
-
return
|
|
10038
|
+
return join11(pluginDir, MANIFEST_RELATIVE);
|
|
10264
10039
|
}
|
|
10265
10040
|
function statePath(configDir) {
|
|
10266
|
-
return
|
|
10041
|
+
return join11(pluginRoot(configDir), STATE_FILE);
|
|
10267
10042
|
}
|
|
10268
10043
|
function loadState(configDir) {
|
|
10269
10044
|
const file = statePath(configDir);
|
|
10270
|
-
if (!
|
|
10045
|
+
if (!existsSync16(file)) return { version: 2, plugins: [] };
|
|
10271
10046
|
try {
|
|
10272
|
-
const parsed = JSON.parse(
|
|
10047
|
+
const parsed = JSON.parse(readFileSync12(file, "utf-8"));
|
|
10273
10048
|
const rawPlugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
|
|
10274
10049
|
return { version: 2, plugins: rawPlugins.map((p) => migrateEntry(p)) };
|
|
10275
10050
|
} catch {
|
|
@@ -10292,7 +10067,7 @@ function migrateEntry(entry) {
|
|
|
10292
10067
|
};
|
|
10293
10068
|
}
|
|
10294
10069
|
function saveState(configDir, state2) {
|
|
10295
|
-
|
|
10070
|
+
mkdirSync8(pluginRoot(configDir), { recursive: true });
|
|
10296
10071
|
atomicWriteFileSync(statePath(configDir), JSON.stringify(state2, null, 2));
|
|
10297
10072
|
}
|
|
10298
10073
|
function normalizeForHash(buf) {
|
|
@@ -10300,7 +10075,7 @@ function normalizeForHash(buf) {
|
|
|
10300
10075
|
return Buffer.from(str, "utf-8");
|
|
10301
10076
|
}
|
|
10302
10077
|
function extractCommandPaths(command, pluginDir) {
|
|
10303
|
-
const resolvedDir =
|
|
10078
|
+
const resolvedDir = resolve6(pluginDir);
|
|
10304
10079
|
const refs = [];
|
|
10305
10080
|
for (const m of command.matchAll(/\$PLUGIN_DIR[\\/]([^"'\s]+)/g)) {
|
|
10306
10081
|
refs.push(m[1]);
|
|
@@ -10317,9 +10092,9 @@ function extractCommandPaths(command, pluginDir) {
|
|
|
10317
10092
|
if (seen.has(ref)) continue;
|
|
10318
10093
|
seen.add(ref);
|
|
10319
10094
|
try {
|
|
10320
|
-
const abs =
|
|
10095
|
+
const abs = resolve6(resolvedDir, ref);
|
|
10321
10096
|
if (!abs.startsWith(resolvedDir + sep3)) continue;
|
|
10322
|
-
if (
|
|
10097
|
+
if (existsSync16(abs)) {
|
|
10323
10098
|
const real = realpathSync2(abs);
|
|
10324
10099
|
if (!real.startsWith(resolvedDir + sep3)) continue;
|
|
10325
10100
|
valid.push(relative3(resolvedDir, real));
|
|
@@ -10330,7 +10105,7 @@ function extractCommandPaths(command, pluginDir) {
|
|
|
10330
10105
|
return valid;
|
|
10331
10106
|
}
|
|
10332
10107
|
function hashPluginContent(pluginDir, manifest) {
|
|
10333
|
-
const resolvedDir =
|
|
10108
|
+
const resolvedDir = resolve6(pluginDir);
|
|
10334
10109
|
const assetRels = /* @__PURE__ */ new Set(
|
|
10335
10110
|
[...manifest.skills, ...manifest.commands, ...manifest.agents]
|
|
10336
10111
|
);
|
|
@@ -10350,11 +10125,11 @@ function hashPluginContent(pluginDir, manifest) {
|
|
|
10350
10125
|
}
|
|
10351
10126
|
}
|
|
10352
10127
|
const sorted = [...assetRels].sort();
|
|
10353
|
-
const hasher =
|
|
10354
|
-
const manifestPath =
|
|
10355
|
-
hasher.update(normalizeForHash(
|
|
10128
|
+
const hasher = createHash2("sha256");
|
|
10129
|
+
const manifestPath = join11(resolvedDir, MANIFEST_RELATIVE);
|
|
10130
|
+
hasher.update(normalizeForHash(readFileSync12(manifestPath)));
|
|
10356
10131
|
for (const rel of sorted) {
|
|
10357
|
-
const abs =
|
|
10132
|
+
const abs = resolve6(resolvedDir, rel);
|
|
10358
10133
|
if (!abs.startsWith(resolvedDir + sep3)) {
|
|
10359
10134
|
throw new Error(`plugin asset escapes plugin dir: ${rel}`);
|
|
10360
10135
|
}
|
|
@@ -10372,7 +10147,7 @@ function hashPluginContent(pluginDir, manifest) {
|
|
|
10372
10147
|
if (!realPath.startsWith(resolvedDir + sep3)) {
|
|
10373
10148
|
throw new Error(`plugin asset symlink escapes plugin dir: ${rel} \u2192 ${realPath}`);
|
|
10374
10149
|
}
|
|
10375
|
-
hasher.update(normalizeForHash(
|
|
10150
|
+
hasher.update(normalizeForHash(readFileSync12(abs)));
|
|
10376
10151
|
}
|
|
10377
10152
|
return hasher.digest("hex");
|
|
10378
10153
|
}
|
|
@@ -10380,22 +10155,22 @@ function effectiveHash(entry) {
|
|
|
10380
10155
|
return entry.contentHash ?? entry.hash ?? "";
|
|
10381
10156
|
}
|
|
10382
10157
|
function readPluginManifest(manifestPath) {
|
|
10383
|
-
return PluginManifestSchema.parse(JSON.parse(
|
|
10158
|
+
return PluginManifestSchema.parse(JSON.parse(readFileSync12(manifestPath, "utf-8")));
|
|
10384
10159
|
}
|
|
10385
10160
|
function findPluginSourceDir(inputPath) {
|
|
10386
|
-
const abs =
|
|
10387
|
-
const direct = statSync7(abs).isDirectory() ? abs :
|
|
10388
|
-
if (
|
|
10389
|
-
const nested =
|
|
10390
|
-
if (
|
|
10161
|
+
const abs = resolve6(inputPath);
|
|
10162
|
+
const direct = statSync7(abs).isDirectory() ? abs : dirname7(abs);
|
|
10163
|
+
if (existsSync16(pluginManifestPath(direct))) return direct;
|
|
10164
|
+
const nested = join11(direct, ".aicli-plugin");
|
|
10165
|
+
if (existsSync16(join11(nested, "plugin.json"))) return direct;
|
|
10391
10166
|
throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
|
|
10392
10167
|
}
|
|
10393
10168
|
function installPlugin(configDir, inputPath) {
|
|
10394
10169
|
const sourceDir = findPluginSourceDir(inputPath);
|
|
10395
10170
|
const manifest = readPluginManifest(pluginManifestPath(sourceDir));
|
|
10396
|
-
const targetDir =
|
|
10397
|
-
|
|
10398
|
-
if (
|
|
10171
|
+
const targetDir = join11(pluginRoot(configDir), manifest.name);
|
|
10172
|
+
mkdirSync8(pluginRoot(configDir), { recursive: true });
|
|
10173
|
+
if (existsSync16(targetDir)) rmSync(targetDir, { recursive: true, force: true });
|
|
10399
10174
|
cpSync(sourceDir, targetDir, { recursive: true });
|
|
10400
10175
|
const manifestPath = pluginManifestPath(targetDir);
|
|
10401
10176
|
const contentHash = hashPluginContent(targetDir, manifest);
|
|
@@ -10453,18 +10228,18 @@ function getInstalledPlugin(configDir, name) {
|
|
|
10453
10228
|
}
|
|
10454
10229
|
function listInstalledPlugins(configDir) {
|
|
10455
10230
|
const root = pluginRoot(configDir);
|
|
10456
|
-
if (!
|
|
10231
|
+
if (!existsSync16(root)) return [];
|
|
10457
10232
|
const state2 = loadState(configDir);
|
|
10458
10233
|
const out = [];
|
|
10459
10234
|
for (const entry of readdirSync7(root)) {
|
|
10460
|
-
const dir =
|
|
10235
|
+
const dir = join11(root, entry);
|
|
10461
10236
|
try {
|
|
10462
10237
|
if (!statSync7(dir).isDirectory()) continue;
|
|
10463
10238
|
} catch {
|
|
10464
10239
|
continue;
|
|
10465
10240
|
}
|
|
10466
10241
|
const manifestPath = pluginManifestPath(dir);
|
|
10467
|
-
if (!
|
|
10242
|
+
if (!existsSync16(manifestPath)) continue;
|
|
10468
10243
|
try {
|
|
10469
10244
|
const manifest = readPluginManifest(manifestPath);
|
|
10470
10245
|
const contentHash = hashPluginContent(dir, manifest);
|
|
@@ -10502,9 +10277,9 @@ function activePlugins(configDir) {
|
|
|
10502
10277
|
function uniqueDirs(plugin, rels) {
|
|
10503
10278
|
const dirs = /* @__PURE__ */ new Set();
|
|
10504
10279
|
for (const rel of rels) {
|
|
10505
|
-
const abs =
|
|
10506
|
-
if (!abs.startsWith(
|
|
10507
|
-
if (
|
|
10280
|
+
const abs = resolve6(plugin.dir, rel);
|
|
10281
|
+
if (!abs.startsWith(resolve6(plugin.dir))) continue;
|
|
10282
|
+
if (existsSync16(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname7(abs));
|
|
10508
10283
|
}
|
|
10509
10284
|
return [...dirs];
|
|
10510
10285
|
}
|
|
@@ -10667,13 +10442,13 @@ function parseAgentConfig(raw, source, path3) {
|
|
|
10667
10442
|
return cfg;
|
|
10668
10443
|
}
|
|
10669
10444
|
function loadAgentDir(dir, source) {
|
|
10670
|
-
if (!
|
|
10445
|
+
if (!existsSync17(dir)) return [];
|
|
10671
10446
|
const out = [];
|
|
10672
10447
|
for (const file of readdirSync8(dir)) {
|
|
10673
10448
|
if (!file.endsWith(".json")) continue;
|
|
10674
|
-
const path3 =
|
|
10449
|
+
const path3 = join12(dir, file);
|
|
10675
10450
|
try {
|
|
10676
|
-
const parsed = JSON.parse(
|
|
10451
|
+
const parsed = JSON.parse(readFileSync13(path3, "utf-8"));
|
|
10677
10452
|
const cfg = parseAgentConfig(parsed, source, path3);
|
|
10678
10453
|
if (cfg) out.push(cfg);
|
|
10679
10454
|
} catch {
|
|
@@ -10683,8 +10458,8 @@ function loadAgentDir(dir, source) {
|
|
|
10683
10458
|
}
|
|
10684
10459
|
function getAgentDirs(configDir, cwd = process.cwd()) {
|
|
10685
10460
|
return {
|
|
10686
|
-
user:
|
|
10687
|
-
project:
|
|
10461
|
+
user: join12(configDir ?? join12(homedir6(), CONFIG_DIR_NAME), "agents"),
|
|
10462
|
+
project: join12(cwd, CONFIG_DIR_NAME, "agents")
|
|
10688
10463
|
};
|
|
10689
10464
|
}
|
|
10690
10465
|
function listAgentConfigs(configDir, cwd = process.cwd()) {
|
|
@@ -10731,7 +10506,7 @@ var nextId = 1;
|
|
|
10731
10506
|
var preferredAgentName = "worker";
|
|
10732
10507
|
var runs = [];
|
|
10733
10508
|
var MAX_RUNS = 50;
|
|
10734
|
-
function
|
|
10509
|
+
function nowIso() {
|
|
10735
10510
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
10736
10511
|
}
|
|
10737
10512
|
function startAgentRun(input) {
|
|
@@ -10740,7 +10515,7 @@ function startAgentRun(input) {
|
|
|
10740
10515
|
agentName: input.agentName,
|
|
10741
10516
|
task: input.task,
|
|
10742
10517
|
status: "running",
|
|
10743
|
-
startedAt:
|
|
10518
|
+
startedAt: nowIso(),
|
|
10744
10519
|
toolNames: [],
|
|
10745
10520
|
agentIndex: input.agentIndex,
|
|
10746
10521
|
stopRequested: false
|
|
@@ -10758,7 +10533,7 @@ function finishAgentRun(runId, status, summary, error) {
|
|
|
10758
10533
|
const run = runs.find((r) => r.id === runId);
|
|
10759
10534
|
if (!run) return;
|
|
10760
10535
|
run.status = status;
|
|
10761
|
-
run.finishedAt =
|
|
10536
|
+
run.finishedAt = nowIso();
|
|
10762
10537
|
if (summary) run.summary = summary;
|
|
10763
10538
|
if (error) run.error = error;
|
|
10764
10539
|
}
|
|
@@ -11184,7 +10959,7 @@ var spawnAgentTool = {
|
|
|
11184
10959
|
|
|
11185
10960
|
// src/tools/builtin/task-manager.ts
|
|
11186
10961
|
import { spawn as spawn4 } from "child_process";
|
|
11187
|
-
import { randomUUID
|
|
10962
|
+
import { randomUUID } from "crypto";
|
|
11188
10963
|
import { platform as platform5 } from "os";
|
|
11189
10964
|
var MAX_OUTPUT_CHARS = 1e4;
|
|
11190
10965
|
var MAX_OUTPUT_BYTES = 64 * 1024;
|
|
@@ -11217,7 +10992,7 @@ function createTask(command, description) {
|
|
|
11217
10992
|
}
|
|
11218
10993
|
}
|
|
11219
10994
|
}
|
|
11220
|
-
const id =
|
|
10995
|
+
const id = randomUUID().slice(0, 8);
|
|
11221
10996
|
const { shell, args } = shellInvocation(command);
|
|
11222
10997
|
const proc = spawn4(shell, args, {
|
|
11223
10998
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -11399,20 +11174,20 @@ var taskStopTool = {
|
|
|
11399
11174
|
|
|
11400
11175
|
// src/tools/builtin/git-tools.ts
|
|
11401
11176
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
11402
|
-
import { existsSync as
|
|
11403
|
-
import { join as
|
|
11177
|
+
import { existsSync as existsSync18 } from "fs";
|
|
11178
|
+
import { join as join13 } from "path";
|
|
11404
11179
|
function assertGitRepo(cwd) {
|
|
11405
11180
|
let dir = cwd;
|
|
11406
11181
|
const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
|
|
11407
11182
|
while (dir && dir !== root) {
|
|
11408
|
-
if (
|
|
11409
|
-
const parent =
|
|
11183
|
+
if (existsSync18(join13(dir, ".git"))) return;
|
|
11184
|
+
const parent = join13(dir, "..");
|
|
11410
11185
|
if (parent === dir) break;
|
|
11411
11186
|
dir = parent;
|
|
11412
11187
|
}
|
|
11413
11188
|
throw new ToolError("git", "Not inside a git repository. Run this command from a directory under a git-tracked project.");
|
|
11414
11189
|
}
|
|
11415
|
-
function
|
|
11190
|
+
function runGit(args, cwd, maxBuffer = 10 * 1024 * 1024) {
|
|
11416
11191
|
try {
|
|
11417
11192
|
const output = execFileSync2("git", args, {
|
|
11418
11193
|
cwd,
|
|
@@ -11447,11 +11222,11 @@ var gitStatusTool = {
|
|
|
11447
11222
|
assertGitRepo(cwd);
|
|
11448
11223
|
let branch = "(unknown)";
|
|
11449
11224
|
try {
|
|
11450
|
-
branch =
|
|
11225
|
+
branch = runGit(["symbolic-ref", "--short", "HEAD"], cwd).trim();
|
|
11451
11226
|
} catch {
|
|
11452
11227
|
branch = "(detached)";
|
|
11453
11228
|
}
|
|
11454
|
-
const porcelain =
|
|
11229
|
+
const porcelain = runGit(["status", "--porcelain=v1", "-b"], cwd);
|
|
11455
11230
|
const lines = porcelain.split("\n").filter((l) => l.length > 0);
|
|
11456
11231
|
const branchLine = lines[0]?.startsWith("##") ? lines[0].slice(3) : `${branch}`;
|
|
11457
11232
|
const fileLines = lines.slice(lines[0]?.startsWith("##") ? 1 : 0);
|
|
@@ -11538,7 +11313,7 @@ var gitDiffTool = {
|
|
|
11538
11313
|
if (args["file"]) {
|
|
11539
11314
|
gitArgs.push("--", String(args["file"]));
|
|
11540
11315
|
}
|
|
11541
|
-
const output =
|
|
11316
|
+
const output = runGit(gitArgs, cwd);
|
|
11542
11317
|
if (output.trim().length === 0) {
|
|
11543
11318
|
const label = args["staged"] ? "staged" : args["commit_range"] ? String(args["commit_range"]) : "unstaged";
|
|
11544
11319
|
return `No ${label} changes.`;
|
|
@@ -11600,7 +11375,7 @@ var gitLogTool = {
|
|
|
11600
11375
|
if (args["file"]) {
|
|
11601
11376
|
gitArgs.push("--", String(args["file"]));
|
|
11602
11377
|
}
|
|
11603
|
-
const output =
|
|
11378
|
+
const output = runGit(gitArgs, cwd);
|
|
11604
11379
|
if (output.trim().length === 0) {
|
|
11605
11380
|
return "No commits found matching the filter.";
|
|
11606
11381
|
}
|
|
@@ -11647,9 +11422,9 @@ var gitCommitTool = {
|
|
|
11647
11422
|
throw new ToolError("git_commit", "Cannot use both stage_all and files at the same time.");
|
|
11648
11423
|
}
|
|
11649
11424
|
if (files.length > 0) {
|
|
11650
|
-
|
|
11425
|
+
runGit(["add", "--", ...files], cwd);
|
|
11651
11426
|
}
|
|
11652
|
-
const statusBefore =
|
|
11427
|
+
const statusBefore = runGit(["status", "--porcelain"], cwd);
|
|
11653
11428
|
const hasStaged = statusBefore.split("\n").some((l) => l.length > 0 && l[0] !== " " && l[0] !== "?");
|
|
11654
11429
|
const hasUnstaged = statusBefore.split("\n").some((l) => l.length > 1 && l[1] !== " " && l[1] !== "?");
|
|
11655
11430
|
if (!hasStaged && !stageAll) {
|
|
@@ -11660,9 +11435,9 @@ var gitCommitTool = {
|
|
|
11660
11435
|
}
|
|
11661
11436
|
const commitArgs = ["commit", "-m", message];
|
|
11662
11437
|
if (stageAll) commitArgs.splice(1, 0, "-a");
|
|
11663
|
-
const commitOutput =
|
|
11664
|
-
const hash =
|
|
11665
|
-
const summary =
|
|
11438
|
+
const commitOutput = runGit(commitArgs, cwd);
|
|
11439
|
+
const hash = runGit(["rev-parse", "--short", "HEAD"], cwd).trim();
|
|
11440
|
+
const summary = runGit(["log", "-1", "--pretty=format:%h %s"], cwd).trim();
|
|
11666
11441
|
return `\u2713 Committed ${hash}
|
|
11667
11442
|
${summary}
|
|
11668
11443
|
|
|
@@ -11671,9 +11446,9 @@ ${commitOutput.trim()}`;
|
|
|
11671
11446
|
};
|
|
11672
11447
|
|
|
11673
11448
|
// src/tools/builtin/notebook-edit.ts
|
|
11674
|
-
import { readFileSync as
|
|
11449
|
+
import { readFileSync as readFileSync14, existsSync as existsSync19 } from "fs";
|
|
11675
11450
|
import { writeFile } from "fs/promises";
|
|
11676
|
-
import { resolve as
|
|
11451
|
+
import { resolve as resolve7, extname as extname2 } from "path";
|
|
11677
11452
|
var notebookEditTool = {
|
|
11678
11453
|
definition: {
|
|
11679
11454
|
name: "notebook_edit",
|
|
@@ -11722,14 +11497,14 @@ var notebookEditTool = {
|
|
|
11722
11497
|
if (!Number.isInteger(cellIndexRaw) || cellIndexRaw < 1) {
|
|
11723
11498
|
throw new ToolError("notebook_edit", "cell_index must be a positive integer (1-based)");
|
|
11724
11499
|
}
|
|
11725
|
-
const absPath =
|
|
11500
|
+
const absPath = resolve7(filePath);
|
|
11726
11501
|
if (extname2(absPath).toLowerCase() !== ".ipynb") {
|
|
11727
11502
|
throw new ToolError("notebook_edit", "path must point to a .ipynb file");
|
|
11728
11503
|
}
|
|
11729
|
-
if (!
|
|
11504
|
+
if (!existsSync19(absPath)) {
|
|
11730
11505
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
11731
11506
|
}
|
|
11732
|
-
const raw =
|
|
11507
|
+
const raw = readFileSync14(absPath, "utf-8");
|
|
11733
11508
|
const nb = parseNotebook(raw);
|
|
11734
11509
|
const cellIdx0 = cellIndexRaw - 1;
|
|
11735
11510
|
undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
|
|
@@ -12055,6 +11830,8 @@ ${lines.join("\n")}`;
|
|
|
12055
11830
|
};
|
|
12056
11831
|
|
|
12057
11832
|
// src/tools/builtin/recall-memory.ts
|
|
11833
|
+
import { join as join14 } from "path";
|
|
11834
|
+
import { homedir as homedir7 } from "os";
|
|
12058
11835
|
function formatHit(h, i) {
|
|
12059
11836
|
const ts = h.chunk.timestamp.slice(0, 16).replace("T", " ");
|
|
12060
11837
|
const title = h.chunk.sessionTitle ? ` \xB7 ${h.chunk.sessionTitle}` : "";
|
|
@@ -12067,7 +11844,7 @@ function formatHit(h, i) {
|
|
|
12067
11844
|
var recallMemoryTool = {
|
|
12068
11845
|
definition: {
|
|
12069
11846
|
name: "recall_memory",
|
|
12070
|
-
description: '
|
|
11847
|
+
description: 'Search long-term memory: governed persistent memory entries (keyword match) plus semantic search over past chat sessions. Call this whenever the user references something that may have been discussed before ("last time", "remember", "\u4E4B\u524D", "\u4E0A\u6B21"), when context is ambiguous and continuity matters, or when you want to check what decisions or preferences have been established across prior conversations \u2014 including memories that were dropped from the injected system prompt by the size budget. Returns persistent-memory hits first, then up to `topK` chat snippets with session id, timestamp, and cosine similarity score. Prefer this over asking the user "can you remind me".',
|
|
12071
11848
|
parameters: {
|
|
12072
11849
|
query: {
|
|
12073
11850
|
type: "string",
|
|
@@ -12104,9 +11881,21 @@ var recallMemoryTool = {
|
|
|
12104
11881
|
const excludeCurrent = Boolean(args["excludeCurrentSession"]);
|
|
12105
11882
|
const currentId = args["currentSessionId"] ? String(args["currentSessionId"]) : void 0;
|
|
12106
11883
|
const minScore = args["minScore"] !== void 0 ? Number(args["minScore"]) : 0.25;
|
|
11884
|
+
const configDir = join14(homedir7(), CONFIG_DIR_NAME);
|
|
11885
|
+
let persistentSection = "";
|
|
11886
|
+
try {
|
|
11887
|
+
const persistentHits = searchPersistentMemories(configDir, query, process.cwd(), 3);
|
|
11888
|
+
if (persistentHits.length > 0) {
|
|
11889
|
+
touchMemoryReferences(configDir, persistentHits.map((h) => h.entry.id));
|
|
11890
|
+
persistentSection = `\u2500\u2500 Persistent memory (${persistentHits.length} hit(s)) \u2500\u2500
|
|
11891
|
+
` + persistentHits.map((h) => `[id ${h.entry.id.slice(0, 8)} \xB7 ${h.entry.scope} \xB7 coverage ${h.score.toFixed(2)}]
|
|
11892
|
+
${h.entry.content}`).join("\n\n") + "\n\n";
|
|
11893
|
+
}
|
|
11894
|
+
} catch {
|
|
11895
|
+
}
|
|
12107
11896
|
const status = loadChatIndex();
|
|
12108
11897
|
if (!status) {
|
|
12109
|
-
return "No chat memory index found. The index is built on REPL startup, or run `/memory rebuild` manually. If you have no past sessions yet, this is expected.";
|
|
11898
|
+
return persistentSection + "No chat memory index found. The index is built on REPL startup, or run `/memory rebuild` manually. If you have no past sessions yet, this is expected.";
|
|
12110
11899
|
}
|
|
12111
11900
|
const hits = await searchChatMemory(query, {
|
|
12112
11901
|
topK,
|
|
@@ -12114,11 +11903,11 @@ var recallMemoryTool = {
|
|
|
12114
11903
|
excludeSessionId: excludeCurrent ? currentId : void 0
|
|
12115
11904
|
});
|
|
12116
11905
|
if (hits.length === 0) {
|
|
12117
|
-
return `No memories matched "${query}" above score ${minScore}. Index has ${status.idx.chunks.length} chunks across ${Object.keys(status.idx.sessionMtimes).length} sessions. Consider lowering minScore to 0.15 or rephrasing the query.`;
|
|
11906
|
+
return persistentSection + `No chat-history memories matched "${query}" above score ${minScore}. Index has ${status.idx.chunks.length} chunks across ${Object.keys(status.idx.sessionMtimes).length} sessions. Consider lowering minScore to 0.15 or rephrasing the query.`;
|
|
12118
11907
|
}
|
|
12119
|
-
const header = `Found ${hits.length}
|
|
11908
|
+
const header = `Found ${hits.length} chat-history hit(s) for "${query}" (min-score ${minScore}):
|
|
12120
11909
|
`;
|
|
12121
|
-
return header + "\n" + hits.map(formatHit).join("\n\n");
|
|
11910
|
+
return persistentSection + header + "\n" + hits.map(formatHit).join("\n\n");
|
|
12122
11911
|
}
|
|
12123
11912
|
};
|
|
12124
11913
|
|
|
@@ -12146,8 +11935,8 @@ function estimateToolDefinitionTokens(def) {
|
|
|
12146
11935
|
|
|
12147
11936
|
// src/tools/registry.ts
|
|
12148
11937
|
import { pathToFileURL } from "url";
|
|
12149
|
-
import { existsSync as
|
|
12150
|
-
import { join as
|
|
11938
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync9 } from "fs";
|
|
11939
|
+
import { join as join15 } from "path";
|
|
12151
11940
|
var ToolRegistry = class {
|
|
12152
11941
|
tools = /* @__PURE__ */ new Map();
|
|
12153
11942
|
pluginToolNames = /* @__PURE__ */ new Set();
|
|
@@ -12308,9 +12097,9 @@ var ToolRegistry = class {
|
|
|
12308
12097
|
* Returns the number of successfully loaded plugins.
|
|
12309
12098
|
*/
|
|
12310
12099
|
async loadPlugins(pluginsDir, allowPlugins = false) {
|
|
12311
|
-
if (!
|
|
12100
|
+
if (!existsSync20(pluginsDir)) {
|
|
12312
12101
|
try {
|
|
12313
|
-
|
|
12102
|
+
mkdirSync9(pluginsDir, { recursive: true });
|
|
12314
12103
|
} catch {
|
|
12315
12104
|
}
|
|
12316
12105
|
return 0;
|
|
@@ -12334,12 +12123,12 @@ var ToolRegistry = class {
|
|
|
12334
12123
|
process.stderr.write(
|
|
12335
12124
|
`
|
|
12336
12125
|
[plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
|
|
12337
|
-
` + files.map((f) => ` + ${
|
|
12126
|
+
` + files.map((f) => ` + ${join15(pluginsDir, f)}`).join("\n") + "\n\n"
|
|
12338
12127
|
);
|
|
12339
12128
|
let loaded = 0;
|
|
12340
12129
|
for (const file of files) {
|
|
12341
12130
|
try {
|
|
12342
|
-
const fileUrl = pathToFileURL(
|
|
12131
|
+
const fileUrl = pathToFileURL(join15(pluginsDir, file)).href;
|
|
12343
12132
|
const mod = await import(fileUrl);
|
|
12344
12133
|
const tool = mod.tool ?? mod.default?.tool ?? mod.default;
|
|
12345
12134
|
if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
|
|
@@ -12501,7 +12290,7 @@ var McpClient = class {
|
|
|
12501
12290
|
// 内部方法:JSON-RPC 通信
|
|
12502
12291
|
// ══════════════════════════════════════════════════════════════════
|
|
12503
12292
|
sendRequest(method, params) {
|
|
12504
|
-
return new Promise((
|
|
12293
|
+
return new Promise((resolve13, reject) => {
|
|
12505
12294
|
if (!this.process?.stdin?.writable) {
|
|
12506
12295
|
return reject(new Error(`MCP server [${this.serverId}] stdin not writable`));
|
|
12507
12296
|
}
|
|
@@ -12525,7 +12314,7 @@ var McpClient = class {
|
|
|
12525
12314
|
this.pendingRequests.set(id, {
|
|
12526
12315
|
resolve: (result) => {
|
|
12527
12316
|
cleanup();
|
|
12528
|
-
|
|
12317
|
+
resolve13(result);
|
|
12529
12318
|
},
|
|
12530
12319
|
reject: (error) => {
|
|
12531
12320
|
cleanup();
|
|
@@ -12602,13 +12391,13 @@ var McpClient = class {
|
|
|
12602
12391
|
}
|
|
12603
12392
|
/** Promise 超时包装 */
|
|
12604
12393
|
withTimeout(promise, ms, label) {
|
|
12605
|
-
return new Promise((
|
|
12394
|
+
return new Promise((resolve13, reject) => {
|
|
12606
12395
|
const timer = setTimeout(() => {
|
|
12607
12396
|
reject(new Error(`MCP [${this.serverId}] ${label} timed out after ${ms}ms`));
|
|
12608
12397
|
}, ms);
|
|
12609
12398
|
promise.then((val) => {
|
|
12610
12399
|
clearTimeout(timer);
|
|
12611
|
-
|
|
12400
|
+
resolve13(val);
|
|
12612
12401
|
}).catch((err) => {
|
|
12613
12402
|
clearTimeout(timer);
|
|
12614
12403
|
reject(err);
|
|
@@ -12885,11 +12674,11 @@ var McpManager = class {
|
|
|
12885
12674
|
};
|
|
12886
12675
|
|
|
12887
12676
|
// src/skills/manager.ts
|
|
12888
|
-
import { existsSync as
|
|
12889
|
-
import { join as
|
|
12677
|
+
import { existsSync as existsSync21, readdirSync as readdirSync10, mkdirSync as mkdirSync10, statSync as statSync8 } from "fs";
|
|
12678
|
+
import { join as join16 } from "path";
|
|
12890
12679
|
|
|
12891
12680
|
// src/skills/types.ts
|
|
12892
|
-
import { readFileSync as
|
|
12681
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
12893
12682
|
import { basename as basename7 } from "path";
|
|
12894
12683
|
function parseSimpleYaml(yaml) {
|
|
12895
12684
|
const result = {};
|
|
@@ -12911,7 +12700,7 @@ function parseYamlArray(value) {
|
|
|
12911
12700
|
function parseSkillFile(filePath) {
|
|
12912
12701
|
let raw;
|
|
12913
12702
|
try {
|
|
12914
|
-
raw =
|
|
12703
|
+
raw = readFileSync15(filePath, "utf-8");
|
|
12915
12704
|
} catch {
|
|
12916
12705
|
return null;
|
|
12917
12706
|
}
|
|
@@ -12959,10 +12748,10 @@ var SkillManager = class {
|
|
|
12959
12748
|
return this.skills.size;
|
|
12960
12749
|
}
|
|
12961
12750
|
loadSkillDir(dir, createIfMissing) {
|
|
12962
|
-
if (!
|
|
12751
|
+
if (!existsSync21(dir)) {
|
|
12963
12752
|
if (createIfMissing) {
|
|
12964
12753
|
try {
|
|
12965
|
-
|
|
12754
|
+
mkdirSync10(dir, { recursive: true });
|
|
12966
12755
|
} catch {
|
|
12967
12756
|
}
|
|
12968
12757
|
}
|
|
@@ -12976,14 +12765,14 @@ var SkillManager = class {
|
|
|
12976
12765
|
}
|
|
12977
12766
|
for (const entry of entries) {
|
|
12978
12767
|
let filePath;
|
|
12979
|
-
const fullPath =
|
|
12768
|
+
const fullPath = join16(dir, entry);
|
|
12980
12769
|
if (entry.endsWith(".md")) {
|
|
12981
12770
|
filePath = fullPath;
|
|
12982
12771
|
} else {
|
|
12983
12772
|
try {
|
|
12984
12773
|
if (statSync8(fullPath).isDirectory()) {
|
|
12985
|
-
const skillMd =
|
|
12986
|
-
if (
|
|
12774
|
+
const skillMd = join16(fullPath, "SKILL.md");
|
|
12775
|
+
if (existsSync21(skillMd)) filePath = skillMd;
|
|
12987
12776
|
else continue;
|
|
12988
12777
|
} else {
|
|
12989
12778
|
continue;
|
|
@@ -13040,9 +12829,9 @@ var SkillManager = class {
|
|
|
13040
12829
|
};
|
|
13041
12830
|
|
|
13042
12831
|
// src/web/tool-executor-web.ts
|
|
13043
|
-
import { randomUUID as
|
|
12832
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
13044
12833
|
import { tmpdir as tmpdir2 } from "os";
|
|
13045
|
-
import { existsSync as
|
|
12834
|
+
import { existsSync as existsSync22, readFileSync as readFileSync16 } from "fs";
|
|
13046
12835
|
var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
13047
12836
|
constructor(registry, ws) {
|
|
13048
12837
|
this.registry = registry;
|
|
@@ -13112,33 +12901,33 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13112
12901
|
}
|
|
13113
12902
|
/** Resolve a pending confirm from client response */
|
|
13114
12903
|
resolveConfirm(requestId, approved) {
|
|
13115
|
-
const
|
|
13116
|
-
if (
|
|
12904
|
+
const resolve13 = this.pendingConfirms.get(requestId);
|
|
12905
|
+
if (resolve13) {
|
|
13117
12906
|
this.clearPendingTimer(requestId);
|
|
13118
12907
|
this.pendingConfirms.delete(requestId);
|
|
13119
12908
|
this.confirming = false;
|
|
13120
|
-
|
|
12909
|
+
resolve13(approved);
|
|
13121
12910
|
}
|
|
13122
12911
|
}
|
|
13123
12912
|
/** Resolve a pending batch confirm from client response */
|
|
13124
12913
|
resolveBatchConfirm(requestId, decision) {
|
|
13125
|
-
const
|
|
13126
|
-
if (
|
|
12914
|
+
const resolve13 = this.pendingBatchConfirms.get(requestId);
|
|
12915
|
+
if (resolve13) {
|
|
13127
12916
|
this.clearPendingTimer(requestId);
|
|
13128
12917
|
this.pendingBatchConfirms.delete(requestId);
|
|
13129
12918
|
this.confirming = false;
|
|
13130
12919
|
if (decision === "all" || decision === "none") {
|
|
13131
|
-
|
|
12920
|
+
resolve13(decision);
|
|
13132
12921
|
} else {
|
|
13133
|
-
|
|
12922
|
+
resolve13(new Set(decision));
|
|
13134
12923
|
}
|
|
13135
12924
|
}
|
|
13136
12925
|
}
|
|
13137
12926
|
/** Cancel all pending confirms (e.g., on disconnect) */
|
|
13138
12927
|
cancelAll() {
|
|
13139
|
-
for (const
|
|
12928
|
+
for (const resolve13 of this.pendingConfirms.values()) resolve13(false);
|
|
13140
12929
|
this.pendingConfirms.clear();
|
|
13141
|
-
for (const
|
|
12930
|
+
for (const resolve13 of this.pendingBatchConfirms.values()) resolve13("none");
|
|
13142
12931
|
this.pendingBatchConfirms.clear();
|
|
13143
12932
|
this.confirming = false;
|
|
13144
12933
|
}
|
|
@@ -13181,9 +12970,9 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13181
12970
|
if (call.name === "write_file") {
|
|
13182
12971
|
const filePath = String(call.arguments["path"] ?? "");
|
|
13183
12972
|
const newContent = String(call.arguments["content"] ?? "");
|
|
13184
|
-
if (filePath &&
|
|
12973
|
+
if (filePath && existsSync22(filePath)) {
|
|
13185
12974
|
try {
|
|
13186
|
-
const old =
|
|
12975
|
+
const old = readFileSync16(filePath, "utf-8");
|
|
13187
12976
|
return renderDiff(old, newContent, { filePath });
|
|
13188
12977
|
} catch {
|
|
13189
12978
|
}
|
|
@@ -13203,7 +12992,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13203
12992
|
}
|
|
13204
12993
|
/** WebSocket-based confirm — sends request, returns Promise resolved by client */
|
|
13205
12994
|
confirm(call, level) {
|
|
13206
|
-
const requestId =
|
|
12995
|
+
const requestId = randomUUID2();
|
|
13207
12996
|
this.confirming = true;
|
|
13208
12997
|
const msg = {
|
|
13209
12998
|
type: "confirm_request",
|
|
@@ -13214,8 +13003,8 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13214
13003
|
diff: this.getDiffPreview(call)
|
|
13215
13004
|
};
|
|
13216
13005
|
this.send(msg);
|
|
13217
|
-
return new Promise((
|
|
13218
|
-
this.pendingConfirms.set(requestId,
|
|
13006
|
+
return new Promise((resolve13) => {
|
|
13007
|
+
this.pendingConfirms.set(requestId, resolve13);
|
|
13219
13008
|
this.pendingTimers.set(requestId, setTimeout(() => {
|
|
13220
13009
|
if (this.pendingConfirms.has(requestId)) {
|
|
13221
13010
|
this.resolveConfirm(requestId, false);
|
|
@@ -13225,7 +13014,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13225
13014
|
}
|
|
13226
13015
|
/** WebSocket-based batch confirm */
|
|
13227
13016
|
batchConfirm(calls) {
|
|
13228
|
-
const requestId =
|
|
13017
|
+
const requestId = randomUUID2();
|
|
13229
13018
|
this.confirming = true;
|
|
13230
13019
|
const files = calls.map((call, i) => ({
|
|
13231
13020
|
index: i + 1,
|
|
@@ -13239,8 +13028,8 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13239
13028
|
files
|
|
13240
13029
|
};
|
|
13241
13030
|
this.send(msg);
|
|
13242
|
-
return new Promise((
|
|
13243
|
-
this.pendingBatchConfirms.set(requestId,
|
|
13031
|
+
return new Promise((resolve13) => {
|
|
13032
|
+
this.pendingBatchConfirms.set(requestId, resolve13);
|
|
13244
13033
|
this.pendingTimers.set(requestId, setTimeout(() => {
|
|
13245
13034
|
if (this.pendingBatchConfirms.has(requestId)) {
|
|
13246
13035
|
this.resolveBatchConfirm(requestId, "none");
|
|
@@ -13487,16 +13276,16 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
13487
13276
|
};
|
|
13488
13277
|
|
|
13489
13278
|
// src/repl/dev-state.ts
|
|
13490
|
-
import { existsSync as
|
|
13491
|
-
import { join as
|
|
13492
|
-
import { homedir as
|
|
13279
|
+
import { existsSync as existsSync23, readFileSync as readFileSync17, unlinkSync as unlinkSync5, mkdirSync as mkdirSync11 } from "fs";
|
|
13280
|
+
import { join as join17 } from "path";
|
|
13281
|
+
import { homedir as homedir8 } from "os";
|
|
13493
13282
|
function getDevStatePath() {
|
|
13494
|
-
return
|
|
13283
|
+
return join17(homedir8(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
|
|
13495
13284
|
}
|
|
13496
13285
|
function loadDevState() {
|
|
13497
13286
|
const path3 = getDevStatePath();
|
|
13498
|
-
if (!
|
|
13499
|
-
const content =
|
|
13287
|
+
if (!existsSync23(path3)) return null;
|
|
13288
|
+
const content = readFileSync17(path3, "utf-8").trim();
|
|
13500
13289
|
return content || null;
|
|
13501
13290
|
}
|
|
13502
13291
|
|
|
@@ -13521,7 +13310,7 @@ Working directory: ${process.cwd()}`;
|
|
|
13521
13310
|
}
|
|
13522
13311
|
stableParts.push(AGENTIC_BEHAVIOR_GUIDELINE);
|
|
13523
13312
|
if (ctx.configDir) {
|
|
13524
|
-
const memory = loadMemoryContent(ctx.configDir);
|
|
13313
|
+
const memory = loadMemoryContent(ctx.configDir, ctx.memoryMaxChars);
|
|
13525
13314
|
if (memory) stableParts.push(`# Persistent Memory
|
|
13526
13315
|
|
|
13527
13316
|
${memory.content}`);
|
|
@@ -13553,8 +13342,8 @@ ${ctx.activeSkill.content}`);
|
|
|
13553
13342
|
if (ctx.planMode) stableParts.push(PLAN_MODE_SYSTEM_ADDON);
|
|
13554
13343
|
return { stable: stableParts.join("\n\n---\n\n"), volatile };
|
|
13555
13344
|
}
|
|
13556
|
-
function loadMemoryContent(configDir) {
|
|
13557
|
-
return formatMemoryForPrompt(configDir, process.cwd());
|
|
13345
|
+
function loadMemoryContent(configDir, maxChars) {
|
|
13346
|
+
return formatMemoryForPrompt(configDir, process.cwd(), maxChars);
|
|
13558
13347
|
}
|
|
13559
13348
|
|
|
13560
13349
|
// src/core/pricing.ts
|
|
@@ -13753,8 +13542,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
13753
13542
|
}
|
|
13754
13543
|
|
|
13755
13544
|
// src/core/context-files.ts
|
|
13756
|
-
import { existsSync as
|
|
13757
|
-
import { join as
|
|
13545
|
+
import { existsSync as existsSync24, readFileSync as readFileSync18 } from "fs";
|
|
13546
|
+
import { join as join18, relative as relative4, resolve as resolve8 } from "path";
|
|
13758
13547
|
function uniqueNonEmpty(values) {
|
|
13759
13548
|
const seen = /* @__PURE__ */ new Set();
|
|
13760
13549
|
const out = [];
|
|
@@ -13777,14 +13566,14 @@ function displayPath(filePath, cwd) {
|
|
|
13777
13566
|
return filePath;
|
|
13778
13567
|
}
|
|
13779
13568
|
function isInsideOrEqual(parent, child) {
|
|
13780
|
-
const rel = relative4(
|
|
13569
|
+
const rel = relative4(resolve8(parent), resolve8(child));
|
|
13781
13570
|
return rel === "" || !!rel && !rel.startsWith("..") && !rel.startsWith("/") && !rel.startsWith("\\");
|
|
13782
13571
|
}
|
|
13783
13572
|
function readContextFile(level, filePath, cwd, maxBytes) {
|
|
13784
13573
|
const name = filePath.split(/[\\/]/).pop() ?? filePath;
|
|
13785
13574
|
const shown = displayPath(filePath, cwd);
|
|
13786
13575
|
try {
|
|
13787
|
-
const raw =
|
|
13576
|
+
const raw = readFileSync18(filePath);
|
|
13788
13577
|
const byteCount = raw.byteLength;
|
|
13789
13578
|
if (byteCount === 0) {
|
|
13790
13579
|
return {
|
|
@@ -13831,8 +13620,8 @@ function readContextFile(level, filePath, cwd, maxBytes) {
|
|
|
13831
13620
|
function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
13832
13621
|
const skipped = [];
|
|
13833
13622
|
for (const candidate of candidates) {
|
|
13834
|
-
const filePath =
|
|
13835
|
-
if (!
|
|
13623
|
+
const filePath = join18(dir, candidate);
|
|
13624
|
+
if (!existsSync24(filePath)) continue;
|
|
13836
13625
|
const result = readContextFile(level, filePath, cwd, maxBytes);
|
|
13837
13626
|
if (result.skipped) skipped.push(result.skipped);
|
|
13838
13627
|
if (result.layer) return { layer: result.layer, skipped };
|
|
@@ -13840,7 +13629,7 @@ function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
|
|
|
13840
13629
|
return { layer: null, skipped };
|
|
13841
13630
|
}
|
|
13842
13631
|
function loadContextFiles(options) {
|
|
13843
|
-
const cwd =
|
|
13632
|
+
const cwd = resolve8(options.cwd);
|
|
13844
13633
|
const maxBytes = options.maxBytes ?? CONTEXT_FILE_MAX_BYTES;
|
|
13845
13634
|
const candidates = options.candidates ?? buildContextFileCandidates(options.fallbackFilenames);
|
|
13846
13635
|
const skipped = [];
|
|
@@ -13848,7 +13637,7 @@ function loadContextFiles(options) {
|
|
|
13848
13637
|
return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
|
|
13849
13638
|
}
|
|
13850
13639
|
if (options.setting && options.setting !== "auto") {
|
|
13851
|
-
const filePath =
|
|
13640
|
+
const filePath = resolve8(cwd, String(options.setting));
|
|
13852
13641
|
if (!isInsideOrEqual(cwd, filePath)) {
|
|
13853
13642
|
skipped.push({
|
|
13854
13643
|
level: "single",
|
|
@@ -13859,7 +13648,7 @@ function loadContextFiles(options) {
|
|
|
13859
13648
|
});
|
|
13860
13649
|
return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
|
|
13861
13650
|
}
|
|
13862
|
-
if (!
|
|
13651
|
+
if (!existsSync24(filePath)) {
|
|
13863
13652
|
skipped.push({
|
|
13864
13653
|
level: "single",
|
|
13865
13654
|
filePath,
|
|
@@ -13892,7 +13681,7 @@ function loadContextFiles(options) {
|
|
|
13892
13681
|
skipped.push(...result.skipped);
|
|
13893
13682
|
if (result.layer) layers.push(result.layer);
|
|
13894
13683
|
}
|
|
13895
|
-
if (
|
|
13684
|
+
if (resolve8(options.cwd) !== resolve8(options.projectRoot)) {
|
|
13896
13685
|
const result = findFirstContextFile("local", options.cwd, cwd, candidates, maxBytes);
|
|
13897
13686
|
skipped.push(...result.skipped);
|
|
13898
13687
|
if (result.layer) layers.push(result.layer);
|
|
@@ -13910,12 +13699,12 @@ function loadContextFiles(options) {
|
|
|
13910
13699
|
}
|
|
13911
13700
|
|
|
13912
13701
|
// src/web/session-handler.ts
|
|
13913
|
-
import { mkdirSync as
|
|
13914
|
-
import {
|
|
13702
|
+
import { mkdirSync as mkdirSync14, createWriteStream as createWriteStream2 } from "fs";
|
|
13703
|
+
import { dirname as dirname9 } from "path";
|
|
13915
13704
|
|
|
13916
13705
|
// src/repl/commands/project-init.ts
|
|
13917
|
-
import { existsSync as
|
|
13918
|
-
import { join as
|
|
13706
|
+
import { existsSync as existsSync25, readFileSync as readFileSync19, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
|
|
13707
|
+
import { join as join19 } from "path";
|
|
13919
13708
|
var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
13920
13709
|
"node_modules",
|
|
13921
13710
|
".git",
|
|
@@ -13959,11 +13748,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13959
13748
|
const sorted = filtered.sort((a, b) => {
|
|
13960
13749
|
let aIsDir = false, bIsDir = false;
|
|
13961
13750
|
try {
|
|
13962
|
-
aIsDir = statSync9(
|
|
13751
|
+
aIsDir = statSync9(join19(d, a)).isDirectory();
|
|
13963
13752
|
} catch {
|
|
13964
13753
|
}
|
|
13965
13754
|
try {
|
|
13966
|
-
bIsDir = statSync9(
|
|
13755
|
+
bIsDir = statSync9(join19(d, b)).isDirectory();
|
|
13967
13756
|
} catch {
|
|
13968
13757
|
}
|
|
13969
13758
|
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
|
|
@@ -13971,7 +13760,7 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
|
|
|
13971
13760
|
});
|
|
13972
13761
|
for (let i = 0; i < sorted.length && count < maxEntries; i++) {
|
|
13973
13762
|
const name = sorted[i];
|
|
13974
|
-
const fullPath =
|
|
13763
|
+
const fullPath = join19(d, name);
|
|
13975
13764
|
const isLast = i === sorted.length - 1;
|
|
13976
13765
|
const connector = isLast ? "+-- " : "|-- ";
|
|
13977
13766
|
let isDir;
|
|
@@ -13998,7 +13787,7 @@ function scanProject(cwd) {
|
|
|
13998
13787
|
configFiles: [],
|
|
13999
13788
|
directoryStructure: ""
|
|
14000
13789
|
};
|
|
14001
|
-
const check = (file) =>
|
|
13790
|
+
const check = (file) => existsSync25(join19(cwd, file));
|
|
14002
13791
|
const configCandidates = [
|
|
14003
13792
|
"package.json",
|
|
14004
13793
|
"tsconfig.json",
|
|
@@ -14025,7 +13814,7 @@ function scanProject(cwd) {
|
|
|
14025
13814
|
info.type = "node";
|
|
14026
13815
|
info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
|
|
14027
13816
|
try {
|
|
14028
|
-
const pkg = JSON.parse(
|
|
13817
|
+
const pkg = JSON.parse(readFileSync19(join19(cwd, "package.json"), "utf-8"));
|
|
14029
13818
|
const scripts = pkg.scripts ?? {};
|
|
14030
13819
|
info.buildCommand = scripts.build ? "npm run build" : void 0;
|
|
14031
13820
|
info.testCommand = scripts.test ? "npm test" : void 0;
|
|
@@ -14674,7 +14463,7 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
|
|
|
14674
14463
|
}
|
|
14675
14464
|
|
|
14676
14465
|
// src/hub/persist.ts
|
|
14677
|
-
import { join as
|
|
14466
|
+
import { join as join20 } from "path";
|
|
14678
14467
|
function discussionToMessages(state2) {
|
|
14679
14468
|
const out = [];
|
|
14680
14469
|
const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
|
|
@@ -14712,7 +14501,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
|
|
|
14712
14501
|
session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
|
|
14713
14502
|
session.titleAiGenerated = true;
|
|
14714
14503
|
await sm.save();
|
|
14715
|
-
return { id: session.id, path:
|
|
14504
|
+
return { id: session.id, path: join20(config.getHistoryDir(), `${session.id}.json`) };
|
|
14716
14505
|
}
|
|
14717
14506
|
|
|
14718
14507
|
// src/web/commands/provider-commands.ts
|
|
@@ -14800,8 +14589,8 @@ async function handlePlan(args, ctx) {
|
|
|
14800
14589
|
}
|
|
14801
14590
|
|
|
14802
14591
|
// src/web/commands/_shared.ts
|
|
14803
|
-
import { existsSync as
|
|
14804
|
-
import { join as
|
|
14592
|
+
import { existsSync as existsSync28, readFileSync as readFileSync21, writeFileSync as writeFileSync3, mkdirSync as mkdirSync13, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
|
|
14593
|
+
import { join as join23, resolve as resolve9, dirname as dirname8 } from "path";
|
|
14805
14594
|
|
|
14806
14595
|
// src/cli/review-prompts.ts
|
|
14807
14596
|
function buildReviewPrompt(diff, gitContextStr, detailed) {
|
|
@@ -14873,34 +14662,34 @@ function readGitDiff(options = {}) {
|
|
|
14873
14662
|
}
|
|
14874
14663
|
|
|
14875
14664
|
// src/diagnostics/doctor-report.ts
|
|
14876
|
-
import { existsSync as
|
|
14877
|
-
import { join as
|
|
14665
|
+
import { existsSync as existsSync27, statSync as statSync11 } from "fs";
|
|
14666
|
+
import { join as join22 } from "path";
|
|
14878
14667
|
import { arch as arch2, platform as platform7, release as release2 } from "os";
|
|
14879
14668
|
|
|
14880
14669
|
// src/diagnostics/crash-log.ts
|
|
14881
14670
|
import {
|
|
14882
|
-
existsSync as
|
|
14883
|
-
mkdirSync as
|
|
14671
|
+
existsSync as existsSync26,
|
|
14672
|
+
mkdirSync as mkdirSync12,
|
|
14884
14673
|
readdirSync as readdirSync12,
|
|
14885
|
-
readFileSync as
|
|
14674
|
+
readFileSync as readFileSync20,
|
|
14886
14675
|
statSync as statSync10,
|
|
14887
14676
|
unlinkSync as unlinkSync6,
|
|
14888
14677
|
writeFileSync as writeFileSync2
|
|
14889
14678
|
} from "fs";
|
|
14890
|
-
import { join as
|
|
14891
|
-
import { homedir as
|
|
14679
|
+
import { join as join21 } from "path";
|
|
14680
|
+
import { homedir as homedir9, platform as platform6, release, arch } from "os";
|
|
14892
14681
|
var LOGS_DIR_NAME = "logs";
|
|
14893
14682
|
var CRASH_LOG_PREFIX = "crash-";
|
|
14894
14683
|
function getLogsDir(configDir) {
|
|
14895
|
-
const base = configDir ??
|
|
14896
|
-
return
|
|
14684
|
+
const base = configDir ?? join21(homedir9(), CONFIG_DIR_NAME);
|
|
14685
|
+
return join21(base, LOGS_DIR_NAME);
|
|
14897
14686
|
}
|
|
14898
14687
|
function truncate(s, n) {
|
|
14899
14688
|
return s.length <= n ? s : s.slice(0, n) + "\u2026";
|
|
14900
14689
|
}
|
|
14901
14690
|
function listRecentCrashes(limit = 10, configDir) {
|
|
14902
14691
|
const dir = getLogsDir(configDir);
|
|
14903
|
-
if (!
|
|
14692
|
+
if (!existsSync26(dir)) return [];
|
|
14904
14693
|
const files = [];
|
|
14905
14694
|
let names;
|
|
14906
14695
|
try {
|
|
@@ -14910,10 +14699,10 @@ function listRecentCrashes(limit = 10, configDir) {
|
|
|
14910
14699
|
}
|
|
14911
14700
|
for (const name of names) {
|
|
14912
14701
|
if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
|
|
14913
|
-
const full =
|
|
14702
|
+
const full = join21(dir, name);
|
|
14914
14703
|
try {
|
|
14915
14704
|
const st = statSync10(full);
|
|
14916
|
-
const head =
|
|
14705
|
+
const head = readFileSync20(full, "utf-8").split("\n").slice(0, 20);
|
|
14917
14706
|
const tsLine = head.find((l) => l.startsWith("timestamp:"));
|
|
14918
14707
|
const kindLine = head.find((l) => l.startsWith("kind:"));
|
|
14919
14708
|
const msgIdx = head.findIndex((l) => l.trim() === "## message");
|
|
@@ -14932,8 +14721,8 @@ function listRecentCrashes(limit = 10, configDir) {
|
|
|
14932
14721
|
return files.slice(0, limit);
|
|
14933
14722
|
}
|
|
14934
14723
|
function getConfigDirUsage(configDir) {
|
|
14935
|
-
const base = configDir ??
|
|
14936
|
-
if (!
|
|
14724
|
+
const base = configDir ?? join21(homedir9(), CONFIG_DIR_NAME);
|
|
14725
|
+
if (!existsSync26(base)) return { totalBytes: 0, entries: [] };
|
|
14937
14726
|
const entries = [];
|
|
14938
14727
|
let total = 0;
|
|
14939
14728
|
let names;
|
|
@@ -14943,7 +14732,7 @@ function getConfigDirUsage(configDir) {
|
|
|
14943
14732
|
return { totalBytes: 0, entries: [] };
|
|
14944
14733
|
}
|
|
14945
14734
|
for (const name of names) {
|
|
14946
|
-
const full =
|
|
14735
|
+
const full = join21(base, name);
|
|
14947
14736
|
const bytes = dirSize(full);
|
|
14948
14737
|
entries.push({ name, bytes });
|
|
14949
14738
|
total += bytes;
|
|
@@ -14958,7 +14747,7 @@ function dirSize(path3) {
|
|
|
14958
14747
|
if (!st.isDirectory()) return 0;
|
|
14959
14748
|
let sum = 0;
|
|
14960
14749
|
for (const name of readdirSync12(path3)) {
|
|
14961
|
-
sum += dirSize(
|
|
14750
|
+
sum += dirSize(join21(path3, name));
|
|
14962
14751
|
}
|
|
14963
14752
|
return sum;
|
|
14964
14753
|
} catch {
|
|
@@ -14968,7 +14757,7 @@ function dirSize(path3) {
|
|
|
14968
14757
|
|
|
14969
14758
|
// src/diagnostics/doctor-report.ts
|
|
14970
14759
|
function checkFile(label, path3) {
|
|
14971
|
-
const exists =
|
|
14760
|
+
const exists = existsSync27(path3);
|
|
14972
14761
|
let sizeBytes = null;
|
|
14973
14762
|
if (exists) {
|
|
14974
14763
|
try {
|
|
@@ -15011,12 +14800,12 @@ function buildDoctorReport(options) {
|
|
|
15011
14800
|
configDir,
|
|
15012
14801
|
providers: options.providers.listAll().map((p) => ({ id: p.id, displayName: p.displayName, configured: p.configured })),
|
|
15013
14802
|
files: [
|
|
15014
|
-
checkFile("config.json",
|
|
15015
|
-
checkFile("memory.md",
|
|
15016
|
-
checkFile("memory.jsonl",
|
|
15017
|
-
checkFile("dev-state.md",
|
|
15018
|
-
checkFile("hooks-trust.json",
|
|
15019
|
-
checkFile("plugin-state.json",
|
|
14803
|
+
checkFile("config.json", join22(configDir, "config.json")),
|
|
14804
|
+
checkFile("memory.md", join22(configDir, MEMORY_FILE_NAME)),
|
|
14805
|
+
checkFile("memory.jsonl", join22(configDir, "memory.jsonl")),
|
|
14806
|
+
checkFile("dev-state.md", join22(configDir, DEV_STATE_FILE_NAME)),
|
|
14807
|
+
checkFile("hooks-trust.json", join22(configDir, "hooks-trust.json")),
|
|
14808
|
+
checkFile("plugin-state.json", join22(configDir, "plugins", "plugin-state.json"))
|
|
15020
14809
|
],
|
|
15021
14810
|
context: {
|
|
15022
14811
|
enabled: contextSetting !== false,
|
|
@@ -15051,7 +14840,7 @@ function buildDoctorReport(options) {
|
|
|
15051
14840
|
disabled: hooks.filter((h) => h.disabled).length
|
|
15052
14841
|
},
|
|
15053
14842
|
plugins: {
|
|
15054
|
-
root:
|
|
14843
|
+
root: join22(configDir, "plugins"),
|
|
15055
14844
|
installed: plugins.length,
|
|
15056
14845
|
active: activeAssets.plugins.length,
|
|
15057
14846
|
invalid: plugins.filter((p) => !p.valid).length,
|
|
@@ -15066,7 +14855,7 @@ function buildDoctorReport(options) {
|
|
|
15066
14855
|
mcp: {
|
|
15067
14856
|
enabled: config.get("mcpEnabled"),
|
|
15068
14857
|
configured: Object.keys(mcpServers).length,
|
|
15069
|
-
projectConfigExists:
|
|
14858
|
+
projectConfigExists: existsSync27(join22(projectRoot, MCP_PROJECT_CONFIG_NAME)),
|
|
15070
14859
|
statuses: options.mcpStatuses ?? []
|
|
15071
14860
|
},
|
|
15072
14861
|
recentCrashes: listRecentCrashes(5, configDir),
|
|
@@ -15292,12 +15081,20 @@ async function handleStatus(_args, ctx) {
|
|
|
15292
15081
|
Cache: write=${cacheCreate} read=${cacheRead}` : "";
|
|
15293
15082
|
const costLine = cost != null ? `
|
|
15294
15083
|
Cost: ${formatCost(cost)}` : "";
|
|
15084
|
+
let memoryLine = "";
|
|
15085
|
+
try {
|
|
15086
|
+
const { countPendingMemories } = await import("./persistent-memory-ZCQKLIC5.js");
|
|
15087
|
+
const pending = countPendingMemories(ctx.config.getConfigDir());
|
|
15088
|
+
if (pending > 0) memoryLine = `
|
|
15089
|
+
Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
|
|
15090
|
+
} catch {
|
|
15091
|
+
}
|
|
15295
15092
|
ctx.send({
|
|
15296
15093
|
type: "info",
|
|
15297
15094
|
message: `Provider: ${ctx.currentProvider}
|
|
15298
15095
|
Model: ${ctx.currentModel}
|
|
15299
15096
|
Session: ${session?.id ?? "none"} (${session?.messages.length ?? 0} messages)
|
|
15300
|
-
Tokens: in=${ctx.sessionTokenUsage.inputTokens} out=${ctx.sessionTokenUsage.outputTokens}${cacheLine}${costLine}`
|
|
15097
|
+
Tokens: in=${ctx.sessionTokenUsage.inputTokens} out=${ctx.sessionTokenUsage.outputTokens}${cacheLine}${costLine}${memoryLine}`
|
|
15301
15098
|
});
|
|
15302
15099
|
}
|
|
15303
15100
|
async function handleSession(args, ctx) {
|
|
@@ -15593,7 +15390,7 @@ async function handleBranch(args, ctx) {
|
|
|
15593
15390
|
return;
|
|
15594
15391
|
}
|
|
15595
15392
|
const sub = args[0]?.toLowerCase();
|
|
15596
|
-
const
|
|
15393
|
+
const resolve13 = (ref) => {
|
|
15597
15394
|
const r = session.resolveBranchRef(ref);
|
|
15598
15395
|
if (r.ok) return r.id;
|
|
15599
15396
|
if (r.reason === "ambiguous") {
|
|
@@ -15647,7 +15444,7 @@ async function handleBranch(args, ctx) {
|
|
|
15647
15444
|
ctx.send({ type: "error", message: "Usage: /branch switch <id|title>" });
|
|
15648
15445
|
return;
|
|
15649
15446
|
}
|
|
15650
|
-
const id =
|
|
15447
|
+
const id = resolve13(ref);
|
|
15651
15448
|
if (!id) return;
|
|
15652
15449
|
const ok = session.switchBranch(id);
|
|
15653
15450
|
if (ok) {
|
|
@@ -15667,7 +15464,7 @@ async function handleBranch(args, ctx) {
|
|
|
15667
15464
|
ctx.send({ type: "error", message: "Usage: /branch delete <id|title>" });
|
|
15668
15465
|
return;
|
|
15669
15466
|
}
|
|
15670
|
-
const id =
|
|
15467
|
+
const id = resolve13(ref);
|
|
15671
15468
|
if (!id) return;
|
|
15672
15469
|
const ok = session.deleteBranch(id);
|
|
15673
15470
|
if (ok) {
|
|
@@ -15686,7 +15483,7 @@ async function handleBranch(args, ctx) {
|
|
|
15686
15483
|
ctx.send({ type: "error", message: "Usage: /branch rename <id|title> <new title>" });
|
|
15687
15484
|
return;
|
|
15688
15485
|
}
|
|
15689
|
-
const id =
|
|
15486
|
+
const id = resolve13(ref);
|
|
15690
15487
|
if (!id) return;
|
|
15691
15488
|
const ok = session.renameBranch(id, title);
|
|
15692
15489
|
if (ok) {
|
|
@@ -15704,7 +15501,7 @@ async function handleBranch(args, ctx) {
|
|
|
15704
15501
|
ctx.send({ type: "error", message: "Usage: /branch diff <id|title>" });
|
|
15705
15502
|
return;
|
|
15706
15503
|
}
|
|
15707
|
-
const id =
|
|
15504
|
+
const id = resolve13(ref);
|
|
15708
15505
|
if (!id) return;
|
|
15709
15506
|
const d = session.diffBranches(id);
|
|
15710
15507
|
if (!d) {
|
|
@@ -15746,7 +15543,7 @@ async function handleBranch(args, ctx) {
|
|
|
15746
15543
|
ctx.send({ type: "error", message: "Usage: /branch cherry-pick <source-id|title> <msg-index>" });
|
|
15747
15544
|
return;
|
|
15748
15545
|
}
|
|
15749
|
-
const id =
|
|
15546
|
+
const id = resolve13(ref);
|
|
15750
15547
|
if (!id) return;
|
|
15751
15548
|
const idx = parseInt(idxStr, 10);
|
|
15752
15549
|
if (Number.isNaN(idx)) {
|
|
@@ -15870,7 +15667,7 @@ async function handlePlugin(args, ctx) {
|
|
|
15870
15667
|
lines.push("");
|
|
15871
15668
|
}
|
|
15872
15669
|
if (pluginTools.length > 0) {
|
|
15873
|
-
const legacyDir =
|
|
15670
|
+
const legacyDir = join23(configDir, PLUGINS_DIR_NAME);
|
|
15874
15671
|
lines.push(`**Legacy JS tools** (${pluginTools.length}):`);
|
|
15875
15672
|
lines.push(`Dir: ${legacyDir}`);
|
|
15876
15673
|
for (const t of pluginTools) lines.push(` \u2022 ${t.definition.name} \u2014 ${t.definition.description}`);
|
|
@@ -15994,8 +15791,8 @@ ${activated.meta.description || ""}` });
|
|
|
15994
15791
|
}
|
|
15995
15792
|
async function handleCommands(_args, ctx) {
|
|
15996
15793
|
const configDir = ctx.config.getConfigDir();
|
|
15997
|
-
const commandsDir =
|
|
15998
|
-
if (!
|
|
15794
|
+
const commandsDir = join23(configDir, CUSTOM_COMMANDS_DIR_NAME);
|
|
15795
|
+
if (!existsSync28(commandsDir)) {
|
|
15999
15796
|
ctx.send({ type: "info", message: `No custom commands directory.
|
|
16000
15797
|
Create: ${commandsDir}/ with .md files.` });
|
|
16001
15798
|
return;
|
|
@@ -16095,7 +15892,7 @@ async function handleSecurityReview(args, ctx) {
|
|
|
16095
15892
|
async function handleTest(args, ctx) {
|
|
16096
15893
|
ctx.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
16097
15894
|
try {
|
|
16098
|
-
const { executeTests } = await import("./run-tests-
|
|
15895
|
+
const { executeTests } = await import("./run-tests-WCZBWQFS.js");
|
|
16099
15896
|
const argStr = args.join(" ").trim();
|
|
16100
15897
|
let testArgs = {};
|
|
16101
15898
|
if (argStr) {
|
|
@@ -16125,9 +15922,9 @@ async function handleDiff(args, ctx) {
|
|
|
16125
15922
|
let modifiedFiles = 0;
|
|
16126
15923
|
const diffLines2 = [];
|
|
16127
15924
|
for (const [filePath, { earliest }] of fileMap) {
|
|
16128
|
-
const currentContent =
|
|
15925
|
+
const currentContent = existsSync28(filePath) ? (() => {
|
|
16129
15926
|
try {
|
|
16130
|
-
return
|
|
15927
|
+
return readFileSync21(filePath, "utf-8");
|
|
16131
15928
|
} catch {
|
|
16132
15929
|
return null;
|
|
16133
15930
|
}
|
|
@@ -16162,9 +15959,9 @@ async function handleDiff(args, ctx) {
|
|
|
16162
15959
|
}
|
|
16163
15960
|
async function handleInit(args, ctx) {
|
|
16164
15961
|
const cwd = process.cwd();
|
|
16165
|
-
const targetPath =
|
|
15962
|
+
const targetPath = join23(cwd, "AICLI.md");
|
|
16166
15963
|
const force = args.includes("--force");
|
|
16167
|
-
if (
|
|
15964
|
+
if (existsSync28(targetPath) && !force) {
|
|
16168
15965
|
ctx.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
|
|
16169
15966
|
Use /init --force to overwrite.` });
|
|
16170
15967
|
return;
|
|
@@ -16326,9 +16123,58 @@ async function handleIndex(args, ctx) {
|
|
|
16326
16123
|
async function handleMemory(args, ctx) {
|
|
16327
16124
|
const sub = args[0];
|
|
16328
16125
|
if (sub === "add" && args.length > 1) {
|
|
16329
|
-
|
|
16126
|
+
const rest = args.slice(1);
|
|
16127
|
+
const opts = {};
|
|
16128
|
+
const text = [];
|
|
16129
|
+
for (let i = 0; i < rest.length; i++) {
|
|
16130
|
+
if (rest[i] === "--scope" && rest[i + 1]) {
|
|
16131
|
+
opts.scope = rest[++i];
|
|
16132
|
+
} else if (rest[i] === "--sensitivity" && rest[i + 1]) {
|
|
16133
|
+
opts.sensitivity = rest[++i];
|
|
16134
|
+
} else if (rest[i] === "--expires-at" && rest[i + 1]) {
|
|
16135
|
+
opts.expiresAt = rest[++i];
|
|
16136
|
+
} else text.push(rest[i]);
|
|
16137
|
+
}
|
|
16138
|
+
if (text.length === 0) {
|
|
16139
|
+
ctx.send({ type: "info", message: "Usage: /memory add [--scope personal|project] [--sensitivity low|medium|high] [--expires-at ISO] <text>" });
|
|
16140
|
+
return;
|
|
16141
|
+
}
|
|
16142
|
+
ctx.memoryAdd(text.join(" "), opts);
|
|
16143
|
+
} else if (sub === "edit") {
|
|
16144
|
+
const id = args[1];
|
|
16145
|
+
const text = args.slice(2).join(" ").trim();
|
|
16146
|
+
if (!id || !text) {
|
|
16147
|
+
ctx.send({ type: "info", message: "Usage: /memory edit <id> <new text>" });
|
|
16148
|
+
return;
|
|
16149
|
+
}
|
|
16150
|
+
ctx.memoryEdit(id, text);
|
|
16151
|
+
} else if (sub === "approve" || sub === "reject" || sub === "delete" || sub === "expire") {
|
|
16152
|
+
if (!args[1]) {
|
|
16153
|
+
ctx.send({ type: "info", message: `Usage: /memory ${sub} <id>` });
|
|
16154
|
+
return;
|
|
16155
|
+
}
|
|
16156
|
+
ctx.handleMemoryManage(sub, args[1], sub === "expire" ? args[2] : void 0);
|
|
16157
|
+
} else if (sub === "export") {
|
|
16158
|
+
if (args[1] === "md") {
|
|
16159
|
+
const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-ZCQKLIC5.js");
|
|
16160
|
+
ctx.send({
|
|
16161
|
+
type: "export_data",
|
|
16162
|
+
format: "md",
|
|
16163
|
+
filename: `aicli-memory-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.md`,
|
|
16164
|
+
content: exportMemoryEntries2(ctx.config.getConfigDir(), process.cwd(), "md")
|
|
16165
|
+
});
|
|
16166
|
+
} else {
|
|
16167
|
+
ctx.handleMemoryManage("export");
|
|
16168
|
+
}
|
|
16330
16169
|
} else if (sub === "clear") {
|
|
16331
|
-
|
|
16170
|
+
if (args[1] === "confirm") {
|
|
16171
|
+
ctx.memoryClear();
|
|
16172
|
+
} else {
|
|
16173
|
+
ctx.send({
|
|
16174
|
+
type: "info",
|
|
16175
|
+
message: "\u26A0 This permanently clears ALL persistent memory (a timestamped .bak backup is kept next to memory.jsonl).\nRun `/memory clear confirm` to proceed, or manage individual entries in the Memory panel."
|
|
16176
|
+
});
|
|
16177
|
+
}
|
|
16332
16178
|
} else {
|
|
16333
16179
|
ctx.memoryShow();
|
|
16334
16180
|
}
|
|
@@ -16347,8 +16193,8 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
|
|
|
16347
16193
|
ctx.send({ type: "info", message: "\u2713 All added directories removed from context." });
|
|
16348
16194
|
return;
|
|
16349
16195
|
}
|
|
16350
|
-
const dirPath =
|
|
16351
|
-
if (!
|
|
16196
|
+
const dirPath = resolve9(sub);
|
|
16197
|
+
if (!existsSync28(dirPath)) {
|
|
16352
16198
|
ctx.send({ type: "error", message: `Directory not found: ${dirPath}` });
|
|
16353
16199
|
return;
|
|
16354
16200
|
}
|
|
@@ -16913,10 +16759,10 @@ var SessionHandler = class {
|
|
|
16913
16759
|
return;
|
|
16914
16760
|
}
|
|
16915
16761
|
case "ask_user_response": {
|
|
16916
|
-
const
|
|
16917
|
-
if (
|
|
16762
|
+
const resolve13 = this.pendingAskUser.get(msg.requestId);
|
|
16763
|
+
if (resolve13) {
|
|
16918
16764
|
this.pendingAskUser.delete(msg.requestId);
|
|
16919
|
-
|
|
16765
|
+
resolve13(msg.answer);
|
|
16920
16766
|
}
|
|
16921
16767
|
return;
|
|
16922
16768
|
}
|
|
@@ -16929,10 +16775,10 @@ var SessionHandler = class {
|
|
|
16929
16775
|
case "memory_manage":
|
|
16930
16776
|
return this.handleMemoryManage(msg.action, msg.id, msg.expiresAt);
|
|
16931
16777
|
case "auto_pause_response": {
|
|
16932
|
-
const
|
|
16933
|
-
if (
|
|
16778
|
+
const resolve13 = this.pendingAutoPause.get(msg.requestId);
|
|
16779
|
+
if (resolve13) {
|
|
16934
16780
|
this.pendingAutoPause.delete(msg.requestId);
|
|
16935
|
-
|
|
16781
|
+
resolve13({ action: msg.action, message: msg.message });
|
|
16936
16782
|
}
|
|
16937
16783
|
return;
|
|
16938
16784
|
}
|
|
@@ -16947,10 +16793,10 @@ var SessionHandler = class {
|
|
|
16947
16793
|
this.hubOrchestrator?.abort();
|
|
16948
16794
|
return;
|
|
16949
16795
|
case "hub_steer": {
|
|
16950
|
-
const
|
|
16951
|
-
if (
|
|
16796
|
+
const resolve13 = this.pendingHubReview.get(msg.requestId);
|
|
16797
|
+
if (resolve13) {
|
|
16952
16798
|
this.pendingHubReview.delete(msg.requestId);
|
|
16953
|
-
|
|
16799
|
+
resolve13({ action: msg.action, message: msg.message });
|
|
16954
16800
|
}
|
|
16955
16801
|
return;
|
|
16956
16802
|
}
|
|
@@ -16963,9 +16809,9 @@ var SessionHandler = class {
|
|
|
16963
16809
|
runLifecycleHooks(mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()), "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
|
|
16964
16810
|
this.toolExecutor.cancelAll();
|
|
16965
16811
|
if (this.abortController) this.abortController.abort();
|
|
16966
|
-
for (const
|
|
16812
|
+
for (const resolve13 of this.pendingAskUser.values()) resolve13(null);
|
|
16967
16813
|
this.pendingAskUser.clear();
|
|
16968
|
-
for (const
|
|
16814
|
+
for (const resolve13 of this.pendingAutoPause.values()) resolve13({ action: "stop" });
|
|
16969
16815
|
this.pendingAutoPause.clear();
|
|
16970
16816
|
this.saveIfNeeded();
|
|
16971
16817
|
if (this._bashSessionKey) clearBashSession(this._bashSessionKey);
|
|
@@ -17100,9 +16946,9 @@ var SessionHandler = class {
|
|
|
17100
16946
|
this.hubOrchestrator = orchestrator;
|
|
17101
16947
|
orchestrator.onEvent = (event) => this.send({ type: "hub_event", event });
|
|
17102
16948
|
if (config.humanSteer) {
|
|
17103
|
-
orchestrator.onRoundReview = ({ round, maxRounds }) => new Promise((
|
|
16949
|
+
orchestrator.onRoundReview = ({ round, maxRounds }) => new Promise((resolve13) => {
|
|
17104
16950
|
const requestId = `hubrev_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
17105
|
-
this.pendingHubReview.set(requestId,
|
|
16951
|
+
this.pendingHubReview.set(requestId, resolve13);
|
|
17106
16952
|
this.send({ type: "hub_review", requestId, round, maxRounds });
|
|
17107
16953
|
});
|
|
17108
16954
|
}
|
|
@@ -17435,7 +17281,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
17435
17281
|
this.send({ type: "response_done", content, usage });
|
|
17436
17282
|
if (pendingTeeSave && isCleanDocumentBody(content)) {
|
|
17437
17283
|
try {
|
|
17438
|
-
|
|
17284
|
+
mkdirSync14(dirname9(pendingTeeSave), { recursive: true });
|
|
17439
17285
|
const bodyToSave = stripOuterCodeFence(content);
|
|
17440
17286
|
atomicWriteFileSync(pendingTeeSave, bodyToSave);
|
|
17441
17287
|
undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
|
|
@@ -17460,8 +17306,8 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
|
|
|
17460
17306
|
onMcpToolUsed: (name) => this.usedMcpToolNames.add(name),
|
|
17461
17307
|
requestAutoPause: async ({ effectiveRound, maxToolRounds: totalRounds, toolSummary }) => {
|
|
17462
17308
|
const requestId = `pause_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
17463
|
-
const pauseResp = await new Promise((
|
|
17464
|
-
this.pendingAutoPause.set(requestId,
|
|
17309
|
+
const pauseResp = await new Promise((resolve13) => {
|
|
17310
|
+
this.pendingAutoPause.set(requestId, resolve13);
|
|
17465
17311
|
this.send({
|
|
17466
17312
|
type: "auto_pause_request",
|
|
17467
17313
|
requestId,
|
|
@@ -17611,7 +17457,7 @@ ${summaryContent}`,
|
|
|
17611
17457
|
let isError = false;
|
|
17612
17458
|
let summary;
|
|
17613
17459
|
try {
|
|
17614
|
-
|
|
17460
|
+
mkdirSync14(dirname9(saveToFile), { recursive: true });
|
|
17615
17461
|
fileStream = createWriteStream2(saveToFile);
|
|
17616
17462
|
const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
|
|
17617
17463
|
const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
|
|
@@ -17640,8 +17486,8 @@ ${summaryContent}`,
|
|
|
17640
17486
|
}
|
|
17641
17487
|
if (chunk.done) break;
|
|
17642
17488
|
}
|
|
17643
|
-
await new Promise((
|
|
17644
|
-
fileStream.end((err) => err ? reject(err) :
|
|
17489
|
+
await new Promise((resolve13, reject) => {
|
|
17490
|
+
fileStream.end((err) => err ? reject(err) : resolve13());
|
|
17645
17491
|
});
|
|
17646
17492
|
const verdict = evaluateTeeContent(fullContent, saveToFile, priorContent);
|
|
17647
17493
|
if (verdict.kind === "reject") {
|
|
@@ -17667,7 +17513,7 @@ ${summaryContent}`,
|
|
|
17667
17513
|
} catch (err) {
|
|
17668
17514
|
if (fileStream) {
|
|
17669
17515
|
try {
|
|
17670
|
-
await new Promise((
|
|
17516
|
+
await new Promise((resolve13) => fileStream.end(() => resolve13()));
|
|
17671
17517
|
} catch {
|
|
17672
17518
|
}
|
|
17673
17519
|
}
|
|
@@ -17874,7 +17720,11 @@ ${summaryContent}`,
|
|
|
17874
17720
|
const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
|
|
17875
17721
|
const content = entries.length === 0 ? "(empty \u2014 no persistent memory entries yet)" : entries.map((entry) => {
|
|
17876
17722
|
const status = isMemoryExpired(entry) ? "expired" : entry.approved ? "approved" : "pending";
|
|
17877
|
-
const source =
|
|
17723
|
+
const source = [
|
|
17724
|
+
entry.source ?? "unknown",
|
|
17725
|
+
entry.sourceSession ? `session ${entry.sourceSession.slice(0, 8)}` : "",
|
|
17726
|
+
entry.sourceProject ? `project ${entry.sourceProject}` : ""
|
|
17727
|
+
].filter(Boolean).join(" \xB7 ");
|
|
17878
17728
|
return `## ${entry.id.slice(0, 8)} \xB7 ${status} \xB7 scope:${entry.scope} \xB7 sensitivity:${entry.sensitivity}
|
|
17879
17729
|
Source: ${source}
|
|
17880
17730
|
${entry.content}`;
|
|
@@ -17886,26 +17736,41 @@ ${entry.content}`;
|
|
|
17886
17736
|
});
|
|
17887
17737
|
this.sendMemoryEntries();
|
|
17888
17738
|
}
|
|
17889
|
-
memoryAdd(text) {
|
|
17739
|
+
memoryAdd(text, opts) {
|
|
17740
|
+
const asSensitivity = (v) => v === "low" || v === "medium" || v === "high" ? v : void 0;
|
|
17890
17741
|
try {
|
|
17891
17742
|
const entry = addMemoryEntry(this.config.getConfigDir(), text, {
|
|
17892
|
-
scope: "personal",
|
|
17743
|
+
scope: parseWritableScope(opts?.scope) ?? "personal",
|
|
17744
|
+
source: "manual",
|
|
17745
|
+
sensitivity: asSensitivity(opts?.sensitivity),
|
|
17746
|
+
expiresAt: opts?.expiresAt,
|
|
17893
17747
|
cwd: process.cwd(),
|
|
17894
17748
|
redact: true
|
|
17895
17749
|
});
|
|
17896
17750
|
const status = entry.approved ? "approved" : "pending approval";
|
|
17897
|
-
this.send({ type: "info", message: `\u{1F4DD} Memory entry ${entry.id.slice(0, 8)} added (${status}).` });
|
|
17751
|
+
this.send({ type: "info", message: `\u{1F4DD} Memory entry ${entry.id.slice(0, 8)} added (${status}, scope:${entry.scope}).` });
|
|
17898
17752
|
this.sendMemoryEntries();
|
|
17899
17753
|
} catch (err) {
|
|
17900
17754
|
this.send({ type: "error", message: `Failed to write memory: ${err.message}` });
|
|
17901
17755
|
}
|
|
17902
17756
|
}
|
|
17757
|
+
memoryEdit(id, text) {
|
|
17758
|
+
try {
|
|
17759
|
+
const entry = updateMemoryEntry(this.config.getConfigDir(), id, text, { source: "manual", redact: true });
|
|
17760
|
+
const status = entry.approved ? "approved" : `pending approval \u2014 approve it in the Memory panel or /memory approve ${entry.id.slice(0, 8)}`;
|
|
17761
|
+
this.send({ type: "info", message: `\u{1F4DD} Memory ${entry.id.slice(0, 8)} updated (${status}).` });
|
|
17762
|
+
this.sendMemoryEntries();
|
|
17763
|
+
} catch (err) {
|
|
17764
|
+
this.send({ type: "error", message: `Failed to update memory: ${err.message}` });
|
|
17765
|
+
}
|
|
17766
|
+
}
|
|
17903
17767
|
memoryClear() {
|
|
17904
17768
|
const configDir = this.config.getConfigDir();
|
|
17905
17769
|
try {
|
|
17906
|
-
|
|
17907
|
-
|
|
17908
|
-
|
|
17770
|
+
const result = clearMemoryEntries(configDir);
|
|
17771
|
+
const backupNote = result.backupPath ? `
|
|
17772
|
+
Backup saved: ${result.backupPath}` : "";
|
|
17773
|
+
this.send({ type: "info", message: `\u{1F5D1}\uFE0F Persistent memory cleared (${result.cleared} entries).${backupNote}` });
|
|
17909
17774
|
this.sendMemoryEntries();
|
|
17910
17775
|
} catch (err) {
|
|
17911
17776
|
this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
|
|
@@ -17921,6 +17786,7 @@ ${entry.content}`;
|
|
|
17921
17786
|
id: entry.id,
|
|
17922
17787
|
content: entry.content,
|
|
17923
17788
|
scope: entry.scope,
|
|
17789
|
+
source: entry.source,
|
|
17924
17790
|
sourceSession: entry.sourceSession,
|
|
17925
17791
|
sourceProject: entry.sourceProject,
|
|
17926
17792
|
createdAt: entry.createdAt,
|
|
@@ -17970,7 +17836,7 @@ ${entry.content}`;
|
|
|
17970
17836
|
return;
|
|
17971
17837
|
}
|
|
17972
17838
|
try {
|
|
17973
|
-
const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-
|
|
17839
|
+
const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-ML5FF5L6.js");
|
|
17974
17840
|
const loaded = loadChatIndex2();
|
|
17975
17841
|
if (!loaded || loaded.idx.chunks.length === 0) {
|
|
17976
17842
|
this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
|
|
@@ -18006,7 +17872,7 @@ ${entry.content}`;
|
|
|
18006
17872
|
}
|
|
18007
17873
|
async handleMemoryStatus() {
|
|
18008
17874
|
try {
|
|
18009
|
-
const { getChatIndexStatus } = await import("./chat-index-
|
|
17875
|
+
const { getChatIndexStatus } = await import("./chat-index-ML5FF5L6.js");
|
|
18010
17876
|
const s = getChatIndexStatus();
|
|
18011
17877
|
this.send({
|
|
18012
17878
|
type: "memory_status",
|
|
@@ -18031,7 +17897,7 @@ ${entry.content}`;
|
|
|
18031
17897
|
type: "info",
|
|
18032
17898
|
message: full ? "\u{1F9E0} Rebuilding chat memory index (this may take a while on first run \u2014 ~117 MB embedder)." : "\u{1F9E0} Refreshing chat memory index (incremental)\u2026"
|
|
18033
17899
|
});
|
|
18034
|
-
const { buildChatIndex } = await import("./chat-index-
|
|
17900
|
+
const { buildChatIndex } = await import("./chat-index-ML5FF5L6.js");
|
|
18035
17901
|
const stats = await buildChatIndex({
|
|
18036
17902
|
full,
|
|
18037
17903
|
onProgress: (p) => {
|
|
@@ -18105,6 +17971,7 @@ ${entry.content}`;
|
|
|
18105
17971
|
activeSkill,
|
|
18106
17972
|
planMode: this.planMode,
|
|
18107
17973
|
configDir: this.config.getConfigDir(),
|
|
17974
|
+
memoryMaxChars: this.config.get("memory")?.maxChars,
|
|
18108
17975
|
userProfile: this.config.get("userProfile")
|
|
18109
17976
|
});
|
|
18110
17977
|
let stable = baseStable;
|
|
@@ -18186,8 +18053,8 @@ async function setupProxy(configProxy) {
|
|
|
18186
18053
|
}
|
|
18187
18054
|
|
|
18188
18055
|
// src/web/auth.ts
|
|
18189
|
-
import { existsSync as
|
|
18190
|
-
import { isAbsolute as isAbsolute3, join as
|
|
18056
|
+
import { existsSync as existsSync30, readFileSync as readFileSync23, writeFileSync as writeFileSync5, mkdirSync as mkdirSync15, readdirSync as readdirSync15, copyFileSync } from "fs";
|
|
18057
|
+
import { isAbsolute as isAbsolute3, join as join26, relative as relative5 } from "path";
|
|
18191
18058
|
import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
|
|
18192
18059
|
var USERS_FILE = "users.json";
|
|
18193
18060
|
var TOKEN_EXPIRY_HOURS = 24;
|
|
@@ -18202,7 +18069,7 @@ var AuthManager = class {
|
|
|
18202
18069
|
db;
|
|
18203
18070
|
constructor(baseDir) {
|
|
18204
18071
|
this.baseDir = baseDir;
|
|
18205
|
-
this.usersFile =
|
|
18072
|
+
this.usersFile = join26(baseDir, USERS_FILE);
|
|
18206
18073
|
this.db = this.loadOrCreate();
|
|
18207
18074
|
}
|
|
18208
18075
|
// ── Public API ─────────────────────────────────────────────────
|
|
@@ -18231,9 +18098,9 @@ var AuthManager = class {
|
|
|
18231
18098
|
}
|
|
18232
18099
|
const salt = randomBytes(16).toString("hex");
|
|
18233
18100
|
const passwordHash = this.hashPassword(password, salt);
|
|
18234
|
-
const dataDir =
|
|
18235
|
-
const fullDataDir =
|
|
18236
|
-
|
|
18101
|
+
const dataDir = join26(USERS_DIR, username);
|
|
18102
|
+
const fullDataDir = join26(this.baseDir, dataDir);
|
|
18103
|
+
mkdirSync15(join26(fullDataDir, "history"), { recursive: true });
|
|
18237
18104
|
const user = {
|
|
18238
18105
|
username,
|
|
18239
18106
|
passwordHash,
|
|
@@ -18348,7 +18215,7 @@ var AuthManager = class {
|
|
|
18348
18215
|
getUserDataDir(username) {
|
|
18349
18216
|
const user = this.db.users.find((u) => u.username === username);
|
|
18350
18217
|
if (!user) throw new Error(`User not found: ${username}`);
|
|
18351
|
-
return
|
|
18218
|
+
return join26(this.baseDir, user.dataDir);
|
|
18352
18219
|
}
|
|
18353
18220
|
/** List all usernames */
|
|
18354
18221
|
listUsers() {
|
|
@@ -18384,30 +18251,30 @@ var AuthManager = class {
|
|
|
18384
18251
|
const err = this.register(username, password);
|
|
18385
18252
|
if (err) return err;
|
|
18386
18253
|
const userDir = this.getUserDataDir(username);
|
|
18387
|
-
const globalConfig =
|
|
18388
|
-
if (
|
|
18254
|
+
const globalConfig = join26(this.baseDir, "config.json");
|
|
18255
|
+
if (existsSync30(globalConfig)) {
|
|
18389
18256
|
try {
|
|
18390
|
-
const content =
|
|
18391
|
-
writeFileSync5(
|
|
18257
|
+
const content = readFileSync23(globalConfig, "utf-8");
|
|
18258
|
+
writeFileSync5(join26(userDir, "config.json"), content, "utf-8");
|
|
18392
18259
|
} catch {
|
|
18393
18260
|
}
|
|
18394
18261
|
}
|
|
18395
|
-
const globalMemory =
|
|
18396
|
-
if (
|
|
18262
|
+
const globalMemory = join26(this.baseDir, "memory.md");
|
|
18263
|
+
if (existsSync30(globalMemory)) {
|
|
18397
18264
|
try {
|
|
18398
|
-
const content =
|
|
18399
|
-
writeFileSync5(
|
|
18265
|
+
const content = readFileSync23(globalMemory, "utf-8");
|
|
18266
|
+
writeFileSync5(join26(userDir, "memory.md"), content, "utf-8");
|
|
18400
18267
|
} catch {
|
|
18401
18268
|
}
|
|
18402
18269
|
}
|
|
18403
|
-
const globalHistory =
|
|
18404
|
-
if (
|
|
18270
|
+
const globalHistory = join26(this.baseDir, "history");
|
|
18271
|
+
if (existsSync30(globalHistory)) {
|
|
18405
18272
|
try {
|
|
18406
18273
|
const files = readdirSync15(globalHistory).filter((f) => f.endsWith(".json"));
|
|
18407
|
-
const userHistory =
|
|
18274
|
+
const userHistory = join26(userDir, "history");
|
|
18408
18275
|
for (const f of files) {
|
|
18409
18276
|
try {
|
|
18410
|
-
copyFileSync(
|
|
18277
|
+
copyFileSync(join26(globalHistory, f), join26(userHistory, f));
|
|
18411
18278
|
} catch {
|
|
18412
18279
|
}
|
|
18413
18280
|
}
|
|
@@ -18418,9 +18285,9 @@ var AuthManager = class {
|
|
|
18418
18285
|
}
|
|
18419
18286
|
// ── Private methods ────────────────────────────────────────────
|
|
18420
18287
|
loadOrCreate() {
|
|
18421
|
-
if (
|
|
18288
|
+
if (existsSync30(this.usersFile)) {
|
|
18422
18289
|
try {
|
|
18423
|
-
const parsed = JSON.parse(
|
|
18290
|
+
const parsed = JSON.parse(readFileSync23(this.usersFile, "utf-8"));
|
|
18424
18291
|
if (!this.isValidUsersDB(parsed)) {
|
|
18425
18292
|
throw new Error("database structure is invalid");
|
|
18426
18293
|
}
|
|
@@ -18454,7 +18321,7 @@ var AuthManager = class {
|
|
|
18454
18321
|
if (typeof record.username !== "string" || !/^[a-z0-9_-]{2,32}$/.test(record.username) || usernames.has(record.username) || typeof record.passwordHash !== "string" || record.passwordHash.length === 0 || typeof record.salt !== "string" || record.salt.length === 0 || typeof record.createdAt !== "string" || typeof record.dataDir !== "string" || record.dataDir.length === 0 || isAbsolute3(record.dataDir)) {
|
|
18455
18322
|
return false;
|
|
18456
18323
|
}
|
|
18457
|
-
const dataDirFromBase = relative5(this.baseDir,
|
|
18324
|
+
const dataDirFromBase = relative5(this.baseDir, join26(this.baseDir, record.dataDir));
|
|
18458
18325
|
if (dataDirFromBase.startsWith("..") || isAbsolute3(dataDirFromBase)) return false;
|
|
18459
18326
|
if (record.hashVersion !== void 0 && record.hashVersion !== 1 && record.hashVersion !== 2) return false;
|
|
18460
18327
|
if (record.tokensRevokedBefore !== void 0 && !Number.isFinite(record.tokensRevokedBefore)) return false;
|
|
@@ -18466,7 +18333,7 @@ var AuthManager = class {
|
|
|
18466
18333
|
this.saveDB(this.db);
|
|
18467
18334
|
}
|
|
18468
18335
|
saveDB(db) {
|
|
18469
|
-
|
|
18336
|
+
mkdirSync15(this.baseDir, { recursive: true });
|
|
18470
18337
|
atomicWriteFileSync(this.usersFile, JSON.stringify(db, null, 2));
|
|
18471
18338
|
}
|
|
18472
18339
|
/** Legacy hash — kept only for migrating old users (v0.2.x) */
|
|
@@ -18498,11 +18365,11 @@ function getModuleDir() {
|
|
|
18498
18365
|
if (typeof import.meta?.url === "string") {
|
|
18499
18366
|
const url = new URL(import.meta.url);
|
|
18500
18367
|
const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
|
|
18501
|
-
return
|
|
18368
|
+
return dirname10(filePath);
|
|
18502
18369
|
}
|
|
18503
18370
|
} catch {
|
|
18504
18371
|
}
|
|
18505
|
-
return
|
|
18372
|
+
return resolve12(".");
|
|
18506
18373
|
}
|
|
18507
18374
|
async function startWebServer(options = {}) {
|
|
18508
18375
|
const port = options.port ?? 3e3;
|
|
@@ -18570,8 +18437,8 @@ async function startWebServer(options = {}) {
|
|
|
18570
18437
|
}
|
|
18571
18438
|
}
|
|
18572
18439
|
let skillManager = null;
|
|
18573
|
-
const skillsDir =
|
|
18574
|
-
if (
|
|
18440
|
+
const skillsDir = join27(config.getConfigDir(), SKILLS_DIR_NAME);
|
|
18441
|
+
if (existsSync31(skillsDir)) {
|
|
18575
18442
|
skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
|
|
18576
18443
|
skillManager.loadSkills(pluginAssets.skillDirs);
|
|
18577
18444
|
const count = skillManager.listSkills().length;
|
|
@@ -18642,18 +18509,18 @@ async function startWebServer(options = {}) {
|
|
|
18642
18509
|
next();
|
|
18643
18510
|
};
|
|
18644
18511
|
const moduleDir = getModuleDir();
|
|
18645
|
-
let clientDir =
|
|
18646
|
-
if (!
|
|
18647
|
-
clientDir =
|
|
18512
|
+
let clientDir = join27(moduleDir, "web", "client");
|
|
18513
|
+
if (!existsSync31(clientDir)) {
|
|
18514
|
+
clientDir = join27(moduleDir, "client");
|
|
18648
18515
|
}
|
|
18649
|
-
if (!
|
|
18650
|
-
clientDir =
|
|
18516
|
+
if (!existsSync31(clientDir)) {
|
|
18517
|
+
clientDir = join27(moduleDir, "..", "..", "src", "web", "client");
|
|
18651
18518
|
}
|
|
18652
|
-
if (!
|
|
18653
|
-
clientDir =
|
|
18519
|
+
if (!existsSync31(clientDir)) {
|
|
18520
|
+
clientDir = join27(process.cwd(), "src", "web", "client");
|
|
18654
18521
|
}
|
|
18655
18522
|
console.log(` Static files: ${clientDir}`);
|
|
18656
|
-
app.use("/vendor", express.static(
|
|
18523
|
+
app.use("/vendor", express.static(join27(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
|
|
18657
18524
|
app.use(express.static(clientDir));
|
|
18658
18525
|
app.get("/api/status", (_req, res) => {
|
|
18659
18526
|
res.json({
|
|
@@ -18720,10 +18587,10 @@ async function startWebServer(options = {}) {
|
|
|
18720
18587
|
app.get("/api/files", requireAuth, (req, res) => {
|
|
18721
18588
|
const cwd = process.cwd();
|
|
18722
18589
|
const prefix = req.query.prefix || "";
|
|
18723
|
-
const targetDir =
|
|
18590
|
+
const targetDir = join27(cwd, prefix);
|
|
18724
18591
|
try {
|
|
18725
|
-
const canonicalTarget = realpathSync3(
|
|
18726
|
-
const canonicalCwd = realpathSync3(
|
|
18592
|
+
const canonicalTarget = realpathSync3(resolve12(targetDir));
|
|
18593
|
+
const canonicalCwd = realpathSync3(resolve12(cwd));
|
|
18727
18594
|
if (!canonicalTarget.startsWith(canonicalCwd + sep4) && canonicalTarget !== canonicalCwd) {
|
|
18728
18595
|
res.json({ files: [] });
|
|
18729
18596
|
return;
|
|
@@ -18737,7 +18604,7 @@ async function startWebServer(options = {}) {
|
|
|
18737
18604
|
const entries = readdirSync16(targetDir, { withFileTypes: true });
|
|
18738
18605
|
const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
|
|
18739
18606
|
name: e.name,
|
|
18740
|
-
path: relative6(cwd,
|
|
18607
|
+
path: relative6(cwd, join27(targetDir, e.name)).replace(/\\/g, "/"),
|
|
18741
18608
|
isDir: e.isDirectory()
|
|
18742
18609
|
}));
|
|
18743
18610
|
res.json({ files });
|
|
@@ -18777,8 +18644,8 @@ async function startWebServer(options = {}) {
|
|
|
18777
18644
|
try {
|
|
18778
18645
|
const authUser = req._authUser;
|
|
18779
18646
|
const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
|
|
18780
|
-
const filePath =
|
|
18781
|
-
if (!
|
|
18647
|
+
const filePath = join27(histDir, `${id}.json`);
|
|
18648
|
+
if (!existsSync31(filePath)) {
|
|
18782
18649
|
res.status(404).json({ error: "Session not found" });
|
|
18783
18650
|
return;
|
|
18784
18651
|
}
|
|
@@ -18793,7 +18660,7 @@ async function startWebServer(options = {}) {
|
|
|
18793
18660
|
res.status(404).json({ error: "Session not found" });
|
|
18794
18661
|
return;
|
|
18795
18662
|
}
|
|
18796
|
-
const data = JSON.parse(
|
|
18663
|
+
const data = JSON.parse(readFileSync24(filePath, "utf-8"));
|
|
18797
18664
|
res.json({ session: data });
|
|
18798
18665
|
} catch (err) {
|
|
18799
18666
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
@@ -18806,10 +18673,10 @@ async function startWebServer(options = {}) {
|
|
|
18806
18673
|
return;
|
|
18807
18674
|
}
|
|
18808
18675
|
const cwd = process.cwd();
|
|
18809
|
-
const fullPath =
|
|
18676
|
+
const fullPath = resolve12(join27(cwd, filePath));
|
|
18810
18677
|
try {
|
|
18811
18678
|
const canonicalFull = realpathSync3(fullPath);
|
|
18812
|
-
const canonicalCwd = realpathSync3(
|
|
18679
|
+
const canonicalCwd = realpathSync3(resolve12(cwd));
|
|
18813
18680
|
if (!canonicalFull.startsWith(canonicalCwd + sep4) && canonicalFull !== canonicalCwd) {
|
|
18814
18681
|
res.json({ error: "Access denied" });
|
|
18815
18682
|
return;
|
|
@@ -18824,7 +18691,7 @@ async function startWebServer(options = {}) {
|
|
|
18824
18691
|
res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
|
|
18825
18692
|
return;
|
|
18826
18693
|
}
|
|
18827
|
-
const content =
|
|
18694
|
+
const content = readFileSync24(fullPath, "utf-8");
|
|
18828
18695
|
res.json({ content, size: stat.size });
|
|
18829
18696
|
} catch {
|
|
18830
18697
|
res.json({ error: "Cannot read file" });
|
|
@@ -19028,7 +18895,7 @@ async function startWebServer(options = {}) {
|
|
|
19028
18895
|
});
|
|
19029
18896
|
const MAX_PORT_ATTEMPTS = 10;
|
|
19030
18897
|
let actualPort = port;
|
|
19031
|
-
const result = await new Promise((
|
|
18898
|
+
const result = await new Promise((resolve13, reject) => {
|
|
19032
18899
|
const tryListen = (attempt) => {
|
|
19033
18900
|
server.once("error", (err) => {
|
|
19034
18901
|
if (err.code === "EADDRINUSE" && attempt < MAX_PORT_ATTEMPTS) {
|
|
@@ -19059,7 +18926,7 @@ async function startWebServer(options = {}) {
|
|
|
19059
18926
|
}
|
|
19060
18927
|
console.log(` Press Ctrl+C to stop
|
|
19061
18928
|
`);
|
|
19062
|
-
|
|
18929
|
+
resolve13({ port: actualPort, host, url });
|
|
19063
18930
|
});
|
|
19064
18931
|
};
|
|
19065
18932
|
tryListen(1);
|
|
@@ -19079,17 +18946,17 @@ function resolveProjectMcpPath() {
|
|
|
19079
18946
|
const cwd = process.cwd();
|
|
19080
18947
|
const gitRoot = getGitRoot(cwd);
|
|
19081
18948
|
const projectRoot = gitRoot ?? cwd;
|
|
19082
|
-
const configPath =
|
|
19083
|
-
return
|
|
18949
|
+
const configPath = join27(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18950
|
+
return existsSync31(configPath) ? configPath : null;
|
|
19084
18951
|
}
|
|
19085
18952
|
function loadProjectMcpConfig() {
|
|
19086
18953
|
const cwd = process.cwd();
|
|
19087
18954
|
const gitRoot = getGitRoot(cwd);
|
|
19088
18955
|
const projectRoot = gitRoot ?? cwd;
|
|
19089
|
-
const configPath =
|
|
19090
|
-
if (!
|
|
18956
|
+
const configPath = join27(projectRoot, MCP_PROJECT_CONFIG_NAME);
|
|
18957
|
+
if (!existsSync31(configPath)) return null;
|
|
19091
18958
|
try {
|
|
19092
|
-
const raw = JSON.parse(
|
|
18959
|
+
const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
|
|
19093
18960
|
return raw.mcpServers ?? raw;
|
|
19094
18961
|
} catch {
|
|
19095
18962
|
return null;
|