jinzd-ai-cli 0.4.241 → 0.4.242

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.
Files changed (42) hide show
  1. package/README.md +2 -2
  2. package/dist/{batch-K66BLTLJ.js → batch-D3UHL432.js} +2 -2
  3. package/dist/{chat-index-7HXBWQFH.js → chat-index-ALUHFJKI.js} +3 -2
  4. package/dist/{chat-index-WYXYD7YP.js → chat-index-ML5FF5L6.js} +2 -1
  5. package/dist/{chunk-NAQPP3GZ.js → chunk-6ZWNXHNH.js} +1 -1
  6. package/dist/chunk-B5TYJO7V.js +123 -0
  7. package/dist/chunk-D27TMEOV.js +327 -0
  8. package/dist/{chunk-2QQKS56X.js → chunk-DT77O33T.js} +1 -1
  9. package/dist/{chunk-GORPFFV4.js → chunk-FEMPCNX3.js} +79 -252
  10. package/dist/{chunk-3AIR7N5M.js → chunk-FRNWQIG6.js} +5 -5
  11. package/dist/{chunk-5MYPIQ3Y.js → chunk-IGPFYNZA.js} +4 -2
  12. package/dist/{chunk-2LLQOMMW.js → chunk-J363BGOH.js} +1 -1
  13. package/dist/{chunk-JMP3LJC2.js → chunk-KWQKQVPH.js} +4 -2
  14. package/dist/{chunk-PGHTAKIA.js → chunk-LHINXQZF.js} +1 -1
  15. package/dist/{chunk-ZPXMT2B6.js → chunk-MC2ZNPBC.js} +4 -163
  16. package/dist/chunk-MKLPAQT6.js +164 -0
  17. package/dist/{chunk-UO3XWZFE.js → chunk-NV63EYFL.js} +3 -119
  18. package/dist/{chunk-CHFFEVVT.js → chunk-OO2PHOXD.js} +3 -3
  19. package/dist/{chunk-KCEO2XJ4.js → chunk-PJ3ONBWZ.js} +0 -119
  20. package/dist/{chunk-LXSUPLCK.js → chunk-PSP6UIKL.js} +2 -2
  21. package/dist/chunk-UWV2NBCO.js +418 -0
  22. package/dist/{chunk-IV6GCDVR.js → chunk-WMAXYDCB.js} +7 -1
  23. package/dist/{chunk-SMQHEJSH.js → chunk-Y4CMNXZH.js} +1 -1
  24. package/dist/chunk-YVTASHS5.js +121 -0
  25. package/dist/{ci-T2ABDMMQ.js → ci-WSED3Z6V.js} +4 -4
  26. package/dist/{ci-format-S2PPU27O.js → ci-format-6AYL2X2J.js} +2 -2
  27. package/dist/{constants-RS7OUU7X.js → constants-FJ3L5GYO.js} +1 -1
  28. package/dist/{doctor-cli-TGAW6RAP.js → doctor-cli-HCP56MKH.js} +4 -4
  29. package/dist/electron-server.js +423 -572
  30. package/dist/{hub-GL5ZU6WS.js → hub-K5MYSVEF.js} +2 -2
  31. package/dist/index.js +203 -58
  32. package/dist/{persist-AMH5SQ4W.js → persist-WGIJ6RXY.js} +4 -3
  33. package/dist/persistent-memory-EYAKL2XA.js +55 -0
  34. package/dist/persistent-memory-HWFNDQFQ.js +53 -0
  35. package/dist/{pr-TJFKUZ4B.js → pr-OICMUN6R.js} +5 -5
  36. package/dist/{run-tests-EWMU34A4.js → run-tests-B3FSE5NV.js} +2 -1
  37. package/dist/{run-tests-KEI7IXTB.js → run-tests-DSEW5HED.js} +2 -2
  38. package/dist/{server-UVS2D4M6.js → server-2FS65CQZ.js} +130 -47
  39. package/dist/{server-E545UIX7.js → server-EDCGPEGC.js} +8 -6
  40. package/dist/{task-orchestrator-PWXV2EBN.js → task-orchestrator-WCZYV3TE.js} +8 -6
  41. package/dist/{usage-EDJ6QOWB.js → usage-WVUYNLOD.js} +2 -2
  42. package/package.json +1 -1
@@ -1,9 +1,45 @@
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
+ updateMemoryApproval,
34
+ updateMemoryEntry
35
+ } from "./chunk-UWV2NBCO.js";
36
+ import {
37
+ redactJson,
38
+ scanString
39
+ } from "./chunk-YVTASHS5.js";
40
+ import {
41
+ runTestsTool
42
+ } from "./chunk-MC2ZNPBC.js";
7
43
  import {
8
44
  AGENTIC_BEHAVIOR_GUIDELINE,
9
45
  APP_NAME,
@@ -26,8 +62,6 @@ import {
26
62
  MCP_PROTOCOL_VERSION,
27
63
  MCP_TOOL_PREFIX,
28
64
  MEMORY_FILE_NAME,
29
- MEMORY_MAX_CHARS,
30
- MEMORY_STORE_FILE_NAME,
31
65
  PLAN_MODE_READONLY_TOOLS,
32
66
  PLAN_MODE_SYSTEM_ADDON,
33
67
  PLUGINS_DIR_NAME,
@@ -36,9 +70,8 @@ import {
36
70
  SUBAGENT_DEFAULT_MAX_ROUNDS,
37
71
  SUBAGENT_MAX_ROUNDS_LIMIT,
38
72
  VERSION,
39
- buildUserIdentityPrompt,
40
- runTestsTool
41
- } from "./chunk-ZPXMT2B6.js";
73
+ buildUserIdentityPrompt
74
+ } from "./chunk-MKLPAQT6.js";
42
75
  import {
43
76
  hasSemanticIndex,
44
77
  semanticSearch
@@ -47,20 +80,6 @@ import {
47
80
  loadIndex
48
81
  } from "./chunk-RWM2GFRC.js";
49
82
  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
83
  import "./chunk-JV5N65KN.js";
65
84
  import {
66
85
  atomicWriteFileSync
@@ -70,8 +89,8 @@ import {
70
89
  import express from "express";
71
90
  import { createServer } from "http";
72
91
  import { WebSocketServer } from "ws";
73
- import { join as join28, dirname as dirname11, resolve as resolve13, relative as relative6, sep as sep4 } from "path";
74
- import { existsSync as existsSync33, readFileSync as readFileSync25, readdirSync as readdirSync16, statSync as statSync14, realpathSync as realpathSync3 } from "fs";
92
+ import { join as join26, dirname as dirname10, resolve as resolve12, relative as relative6, sep as sep4 } from "path";
93
+ import { existsSync as existsSync31, readFileSync as readFileSync24, readdirSync as readdirSync16, statSync as statSync14, realpathSync as realpathSync3 } from "fs";
75
94
  import { networkInterfaces } from "os";
76
95
 
77
96
  // src/config/config-manager.ts
@@ -192,6 +211,12 @@ var ConfigSchema = z.object({
192
211
  projectDocMaxBytes: z.number().int().positive().default(32 * 1024),
193
212
  showLoadedContextSources: z.boolean().default(false)
194
213
  }).default({}),
214
+ // 持久记忆注入配置(MEM-005)
215
+ // maxChars: 注入 system prompt 的字符预算,超出丢最老条目并显式标注截断。
216
+ // 注意 CJK ≈ 1 字符/token——中文记忆的 token 开销约等于字符数。
217
+ memory: z.object({
218
+ maxChars: z.number().int().positive().max(2e5).default(1e4)
219
+ }).default({}),
195
220
  // Google Custom Search API 的 Search Engine ID (cx 参数)
196
221
  // API Key 通过 apiKeys['google-search'] 或 AICLI_API_KEY_GOOGLESEARCH 环境变量配置
197
222
  // CX 也可通过 AICLI_GOOGLE_CX 环境变量覆盖
@@ -4927,7 +4952,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
4927
4952
  const markerStart = `AICLI_MARKER_START:${id}`;
4928
4953
  const markerEnd = `AICLI_MARKER_END:${id}`;
4929
4954
  const wrappedCmd = IS_WINDOWS2 ? `${WIN_UTF8_PREAMBLE} echo "${markerStart}"; ${command}; echo "${markerEnd}:0"` : `echo "${markerStart}"; ${command}; echo "${markerEnd}:$?"`;
4930
- return new Promise((resolve14) => {
4955
+ return new Promise((resolve13) => {
4931
4956
  let stdout = "";
4932
4957
  let stderr = "";
4933
4958
  let timedOut = false;
@@ -4943,7 +4968,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
4943
4968
  stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
4944
4969
  resolved = true;
4945
4970
  cleanup();
4946
- resolve14({ stdout, stderr, exitCode, timedOut, aborted });
4971
+ resolve13({ stdout, stderr, exitCode, timedOut, aborted });
4947
4972
  }
4948
4973
  };
4949
4974
  const onErr = (chunk) => {
@@ -4958,7 +4983,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
4958
4983
  resolved = true;
4959
4984
  stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
4960
4985
  cleanup();
4961
- resolve14({ stdout, stderr, exitCode: null, timedOut, aborted });
4986
+ resolve13({ stdout, stderr, exitCode: null, timedOut, aborted });
4962
4987
  try {
4963
4988
  proc.kill();
4964
4989
  } catch {
@@ -4971,7 +4996,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
4971
4996
  resolved = true;
4972
4997
  cleanup();
4973
4998
  stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
4974
- resolve14({ stdout, stderr, exitCode: null, timedOut, aborted });
4999
+ resolve13({ stdout, stderr, exitCode: null, timedOut, aborted });
4975
5000
  try {
4976
5001
  proc.kill();
4977
5002
  } catch {
@@ -4989,7 +5014,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
4989
5014
  stderr += `
4990
5015
  [pool error] ${err.message}`;
4991
5016
  cleanup();
4992
- resolve14({ stdout, stderr, exitCode: null, timedOut, aborted });
5017
+ resolve13({ stdout, stderr, exitCode: null, timedOut, aborted });
4993
5018
  pool.delete(sessionKey);
4994
5019
  };
4995
5020
  proc.on("error", onError);
@@ -7384,7 +7409,7 @@ var ToolExecutor = class {
7384
7409
  rl.resume();
7385
7410
  process.stdout.write(prompt);
7386
7411
  this.confirming = true;
7387
- return new Promise((resolve14) => {
7412
+ return new Promise((resolve13) => {
7388
7413
  let completed = false;
7389
7414
  const cleanup = (result) => {
7390
7415
  if (completed) return;
@@ -7394,7 +7419,7 @@ var ToolExecutor = class {
7394
7419
  rl.pause();
7395
7420
  rlAny.output = savedOutput;
7396
7421
  this.confirming = false;
7397
- resolve14(result);
7422
+ resolve13(result);
7398
7423
  };
7399
7424
  const onLine = (line) => {
7400
7425
  const trimmed = line.trim();
@@ -7568,7 +7593,7 @@ var ToolExecutor = class {
7568
7593
  rl.resume();
7569
7594
  process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
7570
7595
  this.confirming = true;
7571
- return new Promise((resolve14) => {
7596
+ return new Promise((resolve13) => {
7572
7597
  let completed = false;
7573
7598
  const cleanup = (answer) => {
7574
7599
  if (completed) return;
@@ -7578,7 +7603,7 @@ var ToolExecutor = class {
7578
7603
  rl.pause();
7579
7604
  rlAny.output = savedOutput;
7580
7605
  this.confirming = false;
7581
- resolve14(answer === "y");
7606
+ resolve13(answer === "y");
7582
7607
  };
7583
7608
  const onLine = (line) => {
7584
7609
  const trimmed = line.trim();
@@ -8827,7 +8852,7 @@ var runInteractiveTool = {
8827
8852
  PYTHONDONTWRITEBYTECODE: "1"
8828
8853
  };
8829
8854
  const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
8830
- return new Promise((resolve14) => {
8855
+ return new Promise((resolve13) => {
8831
8856
  const child = spawn3(executable, cmdArgs.map(String), {
8832
8857
  cwd: process.cwd(),
8833
8858
  env,
@@ -8860,22 +8885,22 @@ var runInteractiveTool = {
8860
8885
  setTimeout(writeNextLine, 400);
8861
8886
  const timer = setTimeout(() => {
8862
8887
  child.kill();
8863
- resolve14(`${prefixWarnings}[Timeout after ${timeout}ms]
8888
+ resolve13(`${prefixWarnings}[Timeout after ${timeout}ms]
8864
8889
  ${buildOutput(stdout, stderr)}`);
8865
8890
  }, timeout);
8866
8891
  child.on("close", (code) => {
8867
8892
  clearTimeout(timer);
8868
8893
  const output = buildOutput(stdout, stderr);
8869
8894
  if (code !== 0 && code !== null) {
8870
- resolve14(`${prefixWarnings}Exit code ${code}:
8895
+ resolve13(`${prefixWarnings}Exit code ${code}:
8871
8896
  ${output}`);
8872
8897
  } else {
8873
- resolve14(`${prefixWarnings}${output || "(no output)"}`);
8898
+ resolve13(`${prefixWarnings}${output || "(no output)"}`);
8874
8899
  }
8875
8900
  });
8876
8901
  child.on("error", (err) => {
8877
8902
  clearTimeout(timer);
8878
- resolve14(
8903
+ resolve13(
8879
8904
  `${prefixWarnings}Failed to start process "${executable}": ${err.message}
8880
8905
  Hint: On Windows, use the full path to the executable, e.g.:
8881
8906
  C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
@@ -9587,280 +9612,10 @@ Any of these triggers means use save_last_response, NOT write_file:
9587
9612
  };
9588
9613
 
9589
9614
  // 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
9615
  import { join as join10 } from "path";
9602
- function runGit(cmd, cwd) {
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
9616
+ import { homedir as homedir5 } from "os";
9859
9617
  function getConfigDir() {
9860
- return join12(homedir5(), CONFIG_DIR_NAME);
9861
- }
9862
- function parseScope(value) {
9863
- return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
9618
+ return join10(homedir5(), CONFIG_DIR_NAME);
9864
9619
  }
9865
9620
  function parseSensitivity(value) {
9866
9621
  return value === "low" || value === "medium" || value === "high" ? value : void 0;
@@ -9868,7 +9623,7 @@ function parseSensitivity(value) {
9868
9623
  var saveMemoryTool = {
9869
9624
  definition: {
9870
9625
  name: "save_memory",
9871
- description: "Save important information to governed persistent memory. Low-risk memories are approved automatically; sensitive entries are stored as pending and require /memory approve <id>. Use concise entries for user preferences, project decisions, recurring patterns, and key findings.",
9626
+ 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
9627
  parameters: {
9873
9628
  content: {
9874
9629
  type: "string",
@@ -9877,7 +9632,7 @@ var saveMemoryTool = {
9877
9632
  },
9878
9633
  scope: {
9879
9634
  type: "string",
9880
- description: "Memory scope: personal, project, session, or team. Defaults to personal. Project memories are only injected in the same project.",
9635
+ description: "Memory scope: personal (default, injected everywhere) or project (only injected inside the current project).",
9881
9636
  required: false
9882
9637
  },
9883
9638
  sensitivity: {
@@ -9889,6 +9644,11 @@ var saveMemoryTool = {
9889
9644
  type: "string",
9890
9645
  description: "Optional ISO timestamp after which this memory should no longer be injected.",
9891
9646
  required: false
9647
+ },
9648
+ updateId: {
9649
+ type: "string",
9650
+ 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.",
9651
+ required: false
9892
9652
  }
9893
9653
  },
9894
9654
  dangerous: false
@@ -9896,16 +9656,29 @@ var saveMemoryTool = {
9896
9656
  async execute(args) {
9897
9657
  const content = String(args["content"] ?? "").trim();
9898
9658
  if (!content) throw new ToolError("save_memory", "content is required");
9659
+ const updateId = typeof args["updateId"] === "string" ? args["updateId"].trim() : "";
9660
+ if (updateId) {
9661
+ try {
9662
+ const updated = updateMemoryEntry(getConfigDir(), updateId, content, { source: "tool", redact: true });
9663
+ const redactedNote = updated.redactedKinds?.length ? ` Redacted: ${updated.redactedKinds.join(", ")}.` : "";
9664
+ 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}`;
9665
+ } catch (err) {
9666
+ throw new ToolError("save_memory", err instanceof Error ? err.message : String(err));
9667
+ }
9668
+ }
9669
+ const similar = findSimilarMemory(loadMemoryEntries(getConfigDir()), content);
9899
9670
  const entry = addMemoryEntry(getConfigDir(), content, {
9900
- scope: parseScope(args["scope"]) ?? "personal",
9671
+ scope: parseWritableScope(args["scope"]) ?? "personal",
9672
+ source: "tool",
9901
9673
  sensitivity: parseSensitivity(args["sensitivity"]),
9902
9674
  expiresAt: typeof args["expiresAt"] === "string" ? args["expiresAt"] : void 0,
9903
9675
  cwd: process.cwd(),
9904
9676
  redact: true
9905
9677
  });
9906
- const status = entry.approved ? "approved and active" : "pending approval";
9678
+ 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
9679
  const redacted = entry.redactedKinds?.length ? ` Redacted: ${entry.redactedKinds.join(", ")}.` : "";
9908
- return `Memory saved (${status}). id=${entry.id.slice(0, 8)} scope=${entry.scope} sensitivity=${entry.sensitivity}.${redacted}`;
9680
+ 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.` : "";
9681
+ return `Memory saved (${status}). id=${entry.id.slice(0, 8)} scope=${entry.scope} sensitivity=${entry.sensitivity}.${redacted}${similarNote}`;
9909
9682
  }
9910
9683
  };
9911
9684
 
@@ -9949,7 +9722,7 @@ function promptUser(rl, question) {
9949
9722
  console.log();
9950
9723
  console.log(chalk4.cyan("\u2753 ") + chalk4.bold(question));
9951
9724
  process.stdout.write(chalk4.cyan("> "));
9952
- return new Promise((resolve14) => {
9725
+ return new Promise((resolve13) => {
9953
9726
  let completed = false;
9954
9727
  const cleanup = (answer) => {
9955
9728
  if (completed) return;
@@ -9959,7 +9732,7 @@ function promptUser(rl, question) {
9959
9732
  rl.pause();
9960
9733
  rlAny.output = savedOutput;
9961
9734
  askUserContext.prompting = false;
9962
- resolve14(answer);
9735
+ resolve13(answer);
9963
9736
  };
9964
9737
  const onLine = (line) => {
9965
9738
  cleanup(line);
@@ -10191,14 +9964,14 @@ function formatResults2(query, data, _requested) {
10191
9964
  }
10192
9965
 
10193
9966
  // src/agents/agent-config.ts
10194
- import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync14 } from "fs";
10195
- import { join as join14 } from "path";
9967
+ import { existsSync as existsSync17, readdirSync as readdirSync8, readFileSync as readFileSync13 } from "fs";
9968
+ import { join as join12 } from "path";
10196
9969
  import { homedir as homedir6 } from "os";
10197
9970
 
10198
9971
  // src/plugins/plugin-manager.ts
10199
- import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync13, realpathSync as realpathSync2, cpSync, rmSync, statSync as statSync7 } from "fs";
10200
- import { basename as basename6, dirname as dirname8, join as join13, relative as relative3, resolve as resolve7, sep as sep3 } from "path";
10201
- import { createHash as createHash3 } from "crypto";
9972
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync7, readFileSync as readFileSync12, realpathSync as realpathSync2, cpSync, rmSync, statSync as statSync7 } from "fs";
9973
+ import { basename as basename6, dirname as dirname7, join as join11, relative as relative3, resolve as resolve6, sep as sep3 } from "path";
9974
+ import { createHash as createHash2 } from "crypto";
10202
9975
  import { z as z2 } from "zod";
10203
9976
  var MANIFEST_RELATIVE = ".aicli-plugin/plugin.json";
10204
9977
  var STATE_FILE = "plugin-state.json";
@@ -10257,19 +10030,19 @@ var PluginManifestSchema = z2.object({
10257
10030
  permissionHints: z2.array(z2.string()).default([])
10258
10031
  }).strict();
10259
10032
  function pluginRoot(configDir) {
10260
- return join13(configDir, PLUGINS_DIR_NAME);
10033
+ return join11(configDir, PLUGINS_DIR_NAME);
10261
10034
  }
10262
10035
  function pluginManifestPath(pluginDir) {
10263
- return join13(pluginDir, MANIFEST_RELATIVE);
10036
+ return join11(pluginDir, MANIFEST_RELATIVE);
10264
10037
  }
10265
10038
  function statePath(configDir) {
10266
- return join13(pluginRoot(configDir), STATE_FILE);
10039
+ return join11(pluginRoot(configDir), STATE_FILE);
10267
10040
  }
10268
10041
  function loadState(configDir) {
10269
10042
  const file = statePath(configDir);
10270
- if (!existsSync18(file)) return { version: 2, plugins: [] };
10043
+ if (!existsSync16(file)) return { version: 2, plugins: [] };
10271
10044
  try {
10272
- const parsed = JSON.parse(readFileSync13(file, "utf-8"));
10045
+ const parsed = JSON.parse(readFileSync12(file, "utf-8"));
10273
10046
  const rawPlugins = Array.isArray(parsed.plugins) ? parsed.plugins : [];
10274
10047
  return { version: 2, plugins: rawPlugins.map((p) => migrateEntry(p)) };
10275
10048
  } catch {
@@ -10292,7 +10065,7 @@ function migrateEntry(entry) {
10292
10065
  };
10293
10066
  }
10294
10067
  function saveState(configDir, state2) {
10295
- mkdirSync9(pluginRoot(configDir), { recursive: true });
10068
+ mkdirSync8(pluginRoot(configDir), { recursive: true });
10296
10069
  atomicWriteFileSync(statePath(configDir), JSON.stringify(state2, null, 2));
10297
10070
  }
10298
10071
  function normalizeForHash(buf) {
@@ -10300,7 +10073,7 @@ function normalizeForHash(buf) {
10300
10073
  return Buffer.from(str, "utf-8");
10301
10074
  }
10302
10075
  function extractCommandPaths(command, pluginDir) {
10303
- const resolvedDir = resolve7(pluginDir);
10076
+ const resolvedDir = resolve6(pluginDir);
10304
10077
  const refs = [];
10305
10078
  for (const m of command.matchAll(/\$PLUGIN_DIR[\\/]([^"'\s]+)/g)) {
10306
10079
  refs.push(m[1]);
@@ -10317,9 +10090,9 @@ function extractCommandPaths(command, pluginDir) {
10317
10090
  if (seen.has(ref)) continue;
10318
10091
  seen.add(ref);
10319
10092
  try {
10320
- const abs = resolve7(resolvedDir, ref);
10093
+ const abs = resolve6(resolvedDir, ref);
10321
10094
  if (!abs.startsWith(resolvedDir + sep3)) continue;
10322
- if (existsSync18(abs)) {
10095
+ if (existsSync16(abs)) {
10323
10096
  const real = realpathSync2(abs);
10324
10097
  if (!real.startsWith(resolvedDir + sep3)) continue;
10325
10098
  valid.push(relative3(resolvedDir, real));
@@ -10330,7 +10103,7 @@ function extractCommandPaths(command, pluginDir) {
10330
10103
  return valid;
10331
10104
  }
10332
10105
  function hashPluginContent(pluginDir, manifest) {
10333
- const resolvedDir = resolve7(pluginDir);
10106
+ const resolvedDir = resolve6(pluginDir);
10334
10107
  const assetRels = /* @__PURE__ */ new Set(
10335
10108
  [...manifest.skills, ...manifest.commands, ...manifest.agents]
10336
10109
  );
@@ -10350,11 +10123,11 @@ function hashPluginContent(pluginDir, manifest) {
10350
10123
  }
10351
10124
  }
10352
10125
  const sorted = [...assetRels].sort();
10353
- const hasher = createHash3("sha256");
10354
- const manifestPath = join13(resolvedDir, MANIFEST_RELATIVE);
10355
- hasher.update(normalizeForHash(readFileSync13(manifestPath)));
10126
+ const hasher = createHash2("sha256");
10127
+ const manifestPath = join11(resolvedDir, MANIFEST_RELATIVE);
10128
+ hasher.update(normalizeForHash(readFileSync12(manifestPath)));
10356
10129
  for (const rel of sorted) {
10357
- const abs = resolve7(resolvedDir, rel);
10130
+ const abs = resolve6(resolvedDir, rel);
10358
10131
  if (!abs.startsWith(resolvedDir + sep3)) {
10359
10132
  throw new Error(`plugin asset escapes plugin dir: ${rel}`);
10360
10133
  }
@@ -10372,7 +10145,7 @@ function hashPluginContent(pluginDir, manifest) {
10372
10145
  if (!realPath.startsWith(resolvedDir + sep3)) {
10373
10146
  throw new Error(`plugin asset symlink escapes plugin dir: ${rel} \u2192 ${realPath}`);
10374
10147
  }
10375
- hasher.update(normalizeForHash(readFileSync13(abs)));
10148
+ hasher.update(normalizeForHash(readFileSync12(abs)));
10376
10149
  }
10377
10150
  return hasher.digest("hex");
10378
10151
  }
@@ -10380,22 +10153,22 @@ function effectiveHash(entry) {
10380
10153
  return entry.contentHash ?? entry.hash ?? "";
10381
10154
  }
10382
10155
  function readPluginManifest(manifestPath) {
10383
- return PluginManifestSchema.parse(JSON.parse(readFileSync13(manifestPath, "utf-8")));
10156
+ return PluginManifestSchema.parse(JSON.parse(readFileSync12(manifestPath, "utf-8")));
10384
10157
  }
10385
10158
  function findPluginSourceDir(inputPath) {
10386
- const abs = resolve7(inputPath);
10387
- const direct = statSync7(abs).isDirectory() ? abs : dirname8(abs);
10388
- if (existsSync18(pluginManifestPath(direct))) return direct;
10389
- const nested = join13(direct, ".aicli-plugin");
10390
- if (existsSync18(join13(nested, "plugin.json"))) return direct;
10159
+ const abs = resolve6(inputPath);
10160
+ const direct = statSync7(abs).isDirectory() ? abs : dirname7(abs);
10161
+ if (existsSync16(pluginManifestPath(direct))) return direct;
10162
+ const nested = join11(direct, ".aicli-plugin");
10163
+ if (existsSync16(join11(nested, "plugin.json"))) return direct;
10391
10164
  throw new Error(`plugin manifest not found: ${MANIFEST_RELATIVE}`);
10392
10165
  }
10393
10166
  function installPlugin(configDir, inputPath) {
10394
10167
  const sourceDir = findPluginSourceDir(inputPath);
10395
10168
  const manifest = readPluginManifest(pluginManifestPath(sourceDir));
10396
- const targetDir = join13(pluginRoot(configDir), manifest.name);
10397
- mkdirSync9(pluginRoot(configDir), { recursive: true });
10398
- if (existsSync18(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10169
+ const targetDir = join11(pluginRoot(configDir), manifest.name);
10170
+ mkdirSync8(pluginRoot(configDir), { recursive: true });
10171
+ if (existsSync16(targetDir)) rmSync(targetDir, { recursive: true, force: true });
10399
10172
  cpSync(sourceDir, targetDir, { recursive: true });
10400
10173
  const manifestPath = pluginManifestPath(targetDir);
10401
10174
  const contentHash = hashPluginContent(targetDir, manifest);
@@ -10453,18 +10226,18 @@ function getInstalledPlugin(configDir, name) {
10453
10226
  }
10454
10227
  function listInstalledPlugins(configDir) {
10455
10228
  const root = pluginRoot(configDir);
10456
- if (!existsSync18(root)) return [];
10229
+ if (!existsSync16(root)) return [];
10457
10230
  const state2 = loadState(configDir);
10458
10231
  const out = [];
10459
10232
  for (const entry of readdirSync7(root)) {
10460
- const dir = join13(root, entry);
10233
+ const dir = join11(root, entry);
10461
10234
  try {
10462
10235
  if (!statSync7(dir).isDirectory()) continue;
10463
10236
  } catch {
10464
10237
  continue;
10465
10238
  }
10466
10239
  const manifestPath = pluginManifestPath(dir);
10467
- if (!existsSync18(manifestPath)) continue;
10240
+ if (!existsSync16(manifestPath)) continue;
10468
10241
  try {
10469
10242
  const manifest = readPluginManifest(manifestPath);
10470
10243
  const contentHash = hashPluginContent(dir, manifest);
@@ -10502,9 +10275,9 @@ function activePlugins(configDir) {
10502
10275
  function uniqueDirs(plugin, rels) {
10503
10276
  const dirs = /* @__PURE__ */ new Set();
10504
10277
  for (const rel of rels) {
10505
- const abs = resolve7(plugin.dir, rel);
10506
- if (!abs.startsWith(resolve7(plugin.dir))) continue;
10507
- if (existsSync18(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname8(abs));
10278
+ const abs = resolve6(plugin.dir, rel);
10279
+ if (!abs.startsWith(resolve6(plugin.dir))) continue;
10280
+ if (existsSync16(abs)) dirs.add(statSync7(abs).isDirectory() ? abs : dirname7(abs));
10508
10281
  }
10509
10282
  return [...dirs];
10510
10283
  }
@@ -10667,13 +10440,13 @@ function parseAgentConfig(raw, source, path3) {
10667
10440
  return cfg;
10668
10441
  }
10669
10442
  function loadAgentDir(dir, source) {
10670
- if (!existsSync19(dir)) return [];
10443
+ if (!existsSync17(dir)) return [];
10671
10444
  const out = [];
10672
10445
  for (const file of readdirSync8(dir)) {
10673
10446
  if (!file.endsWith(".json")) continue;
10674
- const path3 = join14(dir, file);
10447
+ const path3 = join12(dir, file);
10675
10448
  try {
10676
- const parsed = JSON.parse(readFileSync14(path3, "utf-8"));
10449
+ const parsed = JSON.parse(readFileSync13(path3, "utf-8"));
10677
10450
  const cfg = parseAgentConfig(parsed, source, path3);
10678
10451
  if (cfg) out.push(cfg);
10679
10452
  } catch {
@@ -10683,8 +10456,8 @@ function loadAgentDir(dir, source) {
10683
10456
  }
10684
10457
  function getAgentDirs(configDir, cwd = process.cwd()) {
10685
10458
  return {
10686
- user: join14(configDir ?? join14(homedir6(), CONFIG_DIR_NAME), "agents"),
10687
- project: join14(cwd, CONFIG_DIR_NAME, "agents")
10459
+ user: join12(configDir ?? join12(homedir6(), CONFIG_DIR_NAME), "agents"),
10460
+ project: join12(cwd, CONFIG_DIR_NAME, "agents")
10688
10461
  };
10689
10462
  }
10690
10463
  function listAgentConfigs(configDir, cwd = process.cwd()) {
@@ -10731,7 +10504,7 @@ var nextId = 1;
10731
10504
  var preferredAgentName = "worker";
10732
10505
  var runs = [];
10733
10506
  var MAX_RUNS = 50;
10734
- function nowIso2() {
10507
+ function nowIso() {
10735
10508
  return (/* @__PURE__ */ new Date()).toISOString();
10736
10509
  }
10737
10510
  function startAgentRun(input) {
@@ -10740,7 +10513,7 @@ function startAgentRun(input) {
10740
10513
  agentName: input.agentName,
10741
10514
  task: input.task,
10742
10515
  status: "running",
10743
- startedAt: nowIso2(),
10516
+ startedAt: nowIso(),
10744
10517
  toolNames: [],
10745
10518
  agentIndex: input.agentIndex,
10746
10519
  stopRequested: false
@@ -10758,7 +10531,7 @@ function finishAgentRun(runId, status, summary, error) {
10758
10531
  const run = runs.find((r) => r.id === runId);
10759
10532
  if (!run) return;
10760
10533
  run.status = status;
10761
- run.finishedAt = nowIso2();
10534
+ run.finishedAt = nowIso();
10762
10535
  if (summary) run.summary = summary;
10763
10536
  if (error) run.error = error;
10764
10537
  }
@@ -11184,7 +10957,7 @@ var spawnAgentTool = {
11184
10957
 
11185
10958
  // src/tools/builtin/task-manager.ts
11186
10959
  import { spawn as spawn4 } from "child_process";
11187
- import { randomUUID as randomUUID2 } from "crypto";
10960
+ import { randomUUID } from "crypto";
11188
10961
  import { platform as platform5 } from "os";
11189
10962
  var MAX_OUTPUT_CHARS = 1e4;
11190
10963
  var MAX_OUTPUT_BYTES = 64 * 1024;
@@ -11217,7 +10990,7 @@ function createTask(command, description) {
11217
10990
  }
11218
10991
  }
11219
10992
  }
11220
- const id = randomUUID2().slice(0, 8);
10993
+ const id = randomUUID().slice(0, 8);
11221
10994
  const { shell, args } = shellInvocation(command);
11222
10995
  const proc = spawn4(shell, args, {
11223
10996
  stdio: ["ignore", "pipe", "pipe"],
@@ -11399,20 +11172,20 @@ var taskStopTool = {
11399
11172
 
11400
11173
  // src/tools/builtin/git-tools.ts
11401
11174
  import { execFileSync as execFileSync2 } from "child_process";
11402
- import { existsSync as existsSync20 } from "fs";
11403
- import { join as join15 } from "path";
11175
+ import { existsSync as existsSync18 } from "fs";
11176
+ import { join as join13 } from "path";
11404
11177
  function assertGitRepo(cwd) {
11405
11178
  let dir = cwd;
11406
11179
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
11407
11180
  while (dir && dir !== root) {
11408
- if (existsSync20(join15(dir, ".git"))) return;
11409
- const parent = join15(dir, "..");
11181
+ if (existsSync18(join13(dir, ".git"))) return;
11182
+ const parent = join13(dir, "..");
11410
11183
  if (parent === dir) break;
11411
11184
  dir = parent;
11412
11185
  }
11413
11186
  throw new ToolError("git", "Not inside a git repository. Run this command from a directory under a git-tracked project.");
11414
11187
  }
11415
- function runGit2(args, cwd, maxBuffer = 10 * 1024 * 1024) {
11188
+ function runGit(args, cwd, maxBuffer = 10 * 1024 * 1024) {
11416
11189
  try {
11417
11190
  const output = execFileSync2("git", args, {
11418
11191
  cwd,
@@ -11447,11 +11220,11 @@ var gitStatusTool = {
11447
11220
  assertGitRepo(cwd);
11448
11221
  let branch = "(unknown)";
11449
11222
  try {
11450
- branch = runGit2(["symbolic-ref", "--short", "HEAD"], cwd).trim();
11223
+ branch = runGit(["symbolic-ref", "--short", "HEAD"], cwd).trim();
11451
11224
  } catch {
11452
11225
  branch = "(detached)";
11453
11226
  }
11454
- const porcelain = runGit2(["status", "--porcelain=v1", "-b"], cwd);
11227
+ const porcelain = runGit(["status", "--porcelain=v1", "-b"], cwd);
11455
11228
  const lines = porcelain.split("\n").filter((l) => l.length > 0);
11456
11229
  const branchLine = lines[0]?.startsWith("##") ? lines[0].slice(3) : `${branch}`;
11457
11230
  const fileLines = lines.slice(lines[0]?.startsWith("##") ? 1 : 0);
@@ -11538,7 +11311,7 @@ var gitDiffTool = {
11538
11311
  if (args["file"]) {
11539
11312
  gitArgs.push("--", String(args["file"]));
11540
11313
  }
11541
- const output = runGit2(gitArgs, cwd);
11314
+ const output = runGit(gitArgs, cwd);
11542
11315
  if (output.trim().length === 0) {
11543
11316
  const label = args["staged"] ? "staged" : args["commit_range"] ? String(args["commit_range"]) : "unstaged";
11544
11317
  return `No ${label} changes.`;
@@ -11600,7 +11373,7 @@ var gitLogTool = {
11600
11373
  if (args["file"]) {
11601
11374
  gitArgs.push("--", String(args["file"]));
11602
11375
  }
11603
- const output = runGit2(gitArgs, cwd);
11376
+ const output = runGit(gitArgs, cwd);
11604
11377
  if (output.trim().length === 0) {
11605
11378
  return "No commits found matching the filter.";
11606
11379
  }
@@ -11647,9 +11420,9 @@ var gitCommitTool = {
11647
11420
  throw new ToolError("git_commit", "Cannot use both stage_all and files at the same time.");
11648
11421
  }
11649
11422
  if (files.length > 0) {
11650
- runGit2(["add", "--", ...files], cwd);
11423
+ runGit(["add", "--", ...files], cwd);
11651
11424
  }
11652
- const statusBefore = runGit2(["status", "--porcelain"], cwd);
11425
+ const statusBefore = runGit(["status", "--porcelain"], cwd);
11653
11426
  const hasStaged = statusBefore.split("\n").some((l) => l.length > 0 && l[0] !== " " && l[0] !== "?");
11654
11427
  const hasUnstaged = statusBefore.split("\n").some((l) => l.length > 1 && l[1] !== " " && l[1] !== "?");
11655
11428
  if (!hasStaged && !stageAll) {
@@ -11660,9 +11433,9 @@ var gitCommitTool = {
11660
11433
  }
11661
11434
  const commitArgs = ["commit", "-m", message];
11662
11435
  if (stageAll) commitArgs.splice(1, 0, "-a");
11663
- const commitOutput = runGit2(commitArgs, cwd);
11664
- const hash = runGit2(["rev-parse", "--short", "HEAD"], cwd).trim();
11665
- const summary = runGit2(["log", "-1", "--pretty=format:%h %s"], cwd).trim();
11436
+ const commitOutput = runGit(commitArgs, cwd);
11437
+ const hash = runGit(["rev-parse", "--short", "HEAD"], cwd).trim();
11438
+ const summary = runGit(["log", "-1", "--pretty=format:%h %s"], cwd).trim();
11666
11439
  return `\u2713 Committed ${hash}
11667
11440
  ${summary}
11668
11441
 
@@ -11671,9 +11444,9 @@ ${commitOutput.trim()}`;
11671
11444
  };
11672
11445
 
11673
11446
  // src/tools/builtin/notebook-edit.ts
11674
- import { readFileSync as readFileSync15, existsSync as existsSync21 } from "fs";
11447
+ import { readFileSync as readFileSync14, existsSync as existsSync19 } from "fs";
11675
11448
  import { writeFile } from "fs/promises";
11676
- import { resolve as resolve8, extname as extname2 } from "path";
11449
+ import { resolve as resolve7, extname as extname2 } from "path";
11677
11450
  var notebookEditTool = {
11678
11451
  definition: {
11679
11452
  name: "notebook_edit",
@@ -11722,14 +11495,14 @@ var notebookEditTool = {
11722
11495
  if (!Number.isInteger(cellIndexRaw) || cellIndexRaw < 1) {
11723
11496
  throw new ToolError("notebook_edit", "cell_index must be a positive integer (1-based)");
11724
11497
  }
11725
- const absPath = resolve8(filePath);
11498
+ const absPath = resolve7(filePath);
11726
11499
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
11727
11500
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
11728
11501
  }
11729
- if (!existsSync21(absPath)) {
11502
+ if (!existsSync19(absPath)) {
11730
11503
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
11731
11504
  }
11732
- const raw = readFileSync15(absPath, "utf-8");
11505
+ const raw = readFileSync14(absPath, "utf-8");
11733
11506
  const nb = parseNotebook(raw);
11734
11507
  const cellIdx0 = cellIndexRaw - 1;
11735
11508
  undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
@@ -12146,8 +11919,8 @@ function estimateToolDefinitionTokens(def) {
12146
11919
 
12147
11920
  // src/tools/registry.ts
12148
11921
  import { pathToFileURL } from "url";
12149
- import { existsSync as existsSync22, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "fs";
12150
- import { join as join16 } from "path";
11922
+ import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync9 } from "fs";
11923
+ import { join as join14 } from "path";
12151
11924
  var ToolRegistry = class {
12152
11925
  tools = /* @__PURE__ */ new Map();
12153
11926
  pluginToolNames = /* @__PURE__ */ new Set();
@@ -12308,9 +12081,9 @@ var ToolRegistry = class {
12308
12081
  * Returns the number of successfully loaded plugins.
12309
12082
  */
12310
12083
  async loadPlugins(pluginsDir, allowPlugins = false) {
12311
- if (!existsSync22(pluginsDir)) {
12084
+ if (!existsSync20(pluginsDir)) {
12312
12085
  try {
12313
- mkdirSync10(pluginsDir, { recursive: true });
12086
+ mkdirSync9(pluginsDir, { recursive: true });
12314
12087
  } catch {
12315
12088
  }
12316
12089
  return 0;
@@ -12334,12 +12107,12 @@ var ToolRegistry = class {
12334
12107
  process.stderr.write(
12335
12108
  `
12336
12109
  [plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
12337
- ` + files.map((f) => ` + ${join16(pluginsDir, f)}`).join("\n") + "\n\n"
12110
+ ` + files.map((f) => ` + ${join14(pluginsDir, f)}`).join("\n") + "\n\n"
12338
12111
  );
12339
12112
  let loaded = 0;
12340
12113
  for (const file of files) {
12341
12114
  try {
12342
- const fileUrl = pathToFileURL(join16(pluginsDir, file)).href;
12115
+ const fileUrl = pathToFileURL(join14(pluginsDir, file)).href;
12343
12116
  const mod = await import(fileUrl);
12344
12117
  const tool = mod.tool ?? mod.default?.tool ?? mod.default;
12345
12118
  if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
@@ -12501,7 +12274,7 @@ var McpClient = class {
12501
12274
  // 内部方法:JSON-RPC 通信
12502
12275
  // ══════════════════════════════════════════════════════════════════
12503
12276
  sendRequest(method, params) {
12504
- return new Promise((resolve14, reject) => {
12277
+ return new Promise((resolve13, reject) => {
12505
12278
  if (!this.process?.stdin?.writable) {
12506
12279
  return reject(new Error(`MCP server [${this.serverId}] stdin not writable`));
12507
12280
  }
@@ -12525,7 +12298,7 @@ var McpClient = class {
12525
12298
  this.pendingRequests.set(id, {
12526
12299
  resolve: (result) => {
12527
12300
  cleanup();
12528
- resolve14(result);
12301
+ resolve13(result);
12529
12302
  },
12530
12303
  reject: (error) => {
12531
12304
  cleanup();
@@ -12602,13 +12375,13 @@ var McpClient = class {
12602
12375
  }
12603
12376
  /** Promise 超时包装 */
12604
12377
  withTimeout(promise, ms, label) {
12605
- return new Promise((resolve14, reject) => {
12378
+ return new Promise((resolve13, reject) => {
12606
12379
  const timer = setTimeout(() => {
12607
12380
  reject(new Error(`MCP [${this.serverId}] ${label} timed out after ${ms}ms`));
12608
12381
  }, ms);
12609
12382
  promise.then((val) => {
12610
12383
  clearTimeout(timer);
12611
- resolve14(val);
12384
+ resolve13(val);
12612
12385
  }).catch((err) => {
12613
12386
  clearTimeout(timer);
12614
12387
  reject(err);
@@ -12885,11 +12658,11 @@ var McpManager = class {
12885
12658
  };
12886
12659
 
12887
12660
  // src/skills/manager.ts
12888
- import { existsSync as existsSync23, readdirSync as readdirSync10, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
12889
- import { join as join17 } from "path";
12661
+ import { existsSync as existsSync21, readdirSync as readdirSync10, mkdirSync as mkdirSync10, statSync as statSync8 } from "fs";
12662
+ import { join as join15 } from "path";
12890
12663
 
12891
12664
  // src/skills/types.ts
12892
- import { readFileSync as readFileSync16 } from "fs";
12665
+ import { readFileSync as readFileSync15 } from "fs";
12893
12666
  import { basename as basename7 } from "path";
12894
12667
  function parseSimpleYaml(yaml) {
12895
12668
  const result = {};
@@ -12911,7 +12684,7 @@ function parseYamlArray(value) {
12911
12684
  function parseSkillFile(filePath) {
12912
12685
  let raw;
12913
12686
  try {
12914
- raw = readFileSync16(filePath, "utf-8");
12687
+ raw = readFileSync15(filePath, "utf-8");
12915
12688
  } catch {
12916
12689
  return null;
12917
12690
  }
@@ -12959,10 +12732,10 @@ var SkillManager = class {
12959
12732
  return this.skills.size;
12960
12733
  }
12961
12734
  loadSkillDir(dir, createIfMissing) {
12962
- if (!existsSync23(dir)) {
12735
+ if (!existsSync21(dir)) {
12963
12736
  if (createIfMissing) {
12964
12737
  try {
12965
- mkdirSync11(dir, { recursive: true });
12738
+ mkdirSync10(dir, { recursive: true });
12966
12739
  } catch {
12967
12740
  }
12968
12741
  }
@@ -12976,14 +12749,14 @@ var SkillManager = class {
12976
12749
  }
12977
12750
  for (const entry of entries) {
12978
12751
  let filePath;
12979
- const fullPath = join17(dir, entry);
12752
+ const fullPath = join15(dir, entry);
12980
12753
  if (entry.endsWith(".md")) {
12981
12754
  filePath = fullPath;
12982
12755
  } else {
12983
12756
  try {
12984
12757
  if (statSync8(fullPath).isDirectory()) {
12985
- const skillMd = join17(fullPath, "SKILL.md");
12986
- if (existsSync23(skillMd)) filePath = skillMd;
12758
+ const skillMd = join15(fullPath, "SKILL.md");
12759
+ if (existsSync21(skillMd)) filePath = skillMd;
12987
12760
  else continue;
12988
12761
  } else {
12989
12762
  continue;
@@ -13040,9 +12813,9 @@ var SkillManager = class {
13040
12813
  };
13041
12814
 
13042
12815
  // src/web/tool-executor-web.ts
13043
- import { randomUUID as randomUUID3 } from "crypto";
12816
+ import { randomUUID as randomUUID2 } from "crypto";
13044
12817
  import { tmpdir as tmpdir2 } from "os";
13045
- import { existsSync as existsSync24, readFileSync as readFileSync17 } from "fs";
12818
+ import { existsSync as existsSync22, readFileSync as readFileSync16 } from "fs";
13046
12819
  var ToolExecutorWeb = class _ToolExecutorWeb {
13047
12820
  constructor(registry, ws) {
13048
12821
  this.registry = registry;
@@ -13112,33 +12885,33 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13112
12885
  }
13113
12886
  /** Resolve a pending confirm from client response */
13114
12887
  resolveConfirm(requestId, approved) {
13115
- const resolve14 = this.pendingConfirms.get(requestId);
13116
- if (resolve14) {
12888
+ const resolve13 = this.pendingConfirms.get(requestId);
12889
+ if (resolve13) {
13117
12890
  this.clearPendingTimer(requestId);
13118
12891
  this.pendingConfirms.delete(requestId);
13119
12892
  this.confirming = false;
13120
- resolve14(approved);
12893
+ resolve13(approved);
13121
12894
  }
13122
12895
  }
13123
12896
  /** Resolve a pending batch confirm from client response */
13124
12897
  resolveBatchConfirm(requestId, decision) {
13125
- const resolve14 = this.pendingBatchConfirms.get(requestId);
13126
- if (resolve14) {
12898
+ const resolve13 = this.pendingBatchConfirms.get(requestId);
12899
+ if (resolve13) {
13127
12900
  this.clearPendingTimer(requestId);
13128
12901
  this.pendingBatchConfirms.delete(requestId);
13129
12902
  this.confirming = false;
13130
12903
  if (decision === "all" || decision === "none") {
13131
- resolve14(decision);
12904
+ resolve13(decision);
13132
12905
  } else {
13133
- resolve14(new Set(decision));
12906
+ resolve13(new Set(decision));
13134
12907
  }
13135
12908
  }
13136
12909
  }
13137
12910
  /** Cancel all pending confirms (e.g., on disconnect) */
13138
12911
  cancelAll() {
13139
- for (const resolve14 of this.pendingConfirms.values()) resolve14(false);
12912
+ for (const resolve13 of this.pendingConfirms.values()) resolve13(false);
13140
12913
  this.pendingConfirms.clear();
13141
- for (const resolve14 of this.pendingBatchConfirms.values()) resolve14("none");
12914
+ for (const resolve13 of this.pendingBatchConfirms.values()) resolve13("none");
13142
12915
  this.pendingBatchConfirms.clear();
13143
12916
  this.confirming = false;
13144
12917
  }
@@ -13181,9 +12954,9 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13181
12954
  if (call.name === "write_file") {
13182
12955
  const filePath = String(call.arguments["path"] ?? "");
13183
12956
  const newContent = String(call.arguments["content"] ?? "");
13184
- if (filePath && existsSync24(filePath)) {
12957
+ if (filePath && existsSync22(filePath)) {
13185
12958
  try {
13186
- const old = readFileSync17(filePath, "utf-8");
12959
+ const old = readFileSync16(filePath, "utf-8");
13187
12960
  return renderDiff(old, newContent, { filePath });
13188
12961
  } catch {
13189
12962
  }
@@ -13203,7 +12976,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13203
12976
  }
13204
12977
  /** WebSocket-based confirm — sends request, returns Promise resolved by client */
13205
12978
  confirm(call, level) {
13206
- const requestId = randomUUID3();
12979
+ const requestId = randomUUID2();
13207
12980
  this.confirming = true;
13208
12981
  const msg = {
13209
12982
  type: "confirm_request",
@@ -13214,8 +12987,8 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13214
12987
  diff: this.getDiffPreview(call)
13215
12988
  };
13216
12989
  this.send(msg);
13217
- return new Promise((resolve14) => {
13218
- this.pendingConfirms.set(requestId, resolve14);
12990
+ return new Promise((resolve13) => {
12991
+ this.pendingConfirms.set(requestId, resolve13);
13219
12992
  this.pendingTimers.set(requestId, setTimeout(() => {
13220
12993
  if (this.pendingConfirms.has(requestId)) {
13221
12994
  this.resolveConfirm(requestId, false);
@@ -13225,7 +12998,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13225
12998
  }
13226
12999
  /** WebSocket-based batch confirm */
13227
13000
  batchConfirm(calls) {
13228
- const requestId = randomUUID3();
13001
+ const requestId = randomUUID2();
13229
13002
  this.confirming = true;
13230
13003
  const files = calls.map((call, i) => ({
13231
13004
  index: i + 1,
@@ -13239,8 +13012,8 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13239
13012
  files
13240
13013
  };
13241
13014
  this.send(msg);
13242
- return new Promise((resolve14) => {
13243
- this.pendingBatchConfirms.set(requestId, resolve14);
13015
+ return new Promise((resolve13) => {
13016
+ this.pendingBatchConfirms.set(requestId, resolve13);
13244
13017
  this.pendingTimers.set(requestId, setTimeout(() => {
13245
13018
  if (this.pendingBatchConfirms.has(requestId)) {
13246
13019
  this.resolveBatchConfirm(requestId, "none");
@@ -13487,16 +13260,16 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
13487
13260
  };
13488
13261
 
13489
13262
  // src/repl/dev-state.ts
13490
- import { existsSync as existsSync25, readFileSync as readFileSync18, unlinkSync as unlinkSync5, mkdirSync as mkdirSync12 } from "fs";
13491
- import { join as join18 } from "path";
13263
+ import { existsSync as existsSync23, readFileSync as readFileSync17, unlinkSync as unlinkSync5, mkdirSync as mkdirSync11 } from "fs";
13264
+ import { join as join16 } from "path";
13492
13265
  import { homedir as homedir7 } from "os";
13493
13266
  function getDevStatePath() {
13494
- return join18(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
13267
+ return join16(homedir7(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
13495
13268
  }
13496
13269
  function loadDevState() {
13497
13270
  const path3 = getDevStatePath();
13498
- if (!existsSync25(path3)) return null;
13499
- const content = readFileSync18(path3, "utf-8").trim();
13271
+ if (!existsSync23(path3)) return null;
13272
+ const content = readFileSync17(path3, "utf-8").trim();
13500
13273
  return content || null;
13501
13274
  }
13502
13275
 
@@ -13521,7 +13294,7 @@ Working directory: ${process.cwd()}`;
13521
13294
  }
13522
13295
  stableParts.push(AGENTIC_BEHAVIOR_GUIDELINE);
13523
13296
  if (ctx.configDir) {
13524
- const memory = loadMemoryContent(ctx.configDir);
13297
+ const memory = loadMemoryContent(ctx.configDir, ctx.memoryMaxChars);
13525
13298
  if (memory) stableParts.push(`# Persistent Memory
13526
13299
 
13527
13300
  ${memory.content}`);
@@ -13553,8 +13326,8 @@ ${ctx.activeSkill.content}`);
13553
13326
  if (ctx.planMode) stableParts.push(PLAN_MODE_SYSTEM_ADDON);
13554
13327
  return { stable: stableParts.join("\n\n---\n\n"), volatile };
13555
13328
  }
13556
- function loadMemoryContent(configDir) {
13557
- return formatMemoryForPrompt(configDir, process.cwd());
13329
+ function loadMemoryContent(configDir, maxChars) {
13330
+ return formatMemoryForPrompt(configDir, process.cwd(), maxChars);
13558
13331
  }
13559
13332
 
13560
13333
  // src/core/pricing.ts
@@ -13753,8 +13526,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
13753
13526
  }
13754
13527
 
13755
13528
  // src/core/context-files.ts
13756
- import { existsSync as existsSync26, readFileSync as readFileSync19 } from "fs";
13757
- import { join as join19, relative as relative4, resolve as resolve9 } from "path";
13529
+ import { existsSync as existsSync24, readFileSync as readFileSync18 } from "fs";
13530
+ import { join as join17, relative as relative4, resolve as resolve8 } from "path";
13758
13531
  function uniqueNonEmpty(values) {
13759
13532
  const seen = /* @__PURE__ */ new Set();
13760
13533
  const out = [];
@@ -13777,14 +13550,14 @@ function displayPath(filePath, cwd) {
13777
13550
  return filePath;
13778
13551
  }
13779
13552
  function isInsideOrEqual(parent, child) {
13780
- const rel = relative4(resolve9(parent), resolve9(child));
13553
+ const rel = relative4(resolve8(parent), resolve8(child));
13781
13554
  return rel === "" || !!rel && !rel.startsWith("..") && !rel.startsWith("/") && !rel.startsWith("\\");
13782
13555
  }
13783
13556
  function readContextFile(level, filePath, cwd, maxBytes) {
13784
13557
  const name = filePath.split(/[\\/]/).pop() ?? filePath;
13785
13558
  const shown = displayPath(filePath, cwd);
13786
13559
  try {
13787
- const raw = readFileSync19(filePath);
13560
+ const raw = readFileSync18(filePath);
13788
13561
  const byteCount = raw.byteLength;
13789
13562
  if (byteCount === 0) {
13790
13563
  return {
@@ -13831,8 +13604,8 @@ function readContextFile(level, filePath, cwd, maxBytes) {
13831
13604
  function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
13832
13605
  const skipped = [];
13833
13606
  for (const candidate of candidates) {
13834
- const filePath = join19(dir, candidate);
13835
- if (!existsSync26(filePath)) continue;
13607
+ const filePath = join17(dir, candidate);
13608
+ if (!existsSync24(filePath)) continue;
13836
13609
  const result = readContextFile(level, filePath, cwd, maxBytes);
13837
13610
  if (result.skipped) skipped.push(result.skipped);
13838
13611
  if (result.layer) return { layer: result.layer, skipped };
@@ -13840,7 +13613,7 @@ function findFirstContextFile(level, dir, cwd, candidates, maxBytes) {
13840
13613
  return { layer: null, skipped };
13841
13614
  }
13842
13615
  function loadContextFiles(options) {
13843
- const cwd = resolve9(options.cwd);
13616
+ const cwd = resolve8(options.cwd);
13844
13617
  const maxBytes = options.maxBytes ?? CONTEXT_FILE_MAX_BYTES;
13845
13618
  const candidates = options.candidates ?? buildContextFileCandidates(options.fallbackFilenames);
13846
13619
  const skipped = [];
@@ -13848,7 +13621,7 @@ function loadContextFiles(options) {
13848
13621
  return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
13849
13622
  }
13850
13623
  if (options.setting && options.setting !== "auto") {
13851
- const filePath = resolve9(cwd, String(options.setting));
13624
+ const filePath = resolve8(cwd, String(options.setting));
13852
13625
  if (!isInsideOrEqual(cwd, filePath)) {
13853
13626
  skipped.push({
13854
13627
  level: "single",
@@ -13859,7 +13632,7 @@ function loadContextFiles(options) {
13859
13632
  });
13860
13633
  return { layers: [], mergedContent: "", skipped, totalChars: 0, totalBytes: 0, maxBytes, candidates };
13861
13634
  }
13862
- if (!existsSync26(filePath)) {
13635
+ if (!existsSync24(filePath)) {
13863
13636
  skipped.push({
13864
13637
  level: "single",
13865
13638
  filePath,
@@ -13892,7 +13665,7 @@ function loadContextFiles(options) {
13892
13665
  skipped.push(...result.skipped);
13893
13666
  if (result.layer) layers.push(result.layer);
13894
13667
  }
13895
- if (resolve9(options.cwd) !== resolve9(options.projectRoot)) {
13668
+ if (resolve8(options.cwd) !== resolve8(options.projectRoot)) {
13896
13669
  const result = findFirstContextFile("local", options.cwd, cwd, candidates, maxBytes);
13897
13670
  skipped.push(...result.skipped);
13898
13671
  if (result.layer) layers.push(result.layer);
@@ -13910,12 +13683,12 @@ function loadContextFiles(options) {
13910
13683
  }
13911
13684
 
13912
13685
  // src/web/session-handler.ts
13913
- import { mkdirSync as mkdirSync15, createWriteStream as createWriteStream2 } from "fs";
13914
- import { join as join26, dirname as dirname10 } from "path";
13686
+ import { mkdirSync as mkdirSync14, createWriteStream as createWriteStream2 } from "fs";
13687
+ import { dirname as dirname9 } from "path";
13915
13688
 
13916
13689
  // src/repl/commands/project-init.ts
13917
- import { existsSync as existsSync27, readFileSync as readFileSync20, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
13918
- import { join as join20 } from "path";
13690
+ import { existsSync as existsSync25, readFileSync as readFileSync19, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
13691
+ import { join as join18 } from "path";
13919
13692
  var SCAN_SKIP_DIRS = /* @__PURE__ */ new Set([
13920
13693
  "node_modules",
13921
13694
  ".git",
@@ -13959,11 +13732,11 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
13959
13732
  const sorted = filtered.sort((a, b) => {
13960
13733
  let aIsDir = false, bIsDir = false;
13961
13734
  try {
13962
- aIsDir = statSync9(join20(d, a)).isDirectory();
13735
+ aIsDir = statSync9(join18(d, a)).isDirectory();
13963
13736
  } catch {
13964
13737
  }
13965
13738
  try {
13966
- bIsDir = statSync9(join20(d, b)).isDirectory();
13739
+ bIsDir = statSync9(join18(d, b)).isDirectory();
13967
13740
  } catch {
13968
13741
  }
13969
13742
  if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
@@ -13971,7 +13744,7 @@ function scanDirTree(dir, maxDepth = 2, maxEntries = 80) {
13971
13744
  });
13972
13745
  for (let i = 0; i < sorted.length && count < maxEntries; i++) {
13973
13746
  const name = sorted[i];
13974
- const fullPath = join20(d, name);
13747
+ const fullPath = join18(d, name);
13975
13748
  const isLast = i === sorted.length - 1;
13976
13749
  const connector = isLast ? "+-- " : "|-- ";
13977
13750
  let isDir;
@@ -13998,7 +13771,7 @@ function scanProject(cwd) {
13998
13771
  configFiles: [],
13999
13772
  directoryStructure: ""
14000
13773
  };
14001
- const check = (file) => existsSync27(join20(cwd, file));
13774
+ const check = (file) => existsSync25(join18(cwd, file));
14002
13775
  const configCandidates = [
14003
13776
  "package.json",
14004
13777
  "tsconfig.json",
@@ -14025,7 +13798,7 @@ function scanProject(cwd) {
14025
13798
  info.type = "node";
14026
13799
  info.language = check("tsconfig.json") ? "TypeScript" : "JavaScript";
14027
13800
  try {
14028
- const pkg = JSON.parse(readFileSync20(join20(cwd, "package.json"), "utf-8"));
13801
+ const pkg = JSON.parse(readFileSync19(join18(cwd, "package.json"), "utf-8"));
14029
13802
  const scripts = pkg.scripts ?? {};
14030
13803
  info.buildCommand = scripts.build ? "npm run build" : void 0;
14031
13804
  info.testCommand = scripts.test ? "npm test" : void 0;
@@ -14674,7 +14447,7 @@ function resolveRoleProviders(roles, lookup, defaultProvider, defaultModel, avai
14674
14447
  }
14675
14448
 
14676
14449
  // src/hub/persist.ts
14677
- import { join as join21 } from "path";
14450
+ import { join as join19 } from "path";
14678
14451
  function discussionToMessages(state2) {
14679
14452
  const out = [];
14680
14453
  const t0 = state2.messages[0]?.timestamp ?? /* @__PURE__ */ new Date();
@@ -14712,7 +14485,7 @@ async function persistDiscussion(state2, config, defaultProvider, defaultModel)
14712
14485
  session.title = `[Hub] ${state2.topic.slice(0, 48)}`.replace(/\n/g, " ");
14713
14486
  session.titleAiGenerated = true;
14714
14487
  await sm.save();
14715
- return { id: session.id, path: join21(config.getHistoryDir(), `${session.id}.json`) };
14488
+ return { id: session.id, path: join19(config.getHistoryDir(), `${session.id}.json`) };
14716
14489
  }
14717
14490
 
14718
14491
  // src/web/commands/provider-commands.ts
@@ -14800,8 +14573,8 @@ async function handlePlan(args, ctx) {
14800
14573
  }
14801
14574
 
14802
14575
  // src/web/commands/_shared.ts
14803
- import { existsSync as existsSync30, readFileSync as readFileSync22, writeFileSync as writeFileSync3, mkdirSync as mkdirSync14, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
14804
- import { join as join24, resolve as resolve10, dirname as dirname9 } from "path";
14576
+ import { existsSync as existsSync28, readFileSync as readFileSync21, writeFileSync as writeFileSync3, mkdirSync as mkdirSync13, readdirSync as readdirSync13, statSync as statSync12, createWriteStream } from "fs";
14577
+ import { join as join22, resolve as resolve9, dirname as dirname8 } from "path";
14805
14578
 
14806
14579
  // src/cli/review-prompts.ts
14807
14580
  function buildReviewPrompt(diff, gitContextStr, detailed) {
@@ -14873,34 +14646,34 @@ function readGitDiff(options = {}) {
14873
14646
  }
14874
14647
 
14875
14648
  // src/diagnostics/doctor-report.ts
14876
- import { existsSync as existsSync29, statSync as statSync11 } from "fs";
14877
- import { join as join23 } from "path";
14649
+ import { existsSync as existsSync27, statSync as statSync11 } from "fs";
14650
+ import { join as join21 } from "path";
14878
14651
  import { arch as arch2, platform as platform7, release as release2 } from "os";
14879
14652
 
14880
14653
  // src/diagnostics/crash-log.ts
14881
14654
  import {
14882
- existsSync as existsSync28,
14883
- mkdirSync as mkdirSync13,
14655
+ existsSync as existsSync26,
14656
+ mkdirSync as mkdirSync12,
14884
14657
  readdirSync as readdirSync12,
14885
- readFileSync as readFileSync21,
14658
+ readFileSync as readFileSync20,
14886
14659
  statSync as statSync10,
14887
14660
  unlinkSync as unlinkSync6,
14888
14661
  writeFileSync as writeFileSync2
14889
14662
  } from "fs";
14890
- import { join as join22 } from "path";
14663
+ import { join as join20 } from "path";
14891
14664
  import { homedir as homedir8, platform as platform6, release, arch } from "os";
14892
14665
  var LOGS_DIR_NAME = "logs";
14893
14666
  var CRASH_LOG_PREFIX = "crash-";
14894
14667
  function getLogsDir(configDir) {
14895
- const base = configDir ?? join22(homedir8(), CONFIG_DIR_NAME);
14896
- return join22(base, LOGS_DIR_NAME);
14668
+ const base = configDir ?? join20(homedir8(), CONFIG_DIR_NAME);
14669
+ return join20(base, LOGS_DIR_NAME);
14897
14670
  }
14898
14671
  function truncate(s, n) {
14899
14672
  return s.length <= n ? s : s.slice(0, n) + "\u2026";
14900
14673
  }
14901
14674
  function listRecentCrashes(limit = 10, configDir) {
14902
14675
  const dir = getLogsDir(configDir);
14903
- if (!existsSync28(dir)) return [];
14676
+ if (!existsSync26(dir)) return [];
14904
14677
  const files = [];
14905
14678
  let names;
14906
14679
  try {
@@ -14910,10 +14683,10 @@ function listRecentCrashes(limit = 10, configDir) {
14910
14683
  }
14911
14684
  for (const name of names) {
14912
14685
  if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
14913
- const full = join22(dir, name);
14686
+ const full = join20(dir, name);
14914
14687
  try {
14915
14688
  const st = statSync10(full);
14916
- const head = readFileSync21(full, "utf-8").split("\n").slice(0, 20);
14689
+ const head = readFileSync20(full, "utf-8").split("\n").slice(0, 20);
14917
14690
  const tsLine = head.find((l) => l.startsWith("timestamp:"));
14918
14691
  const kindLine = head.find((l) => l.startsWith("kind:"));
14919
14692
  const msgIdx = head.findIndex((l) => l.trim() === "## message");
@@ -14932,8 +14705,8 @@ function listRecentCrashes(limit = 10, configDir) {
14932
14705
  return files.slice(0, limit);
14933
14706
  }
14934
14707
  function getConfigDirUsage(configDir) {
14935
- const base = configDir ?? join22(homedir8(), CONFIG_DIR_NAME);
14936
- if (!existsSync28(base)) return { totalBytes: 0, entries: [] };
14708
+ const base = configDir ?? join20(homedir8(), CONFIG_DIR_NAME);
14709
+ if (!existsSync26(base)) return { totalBytes: 0, entries: [] };
14937
14710
  const entries = [];
14938
14711
  let total = 0;
14939
14712
  let names;
@@ -14943,7 +14716,7 @@ function getConfigDirUsage(configDir) {
14943
14716
  return { totalBytes: 0, entries: [] };
14944
14717
  }
14945
14718
  for (const name of names) {
14946
- const full = join22(base, name);
14719
+ const full = join20(base, name);
14947
14720
  const bytes = dirSize(full);
14948
14721
  entries.push({ name, bytes });
14949
14722
  total += bytes;
@@ -14958,7 +14731,7 @@ function dirSize(path3) {
14958
14731
  if (!st.isDirectory()) return 0;
14959
14732
  let sum = 0;
14960
14733
  for (const name of readdirSync12(path3)) {
14961
- sum += dirSize(join22(path3, name));
14734
+ sum += dirSize(join20(path3, name));
14962
14735
  }
14963
14736
  return sum;
14964
14737
  } catch {
@@ -14968,7 +14741,7 @@ function dirSize(path3) {
14968
14741
 
14969
14742
  // src/diagnostics/doctor-report.ts
14970
14743
  function checkFile(label, path3) {
14971
- const exists = existsSync29(path3);
14744
+ const exists = existsSync27(path3);
14972
14745
  let sizeBytes = null;
14973
14746
  if (exists) {
14974
14747
  try {
@@ -15011,12 +14784,12 @@ function buildDoctorReport(options) {
15011
14784
  configDir,
15012
14785
  providers: options.providers.listAll().map((p) => ({ id: p.id, displayName: p.displayName, configured: p.configured })),
15013
14786
  files: [
15014
- checkFile("config.json", join23(configDir, "config.json")),
15015
- checkFile("memory.md", join23(configDir, MEMORY_FILE_NAME)),
15016
- checkFile("memory.jsonl", join23(configDir, "memory.jsonl")),
15017
- checkFile("dev-state.md", join23(configDir, DEV_STATE_FILE_NAME)),
15018
- checkFile("hooks-trust.json", join23(configDir, "hooks-trust.json")),
15019
- checkFile("plugin-state.json", join23(configDir, "plugins", "plugin-state.json"))
14787
+ checkFile("config.json", join21(configDir, "config.json")),
14788
+ checkFile("memory.md", join21(configDir, MEMORY_FILE_NAME)),
14789
+ checkFile("memory.jsonl", join21(configDir, "memory.jsonl")),
14790
+ checkFile("dev-state.md", join21(configDir, DEV_STATE_FILE_NAME)),
14791
+ checkFile("hooks-trust.json", join21(configDir, "hooks-trust.json")),
14792
+ checkFile("plugin-state.json", join21(configDir, "plugins", "plugin-state.json"))
15020
14793
  ],
15021
14794
  context: {
15022
14795
  enabled: contextSetting !== false,
@@ -15051,7 +14824,7 @@ function buildDoctorReport(options) {
15051
14824
  disabled: hooks.filter((h) => h.disabled).length
15052
14825
  },
15053
14826
  plugins: {
15054
- root: join23(configDir, "plugins"),
14827
+ root: join21(configDir, "plugins"),
15055
14828
  installed: plugins.length,
15056
14829
  active: activeAssets.plugins.length,
15057
14830
  invalid: plugins.filter((p) => !p.valid).length,
@@ -15066,7 +14839,7 @@ function buildDoctorReport(options) {
15066
14839
  mcp: {
15067
14840
  enabled: config.get("mcpEnabled"),
15068
14841
  configured: Object.keys(mcpServers).length,
15069
- projectConfigExists: existsSync29(join23(projectRoot, MCP_PROJECT_CONFIG_NAME)),
14842
+ projectConfigExists: existsSync27(join21(projectRoot, MCP_PROJECT_CONFIG_NAME)),
15070
14843
  statuses: options.mcpStatuses ?? []
15071
14844
  },
15072
14845
  recentCrashes: listRecentCrashes(5, configDir),
@@ -15292,12 +15065,20 @@ async function handleStatus(_args, ctx) {
15292
15065
  Cache: write=${cacheCreate} read=${cacheRead}` : "";
15293
15066
  const costLine = cost != null ? `
15294
15067
  Cost: ${formatCost(cost)}` : "";
15068
+ let memoryLine = "";
15069
+ try {
15070
+ const { countPendingMemories } = await import("./persistent-memory-HWFNDQFQ.js");
15071
+ const pending = countPendingMemories(ctx.config.getConfigDir());
15072
+ if (pending > 0) memoryLine = `
15073
+ Memory: \u26A0 ${pending} pending approval \u2014 see the Memory panel or /memory`;
15074
+ } catch {
15075
+ }
15295
15076
  ctx.send({
15296
15077
  type: "info",
15297
15078
  message: `Provider: ${ctx.currentProvider}
15298
15079
  Model: ${ctx.currentModel}
15299
15080
  Session: ${session?.id ?? "none"} (${session?.messages.length ?? 0} messages)
15300
- Tokens: in=${ctx.sessionTokenUsage.inputTokens} out=${ctx.sessionTokenUsage.outputTokens}${cacheLine}${costLine}`
15081
+ Tokens: in=${ctx.sessionTokenUsage.inputTokens} out=${ctx.sessionTokenUsage.outputTokens}${cacheLine}${costLine}${memoryLine}`
15301
15082
  });
15302
15083
  }
15303
15084
  async function handleSession(args, ctx) {
@@ -15593,7 +15374,7 @@ async function handleBranch(args, ctx) {
15593
15374
  return;
15594
15375
  }
15595
15376
  const sub = args[0]?.toLowerCase();
15596
- const resolve14 = (ref) => {
15377
+ const resolve13 = (ref) => {
15597
15378
  const r = session.resolveBranchRef(ref);
15598
15379
  if (r.ok) return r.id;
15599
15380
  if (r.reason === "ambiguous") {
@@ -15647,7 +15428,7 @@ async function handleBranch(args, ctx) {
15647
15428
  ctx.send({ type: "error", message: "Usage: /branch switch <id|title>" });
15648
15429
  return;
15649
15430
  }
15650
- const id = resolve14(ref);
15431
+ const id = resolve13(ref);
15651
15432
  if (!id) return;
15652
15433
  const ok = session.switchBranch(id);
15653
15434
  if (ok) {
@@ -15667,7 +15448,7 @@ async function handleBranch(args, ctx) {
15667
15448
  ctx.send({ type: "error", message: "Usage: /branch delete <id|title>" });
15668
15449
  return;
15669
15450
  }
15670
- const id = resolve14(ref);
15451
+ const id = resolve13(ref);
15671
15452
  if (!id) return;
15672
15453
  const ok = session.deleteBranch(id);
15673
15454
  if (ok) {
@@ -15686,7 +15467,7 @@ async function handleBranch(args, ctx) {
15686
15467
  ctx.send({ type: "error", message: "Usage: /branch rename <id|title> <new title>" });
15687
15468
  return;
15688
15469
  }
15689
- const id = resolve14(ref);
15470
+ const id = resolve13(ref);
15690
15471
  if (!id) return;
15691
15472
  const ok = session.renameBranch(id, title);
15692
15473
  if (ok) {
@@ -15704,7 +15485,7 @@ async function handleBranch(args, ctx) {
15704
15485
  ctx.send({ type: "error", message: "Usage: /branch diff <id|title>" });
15705
15486
  return;
15706
15487
  }
15707
- const id = resolve14(ref);
15488
+ const id = resolve13(ref);
15708
15489
  if (!id) return;
15709
15490
  const d = session.diffBranches(id);
15710
15491
  if (!d) {
@@ -15746,7 +15527,7 @@ async function handleBranch(args, ctx) {
15746
15527
  ctx.send({ type: "error", message: "Usage: /branch cherry-pick <source-id|title> <msg-index>" });
15747
15528
  return;
15748
15529
  }
15749
- const id = resolve14(ref);
15530
+ const id = resolve13(ref);
15750
15531
  if (!id) return;
15751
15532
  const idx = parseInt(idxStr, 10);
15752
15533
  if (Number.isNaN(idx)) {
@@ -15870,7 +15651,7 @@ async function handlePlugin(args, ctx) {
15870
15651
  lines.push("");
15871
15652
  }
15872
15653
  if (pluginTools.length > 0) {
15873
- const legacyDir = join24(configDir, PLUGINS_DIR_NAME);
15654
+ const legacyDir = join22(configDir, PLUGINS_DIR_NAME);
15874
15655
  lines.push(`**Legacy JS tools** (${pluginTools.length}):`);
15875
15656
  lines.push(`Dir: ${legacyDir}`);
15876
15657
  for (const t of pluginTools) lines.push(` \u2022 ${t.definition.name} \u2014 ${t.definition.description}`);
@@ -15994,8 +15775,8 @@ ${activated.meta.description || ""}` });
15994
15775
  }
15995
15776
  async function handleCommands(_args, ctx) {
15996
15777
  const configDir = ctx.config.getConfigDir();
15997
- const commandsDir = join24(configDir, CUSTOM_COMMANDS_DIR_NAME);
15998
- if (!existsSync30(commandsDir)) {
15778
+ const commandsDir = join22(configDir, CUSTOM_COMMANDS_DIR_NAME);
15779
+ if (!existsSync28(commandsDir)) {
15999
15780
  ctx.send({ type: "info", message: `No custom commands directory.
16000
15781
  Create: ${commandsDir}/ with .md files.` });
16001
15782
  return;
@@ -16095,7 +15876,7 @@ async function handleSecurityReview(args, ctx) {
16095
15876
  async function handleTest(args, ctx) {
16096
15877
  ctx.send({ type: "info", message: "\u{1F9EA} Running tests..." });
16097
15878
  try {
16098
- const { executeTests } = await import("./run-tests-EWMU34A4.js");
15879
+ const { executeTests } = await import("./run-tests-B3FSE5NV.js");
16099
15880
  const argStr = args.join(" ").trim();
16100
15881
  let testArgs = {};
16101
15882
  if (argStr) {
@@ -16125,9 +15906,9 @@ async function handleDiff(args, ctx) {
16125
15906
  let modifiedFiles = 0;
16126
15907
  const diffLines2 = [];
16127
15908
  for (const [filePath, { earliest }] of fileMap) {
16128
- const currentContent = existsSync30(filePath) ? (() => {
15909
+ const currentContent = existsSync28(filePath) ? (() => {
16129
15910
  try {
16130
- return readFileSync22(filePath, "utf-8");
15911
+ return readFileSync21(filePath, "utf-8");
16131
15912
  } catch {
16132
15913
  return null;
16133
15914
  }
@@ -16162,9 +15943,9 @@ async function handleDiff(args, ctx) {
16162
15943
  }
16163
15944
  async function handleInit(args, ctx) {
16164
15945
  const cwd = process.cwd();
16165
- const targetPath = join24(cwd, "AICLI.md");
15946
+ const targetPath = join22(cwd, "AICLI.md");
16166
15947
  const force = args.includes("--force");
16167
- if (existsSync30(targetPath) && !force) {
15948
+ if (existsSync28(targetPath) && !force) {
16168
15949
  ctx.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
16169
15950
  Use /init --force to overwrite.` });
16170
15951
  return;
@@ -16326,9 +16107,58 @@ async function handleIndex(args, ctx) {
16326
16107
  async function handleMemory(args, ctx) {
16327
16108
  const sub = args[0];
16328
16109
  if (sub === "add" && args.length > 1) {
16329
- ctx.memoryAdd(args.slice(1).join(" "));
16110
+ const rest = args.slice(1);
16111
+ const opts = {};
16112
+ const text = [];
16113
+ for (let i = 0; i < rest.length; i++) {
16114
+ if (rest[i] === "--scope" && rest[i + 1]) {
16115
+ opts.scope = rest[++i];
16116
+ } else if (rest[i] === "--sensitivity" && rest[i + 1]) {
16117
+ opts.sensitivity = rest[++i];
16118
+ } else if (rest[i] === "--expires-at" && rest[i + 1]) {
16119
+ opts.expiresAt = rest[++i];
16120
+ } else text.push(rest[i]);
16121
+ }
16122
+ if (text.length === 0) {
16123
+ ctx.send({ type: "info", message: "Usage: /memory add [--scope personal|project] [--sensitivity low|medium|high] [--expires-at ISO] <text>" });
16124
+ return;
16125
+ }
16126
+ ctx.memoryAdd(text.join(" "), opts);
16127
+ } else if (sub === "edit") {
16128
+ const id = args[1];
16129
+ const text = args.slice(2).join(" ").trim();
16130
+ if (!id || !text) {
16131
+ ctx.send({ type: "info", message: "Usage: /memory edit <id> <new text>" });
16132
+ return;
16133
+ }
16134
+ ctx.memoryEdit(id, text);
16135
+ } else if (sub === "approve" || sub === "reject" || sub === "delete" || sub === "expire") {
16136
+ if (!args[1]) {
16137
+ ctx.send({ type: "info", message: `Usage: /memory ${sub} <id>` });
16138
+ return;
16139
+ }
16140
+ ctx.handleMemoryManage(sub, args[1], sub === "expire" ? args[2] : void 0);
16141
+ } else if (sub === "export") {
16142
+ if (args[1] === "md") {
16143
+ const { exportMemoryEntries: exportMemoryEntries2 } = await import("./persistent-memory-HWFNDQFQ.js");
16144
+ ctx.send({
16145
+ type: "export_data",
16146
+ format: "md",
16147
+ filename: `aicli-memory-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.md`,
16148
+ content: exportMemoryEntries2(ctx.config.getConfigDir(), process.cwd(), "md")
16149
+ });
16150
+ } else {
16151
+ ctx.handleMemoryManage("export");
16152
+ }
16330
16153
  } else if (sub === "clear") {
16331
- ctx.memoryClear();
16154
+ if (args[1] === "confirm") {
16155
+ ctx.memoryClear();
16156
+ } else {
16157
+ ctx.send({
16158
+ type: "info",
16159
+ 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."
16160
+ });
16161
+ }
16332
16162
  } else {
16333
16163
  ctx.memoryShow();
16334
16164
  }
@@ -16347,8 +16177,8 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
16347
16177
  ctx.send({ type: "info", message: "\u2713 All added directories removed from context." });
16348
16178
  return;
16349
16179
  }
16350
- const dirPath = resolve10(sub);
16351
- if (!existsSync30(dirPath)) {
16180
+ const dirPath = resolve9(sub);
16181
+ if (!existsSync28(dirPath)) {
16352
16182
  ctx.send({ type: "error", message: `Directory not found: ${dirPath}` });
16353
16183
  return;
16354
16184
  }
@@ -16913,10 +16743,10 @@ var SessionHandler = class {
16913
16743
  return;
16914
16744
  }
16915
16745
  case "ask_user_response": {
16916
- const resolve14 = this.pendingAskUser.get(msg.requestId);
16917
- if (resolve14) {
16746
+ const resolve13 = this.pendingAskUser.get(msg.requestId);
16747
+ if (resolve13) {
16918
16748
  this.pendingAskUser.delete(msg.requestId);
16919
- resolve14(msg.answer);
16749
+ resolve13(msg.answer);
16920
16750
  }
16921
16751
  return;
16922
16752
  }
@@ -16929,10 +16759,10 @@ var SessionHandler = class {
16929
16759
  case "memory_manage":
16930
16760
  return this.handleMemoryManage(msg.action, msg.id, msg.expiresAt);
16931
16761
  case "auto_pause_response": {
16932
- const resolve14 = this.pendingAutoPause.get(msg.requestId);
16933
- if (resolve14) {
16762
+ const resolve13 = this.pendingAutoPause.get(msg.requestId);
16763
+ if (resolve13) {
16934
16764
  this.pendingAutoPause.delete(msg.requestId);
16935
- resolve14({ action: msg.action, message: msg.message });
16765
+ resolve13({ action: msg.action, message: msg.message });
16936
16766
  }
16937
16767
  return;
16938
16768
  }
@@ -16947,10 +16777,10 @@ var SessionHandler = class {
16947
16777
  this.hubOrchestrator?.abort();
16948
16778
  return;
16949
16779
  case "hub_steer": {
16950
- const resolve14 = this.pendingHubReview.get(msg.requestId);
16951
- if (resolve14) {
16780
+ const resolve13 = this.pendingHubReview.get(msg.requestId);
16781
+ if (resolve13) {
16952
16782
  this.pendingHubReview.delete(msg.requestId);
16953
- resolve14({ action: msg.action, message: msg.message });
16783
+ resolve13({ action: msg.action, message: msg.message });
16954
16784
  }
16955
16785
  return;
16956
16786
  }
@@ -16963,9 +16793,9 @@ var SessionHandler = class {
16963
16793
  runLifecycleHooks(mergePluginHooks(this.config.get("hooks") ?? void 0, this.config.getConfigDir()), "Stop", { sessionId: this.sessions.current?.id }, { configDir: this.config.getConfigDir() });
16964
16794
  this.toolExecutor.cancelAll();
16965
16795
  if (this.abortController) this.abortController.abort();
16966
- for (const resolve14 of this.pendingAskUser.values()) resolve14(null);
16796
+ for (const resolve13 of this.pendingAskUser.values()) resolve13(null);
16967
16797
  this.pendingAskUser.clear();
16968
- for (const resolve14 of this.pendingAutoPause.values()) resolve14({ action: "stop" });
16798
+ for (const resolve13 of this.pendingAutoPause.values()) resolve13({ action: "stop" });
16969
16799
  this.pendingAutoPause.clear();
16970
16800
  this.saveIfNeeded();
16971
16801
  if (this._bashSessionKey) clearBashSession(this._bashSessionKey);
@@ -17100,9 +16930,9 @@ var SessionHandler = class {
17100
16930
  this.hubOrchestrator = orchestrator;
17101
16931
  orchestrator.onEvent = (event) => this.send({ type: "hub_event", event });
17102
16932
  if (config.humanSteer) {
17103
- orchestrator.onRoundReview = ({ round, maxRounds }) => new Promise((resolve14) => {
16933
+ orchestrator.onRoundReview = ({ round, maxRounds }) => new Promise((resolve13) => {
17104
16934
  const requestId = `hubrev_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
17105
- this.pendingHubReview.set(requestId, resolve14);
16935
+ this.pendingHubReview.set(requestId, resolve13);
17106
16936
  this.send({ type: "hub_review", requestId, round, maxRounds });
17107
16937
  });
17108
16938
  }
@@ -17435,7 +17265,7 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
17435
17265
  this.send({ type: "response_done", content, usage });
17436
17266
  if (pendingTeeSave && isCleanDocumentBody(content)) {
17437
17267
  try {
17438
- mkdirSync15(dirname10(pendingTeeSave), { recursive: true });
17268
+ mkdirSync14(dirname9(pendingTeeSave), { recursive: true });
17439
17269
  const bodyToSave = stripOuterCodeFence(content);
17440
17270
  atomicWriteFileSync(pendingTeeSave, bodyToSave);
17441
17271
  undoStack.push(pendingTeeSave, `save_last_response (deferred): ${pendingTeeSave}`);
@@ -17460,8 +17290,8 @@ Try: /compact to reduce context, /clear to reset, or switch to a larger-context
17460
17290
  onMcpToolUsed: (name) => this.usedMcpToolNames.add(name),
17461
17291
  requestAutoPause: async ({ effectiveRound, maxToolRounds: totalRounds, toolSummary }) => {
17462
17292
  const requestId = `pause_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
17463
- const pauseResp = await new Promise((resolve14) => {
17464
- this.pendingAutoPause.set(requestId, resolve14);
17293
+ const pauseResp = await new Promise((resolve13) => {
17294
+ this.pendingAutoPause.set(requestId, resolve13);
17465
17295
  this.send({
17466
17296
  type: "auto_pause_request",
17467
17297
  requestId,
@@ -17611,7 +17441,7 @@ ${summaryContent}`,
17611
17441
  let isError = false;
17612
17442
  let summary;
17613
17443
  try {
17614
- mkdirSync15(dirname10(saveToFile), { recursive: true });
17444
+ mkdirSync14(dirname9(saveToFile), { recursive: true });
17615
17445
  fileStream = createWriteStream2(saveToFile);
17616
17446
  const teeSystemPrompt = stripToolCallReminder(systemPrompt ?? "") + CONTENT_ONLY_STREAM_REMINDER;
17617
17447
  const teeExtraMessages = extraMessages.length > 0 ? [...extraMessages, { role: "user", content: TEE_FINAL_USER_NUDGE }] : [{ role: "user", content: TEE_FINAL_USER_NUDGE }];
@@ -17640,8 +17470,8 @@ ${summaryContent}`,
17640
17470
  }
17641
17471
  if (chunk.done) break;
17642
17472
  }
17643
- await new Promise((resolve14, reject) => {
17644
- fileStream.end((err) => err ? reject(err) : resolve14());
17473
+ await new Promise((resolve13, reject) => {
17474
+ fileStream.end((err) => err ? reject(err) : resolve13());
17645
17475
  });
17646
17476
  const verdict = evaluateTeeContent(fullContent, saveToFile, priorContent);
17647
17477
  if (verdict.kind === "reject") {
@@ -17667,7 +17497,7 @@ ${summaryContent}`,
17667
17497
  } catch (err) {
17668
17498
  if (fileStream) {
17669
17499
  try {
17670
- await new Promise((resolve14) => fileStream.end(() => resolve14()));
17500
+ await new Promise((resolve13) => fileStream.end(() => resolve13()));
17671
17501
  } catch {
17672
17502
  }
17673
17503
  }
@@ -17874,7 +17704,11 @@ ${summaryContent}`,
17874
17704
  const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
17875
17705
  const content = entries.length === 0 ? "(empty \u2014 no persistent memory entries yet)" : entries.map((entry) => {
17876
17706
  const status = isMemoryExpired(entry) ? "expired" : entry.approved ? "approved" : "pending";
17877
- const source = entry.sourceSession ? `session ${entry.sourceSession.slice(0, 8)}` : entry.sourceProject ? `project ${entry.sourceProject}` : "manual/tool";
17707
+ const source = [
17708
+ entry.source ?? "unknown",
17709
+ entry.sourceSession ? `session ${entry.sourceSession.slice(0, 8)}` : "",
17710
+ entry.sourceProject ? `project ${entry.sourceProject}` : ""
17711
+ ].filter(Boolean).join(" \xB7 ");
17878
17712
  return `## ${entry.id.slice(0, 8)} \xB7 ${status} \xB7 scope:${entry.scope} \xB7 sensitivity:${entry.sensitivity}
17879
17713
  Source: ${source}
17880
17714
  ${entry.content}`;
@@ -17886,26 +17720,41 @@ ${entry.content}`;
17886
17720
  });
17887
17721
  this.sendMemoryEntries();
17888
17722
  }
17889
- memoryAdd(text) {
17723
+ memoryAdd(text, opts) {
17724
+ const asSensitivity = (v) => v === "low" || v === "medium" || v === "high" ? v : void 0;
17890
17725
  try {
17891
17726
  const entry = addMemoryEntry(this.config.getConfigDir(), text, {
17892
- scope: "personal",
17727
+ scope: parseWritableScope(opts?.scope) ?? "personal",
17728
+ source: "manual",
17729
+ sensitivity: asSensitivity(opts?.sensitivity),
17730
+ expiresAt: opts?.expiresAt,
17893
17731
  cwd: process.cwd(),
17894
17732
  redact: true
17895
17733
  });
17896
17734
  const status = entry.approved ? "approved" : "pending approval";
17897
- this.send({ type: "info", message: `\u{1F4DD} Memory entry ${entry.id.slice(0, 8)} added (${status}).` });
17735
+ this.send({ type: "info", message: `\u{1F4DD} Memory entry ${entry.id.slice(0, 8)} added (${status}, scope:${entry.scope}).` });
17898
17736
  this.sendMemoryEntries();
17899
17737
  } catch (err) {
17900
17738
  this.send({ type: "error", message: `Failed to write memory: ${err.message}` });
17901
17739
  }
17902
17740
  }
17741
+ memoryEdit(id, text) {
17742
+ try {
17743
+ const entry = updateMemoryEntry(this.config.getConfigDir(), id, text, { source: "manual", redact: true });
17744
+ const status = entry.approved ? "approved" : `pending approval \u2014 approve it in the Memory panel or /memory approve ${entry.id.slice(0, 8)}`;
17745
+ this.send({ type: "info", message: `\u{1F4DD} Memory ${entry.id.slice(0, 8)} updated (${status}).` });
17746
+ this.sendMemoryEntries();
17747
+ } catch (err) {
17748
+ this.send({ type: "error", message: `Failed to update memory: ${err.message}` });
17749
+ }
17750
+ }
17903
17751
  memoryClear() {
17904
17752
  const configDir = this.config.getConfigDir();
17905
17753
  try {
17906
- atomicWriteFileSync(memoryStorePath(configDir), "");
17907
- atomicWriteFileSync(join26(configDir, MEMORY_FILE_NAME), "");
17908
- this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
17754
+ const result = clearMemoryEntries(configDir);
17755
+ const backupNote = result.backupPath ? `
17756
+ Backup saved: ${result.backupPath}` : "";
17757
+ this.send({ type: "info", message: `\u{1F5D1}\uFE0F Persistent memory cleared (${result.cleared} entries).${backupNote}` });
17909
17758
  this.sendMemoryEntries();
17910
17759
  } catch (err) {
17911
17760
  this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
@@ -17921,6 +17770,7 @@ ${entry.content}`;
17921
17770
  id: entry.id,
17922
17771
  content: entry.content,
17923
17772
  scope: entry.scope,
17773
+ source: entry.source,
17924
17774
  sourceSession: entry.sourceSession,
17925
17775
  sourceProject: entry.sourceProject,
17926
17776
  createdAt: entry.createdAt,
@@ -17970,7 +17820,7 @@ ${entry.content}`;
17970
17820
  return;
17971
17821
  }
17972
17822
  try {
17973
- const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-WYXYD7YP.js");
17823
+ const { searchChatMemory: searchChatMemory2, loadChatIndex: loadChatIndex2 } = await import("./chat-index-ML5FF5L6.js");
17974
17824
  const loaded = loadChatIndex2();
17975
17825
  if (!loaded || loaded.idx.chunks.length === 0) {
17976
17826
  this.send({ type: "memory_hits", query: q, hits: [], indexMissing: true });
@@ -18006,7 +17856,7 @@ ${entry.content}`;
18006
17856
  }
18007
17857
  async handleMemoryStatus() {
18008
17858
  try {
18009
- const { getChatIndexStatus } = await import("./chat-index-WYXYD7YP.js");
17859
+ const { getChatIndexStatus } = await import("./chat-index-ML5FF5L6.js");
18010
17860
  const s = getChatIndexStatus();
18011
17861
  this.send({
18012
17862
  type: "memory_status",
@@ -18031,7 +17881,7 @@ ${entry.content}`;
18031
17881
  type: "info",
18032
17882
  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
17883
  });
18034
- const { buildChatIndex } = await import("./chat-index-WYXYD7YP.js");
17884
+ const { buildChatIndex } = await import("./chat-index-ML5FF5L6.js");
18035
17885
  const stats = await buildChatIndex({
18036
17886
  full,
18037
17887
  onProgress: (p) => {
@@ -18105,6 +17955,7 @@ ${entry.content}`;
18105
17955
  activeSkill,
18106
17956
  planMode: this.planMode,
18107
17957
  configDir: this.config.getConfigDir(),
17958
+ memoryMaxChars: this.config.get("memory")?.maxChars,
18108
17959
  userProfile: this.config.get("userProfile")
18109
17960
  });
18110
17961
  let stable = baseStable;
@@ -18186,8 +18037,8 @@ async function setupProxy(configProxy) {
18186
18037
  }
18187
18038
 
18188
18039
  // src/web/auth.ts
18189
- import { existsSync as existsSync32, readFileSync as readFileSync24, writeFileSync as writeFileSync5, mkdirSync as mkdirSync16, readdirSync as readdirSync15, copyFileSync } from "fs";
18190
- import { isAbsolute as isAbsolute3, join as join27, relative as relative5 } from "path";
18040
+ import { existsSync as existsSync30, readFileSync as readFileSync23, writeFileSync as writeFileSync5, mkdirSync as mkdirSync15, readdirSync as readdirSync15, copyFileSync } from "fs";
18041
+ import { isAbsolute as isAbsolute3, join as join25, relative as relative5 } from "path";
18191
18042
  import { createHmac, randomBytes, timingSafeEqual, pbkdf2Sync } from "crypto";
18192
18043
  var USERS_FILE = "users.json";
18193
18044
  var TOKEN_EXPIRY_HOURS = 24;
@@ -18202,7 +18053,7 @@ var AuthManager = class {
18202
18053
  db;
18203
18054
  constructor(baseDir) {
18204
18055
  this.baseDir = baseDir;
18205
- this.usersFile = join27(baseDir, USERS_FILE);
18056
+ this.usersFile = join25(baseDir, USERS_FILE);
18206
18057
  this.db = this.loadOrCreate();
18207
18058
  }
18208
18059
  // ── Public API ─────────────────────────────────────────────────
@@ -18231,9 +18082,9 @@ var AuthManager = class {
18231
18082
  }
18232
18083
  const salt = randomBytes(16).toString("hex");
18233
18084
  const passwordHash = this.hashPassword(password, salt);
18234
- const dataDir = join27(USERS_DIR, username);
18235
- const fullDataDir = join27(this.baseDir, dataDir);
18236
- mkdirSync16(join27(fullDataDir, "history"), { recursive: true });
18085
+ const dataDir = join25(USERS_DIR, username);
18086
+ const fullDataDir = join25(this.baseDir, dataDir);
18087
+ mkdirSync15(join25(fullDataDir, "history"), { recursive: true });
18237
18088
  const user = {
18238
18089
  username,
18239
18090
  passwordHash,
@@ -18348,7 +18199,7 @@ var AuthManager = class {
18348
18199
  getUserDataDir(username) {
18349
18200
  const user = this.db.users.find((u) => u.username === username);
18350
18201
  if (!user) throw new Error(`User not found: ${username}`);
18351
- return join27(this.baseDir, user.dataDir);
18202
+ return join25(this.baseDir, user.dataDir);
18352
18203
  }
18353
18204
  /** List all usernames */
18354
18205
  listUsers() {
@@ -18384,30 +18235,30 @@ var AuthManager = class {
18384
18235
  const err = this.register(username, password);
18385
18236
  if (err) return err;
18386
18237
  const userDir = this.getUserDataDir(username);
18387
- const globalConfig = join27(this.baseDir, "config.json");
18388
- if (existsSync32(globalConfig)) {
18238
+ const globalConfig = join25(this.baseDir, "config.json");
18239
+ if (existsSync30(globalConfig)) {
18389
18240
  try {
18390
- const content = readFileSync24(globalConfig, "utf-8");
18391
- writeFileSync5(join27(userDir, "config.json"), content, "utf-8");
18241
+ const content = readFileSync23(globalConfig, "utf-8");
18242
+ writeFileSync5(join25(userDir, "config.json"), content, "utf-8");
18392
18243
  } catch {
18393
18244
  }
18394
18245
  }
18395
- const globalMemory = join27(this.baseDir, "memory.md");
18396
- if (existsSync32(globalMemory)) {
18246
+ const globalMemory = join25(this.baseDir, "memory.md");
18247
+ if (existsSync30(globalMemory)) {
18397
18248
  try {
18398
- const content = readFileSync24(globalMemory, "utf-8");
18399
- writeFileSync5(join27(userDir, "memory.md"), content, "utf-8");
18249
+ const content = readFileSync23(globalMemory, "utf-8");
18250
+ writeFileSync5(join25(userDir, "memory.md"), content, "utf-8");
18400
18251
  } catch {
18401
18252
  }
18402
18253
  }
18403
- const globalHistory = join27(this.baseDir, "history");
18404
- if (existsSync32(globalHistory)) {
18254
+ const globalHistory = join25(this.baseDir, "history");
18255
+ if (existsSync30(globalHistory)) {
18405
18256
  try {
18406
18257
  const files = readdirSync15(globalHistory).filter((f) => f.endsWith(".json"));
18407
- const userHistory = join27(userDir, "history");
18258
+ const userHistory = join25(userDir, "history");
18408
18259
  for (const f of files) {
18409
18260
  try {
18410
- copyFileSync(join27(globalHistory, f), join27(userHistory, f));
18261
+ copyFileSync(join25(globalHistory, f), join25(userHistory, f));
18411
18262
  } catch {
18412
18263
  }
18413
18264
  }
@@ -18418,9 +18269,9 @@ var AuthManager = class {
18418
18269
  }
18419
18270
  // ── Private methods ────────────────────────────────────────────
18420
18271
  loadOrCreate() {
18421
- if (existsSync32(this.usersFile)) {
18272
+ if (existsSync30(this.usersFile)) {
18422
18273
  try {
18423
- const parsed = JSON.parse(readFileSync24(this.usersFile, "utf-8"));
18274
+ const parsed = JSON.parse(readFileSync23(this.usersFile, "utf-8"));
18424
18275
  if (!this.isValidUsersDB(parsed)) {
18425
18276
  throw new Error("database structure is invalid");
18426
18277
  }
@@ -18454,7 +18305,7 @@ var AuthManager = class {
18454
18305
  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
18306
  return false;
18456
18307
  }
18457
- const dataDirFromBase = relative5(this.baseDir, join27(this.baseDir, record.dataDir));
18308
+ const dataDirFromBase = relative5(this.baseDir, join25(this.baseDir, record.dataDir));
18458
18309
  if (dataDirFromBase.startsWith("..") || isAbsolute3(dataDirFromBase)) return false;
18459
18310
  if (record.hashVersion !== void 0 && record.hashVersion !== 1 && record.hashVersion !== 2) return false;
18460
18311
  if (record.tokensRevokedBefore !== void 0 && !Number.isFinite(record.tokensRevokedBefore)) return false;
@@ -18466,7 +18317,7 @@ var AuthManager = class {
18466
18317
  this.saveDB(this.db);
18467
18318
  }
18468
18319
  saveDB(db) {
18469
- mkdirSync16(this.baseDir, { recursive: true });
18320
+ mkdirSync15(this.baseDir, { recursive: true });
18470
18321
  atomicWriteFileSync(this.usersFile, JSON.stringify(db, null, 2));
18471
18322
  }
18472
18323
  /** Legacy hash — kept only for migrating old users (v0.2.x) */
@@ -18498,11 +18349,11 @@ function getModuleDir() {
18498
18349
  if (typeof import.meta?.url === "string") {
18499
18350
  const url = new URL(import.meta.url);
18500
18351
  const filePath = url.pathname.replace(/^\/([A-Z]:)/i, "$1");
18501
- return dirname11(filePath);
18352
+ return dirname10(filePath);
18502
18353
  }
18503
18354
  } catch {
18504
18355
  }
18505
- return resolve13(".");
18356
+ return resolve12(".");
18506
18357
  }
18507
18358
  async function startWebServer(options = {}) {
18508
18359
  const port = options.port ?? 3e3;
@@ -18570,8 +18421,8 @@ async function startWebServer(options = {}) {
18570
18421
  }
18571
18422
  }
18572
18423
  let skillManager = null;
18573
- const skillsDir = join28(config.getConfigDir(), SKILLS_DIR_NAME);
18574
- if (existsSync33(skillsDir)) {
18424
+ const skillsDir = join26(config.getConfigDir(), SKILLS_DIR_NAME);
18425
+ if (existsSync31(skillsDir)) {
18575
18426
  skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
18576
18427
  skillManager.loadSkills(pluginAssets.skillDirs);
18577
18428
  const count = skillManager.listSkills().length;
@@ -18642,18 +18493,18 @@ async function startWebServer(options = {}) {
18642
18493
  next();
18643
18494
  };
18644
18495
  const moduleDir = getModuleDir();
18645
- let clientDir = join28(moduleDir, "web", "client");
18646
- if (!existsSync33(clientDir)) {
18647
- clientDir = join28(moduleDir, "client");
18496
+ let clientDir = join26(moduleDir, "web", "client");
18497
+ if (!existsSync31(clientDir)) {
18498
+ clientDir = join26(moduleDir, "client");
18648
18499
  }
18649
- if (!existsSync33(clientDir)) {
18650
- clientDir = join28(moduleDir, "..", "..", "src", "web", "client");
18500
+ if (!existsSync31(clientDir)) {
18501
+ clientDir = join26(moduleDir, "..", "..", "src", "web", "client");
18651
18502
  }
18652
- if (!existsSync33(clientDir)) {
18653
- clientDir = join28(process.cwd(), "src", "web", "client");
18503
+ if (!existsSync31(clientDir)) {
18504
+ clientDir = join26(process.cwd(), "src", "web", "client");
18654
18505
  }
18655
18506
  console.log(` Static files: ${clientDir}`);
18656
- app.use("/vendor", express.static(join28(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
18507
+ app.use("/vendor", express.static(join26(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
18657
18508
  app.use(express.static(clientDir));
18658
18509
  app.get("/api/status", (_req, res) => {
18659
18510
  res.json({
@@ -18720,10 +18571,10 @@ async function startWebServer(options = {}) {
18720
18571
  app.get("/api/files", requireAuth, (req, res) => {
18721
18572
  const cwd = process.cwd();
18722
18573
  const prefix = req.query.prefix || "";
18723
- const targetDir = join28(cwd, prefix);
18574
+ const targetDir = join26(cwd, prefix);
18724
18575
  try {
18725
- const canonicalTarget = realpathSync3(resolve13(targetDir));
18726
- const canonicalCwd = realpathSync3(resolve13(cwd));
18576
+ const canonicalTarget = realpathSync3(resolve12(targetDir));
18577
+ const canonicalCwd = realpathSync3(resolve12(cwd));
18727
18578
  if (!canonicalTarget.startsWith(canonicalCwd + sep4) && canonicalTarget !== canonicalCwd) {
18728
18579
  res.json({ files: [] });
18729
18580
  return;
@@ -18737,7 +18588,7 @@ async function startWebServer(options = {}) {
18737
18588
  const entries = readdirSync16(targetDir, { withFileTypes: true });
18738
18589
  const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
18739
18590
  name: e.name,
18740
- path: relative6(cwd, join28(targetDir, e.name)).replace(/\\/g, "/"),
18591
+ path: relative6(cwd, join26(targetDir, e.name)).replace(/\\/g, "/"),
18741
18592
  isDir: e.isDirectory()
18742
18593
  }));
18743
18594
  res.json({ files });
@@ -18777,8 +18628,8 @@ async function startWebServer(options = {}) {
18777
18628
  try {
18778
18629
  const authUser = req._authUser;
18779
18630
  const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
18780
- const filePath = join28(histDir, `${id}.json`);
18781
- if (!existsSync33(filePath)) {
18631
+ const filePath = join26(histDir, `${id}.json`);
18632
+ if (!existsSync31(filePath)) {
18782
18633
  res.status(404).json({ error: "Session not found" });
18783
18634
  return;
18784
18635
  }
@@ -18793,7 +18644,7 @@ async function startWebServer(options = {}) {
18793
18644
  res.status(404).json({ error: "Session not found" });
18794
18645
  return;
18795
18646
  }
18796
- const data = JSON.parse(readFileSync25(filePath, "utf-8"));
18647
+ const data = JSON.parse(readFileSync24(filePath, "utf-8"));
18797
18648
  res.json({ session: data });
18798
18649
  } catch (err) {
18799
18650
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
@@ -18806,10 +18657,10 @@ async function startWebServer(options = {}) {
18806
18657
  return;
18807
18658
  }
18808
18659
  const cwd = process.cwd();
18809
- const fullPath = resolve13(join28(cwd, filePath));
18660
+ const fullPath = resolve12(join26(cwd, filePath));
18810
18661
  try {
18811
18662
  const canonicalFull = realpathSync3(fullPath);
18812
- const canonicalCwd = realpathSync3(resolve13(cwd));
18663
+ const canonicalCwd = realpathSync3(resolve12(cwd));
18813
18664
  if (!canonicalFull.startsWith(canonicalCwd + sep4) && canonicalFull !== canonicalCwd) {
18814
18665
  res.json({ error: "Access denied" });
18815
18666
  return;
@@ -18824,7 +18675,7 @@ async function startWebServer(options = {}) {
18824
18675
  res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
18825
18676
  return;
18826
18677
  }
18827
- const content = readFileSync25(fullPath, "utf-8");
18678
+ const content = readFileSync24(fullPath, "utf-8");
18828
18679
  res.json({ content, size: stat.size });
18829
18680
  } catch {
18830
18681
  res.json({ error: "Cannot read file" });
@@ -19028,7 +18879,7 @@ async function startWebServer(options = {}) {
19028
18879
  });
19029
18880
  const MAX_PORT_ATTEMPTS = 10;
19030
18881
  let actualPort = port;
19031
- const result = await new Promise((resolve14, reject) => {
18882
+ const result = await new Promise((resolve13, reject) => {
19032
18883
  const tryListen = (attempt) => {
19033
18884
  server.once("error", (err) => {
19034
18885
  if (err.code === "EADDRINUSE" && attempt < MAX_PORT_ATTEMPTS) {
@@ -19059,7 +18910,7 @@ async function startWebServer(options = {}) {
19059
18910
  }
19060
18911
  console.log(` Press Ctrl+C to stop
19061
18912
  `);
19062
- resolve14({ port: actualPort, host, url });
18913
+ resolve13({ port: actualPort, host, url });
19063
18914
  });
19064
18915
  };
19065
18916
  tryListen(1);
@@ -19079,17 +18930,17 @@ function resolveProjectMcpPath() {
19079
18930
  const cwd = process.cwd();
19080
18931
  const gitRoot = getGitRoot(cwd);
19081
18932
  const projectRoot = gitRoot ?? cwd;
19082
- const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
19083
- return existsSync33(configPath) ? configPath : null;
18933
+ const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
18934
+ return existsSync31(configPath) ? configPath : null;
19084
18935
  }
19085
18936
  function loadProjectMcpConfig() {
19086
18937
  const cwd = process.cwd();
19087
18938
  const gitRoot = getGitRoot(cwd);
19088
18939
  const projectRoot = gitRoot ?? cwd;
19089
- const configPath = join28(projectRoot, MCP_PROJECT_CONFIG_NAME);
19090
- if (!existsSync33(configPath)) return null;
18940
+ const configPath = join26(projectRoot, MCP_PROJECT_CONFIG_NAME);
18941
+ if (!existsSync31(configPath)) return null;
19091
18942
  try {
19092
- const raw = JSON.parse(readFileSync25(configPath, "utf-8"));
18943
+ const raw = JSON.parse(readFileSync24(configPath, "utf-8"));
19093
18944
  return raw.mcpServers ?? raw;
19094
18945
  } catch {
19095
18946
  return null;