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,4 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ addMemoryEntry,
4
+ findSimilarMemory,
5
+ loadMemoryEntries,
6
+ parseWritableScope,
7
+ updateMemoryEntry
8
+ } from "./chunk-D27TMEOV.js";
2
9
  import {
3
10
  indexProject
4
11
  } from "./chunk-K3CF65QH.js";
@@ -14,7 +21,7 @@ import {
14
21
  runHook,
15
22
  runLifecycleHooks,
16
23
  runTool
17
- } from "./chunk-2LLQOMMW.js";
24
+ } from "./chunk-J363BGOH.js";
18
25
  import {
19
26
  getDangerLevel,
20
27
  isFileWriteTool,
@@ -25,33 +32,23 @@ import {
25
32
  NetworkError,
26
33
  ToolError
27
34
  } from "./chunk-5ULLIOVC.js";
28
- import {
29
- getGitRoot
30
- } from "./chunk-HOSJZMQS.js";
31
35
  import {
32
36
  fileCheckpoints
33
37
  } from "./chunk-4BKXL7SM.js";
34
38
  import {
35
39
  runTestsTool
36
- } from "./chunk-NAQPP3GZ.js";
40
+ } from "./chunk-6ZWNXHNH.js";
37
41
  import {
38
42
  CONFIG_DIR_NAME,
39
43
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
40
- MEMORY_FILE_NAME,
41
- MEMORY_MAX_CHARS,
42
- MEMORY_STORE_FILE_NAME,
43
44
  SUBAGENT_ALLOWED_TOOLS,
44
45
  SUBAGENT_DEFAULT_MAX_ROUNDS,
45
46
  SUBAGENT_MAX_ROUNDS_LIMIT
46
- } from "./chunk-SMQHEJSH.js";
47
+ } from "./chunk-Y4CMNXZH.js";
47
48
  import {
48
49
  loadChatIndex,
49
50
  searchChatMemory
50
- } from "./chunk-5MYPIQ3Y.js";
51
- import {
52
- DEFAULT_PATTERNS,
53
- redactString
54
- } from "./chunk-KCEO2XJ4.js";
51
+ } from "./chunk-IGPFYNZA.js";
55
52
  import {
56
53
  atomicWriteFileSync
57
54
  } from "./chunk-IW3Q7AE5.js";
@@ -140,7 +137,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
140
137
  const markerStart = `AICLI_MARKER_START:${id}`;
141
138
  const markerEnd = `AICLI_MARKER_END:${id}`;
142
139
  const wrappedCmd = IS_WINDOWS2 ? `${WIN_UTF8_PREAMBLE} echo "${markerStart}"; ${command}; echo "${markerEnd}:0"` : `echo "${markerStart}"; ${command}; echo "${markerEnd}:$?"`;
143
- return new Promise((resolve8) => {
140
+ return new Promise((resolve7) => {
144
141
  let stdout = "";
145
142
  let stderr = "";
146
143
  let timedOut = false;
@@ -156,7 +153,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
156
153
  stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
157
154
  resolved = true;
158
155
  cleanup();
159
- resolve8({ stdout, stderr, exitCode, timedOut, aborted });
156
+ resolve7({ stdout, stderr, exitCode, timedOut, aborted });
160
157
  }
161
158
  };
162
159
  const onErr = (chunk) => {
@@ -171,7 +168,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
171
168
  resolved = true;
172
169
  stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
173
170
  cleanup();
174
- resolve8({ stdout, stderr, exitCode: null, timedOut, aborted });
171
+ resolve7({ stdout, stderr, exitCode: null, timedOut, aborted });
175
172
  try {
176
173
  proc.kill();
177
174
  } catch {
@@ -184,7 +181,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
184
181
  resolved = true;
185
182
  cleanup();
186
183
  stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
187
- resolve8({ stdout, stderr, exitCode: null, timedOut, aborted });
184
+ resolve7({ stdout, stderr, exitCode: null, timedOut, aborted });
188
185
  try {
189
186
  proc.kill();
190
187
  } catch {
@@ -202,7 +199,7 @@ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
202
199
  stderr += `
203
200
  [pool error] ${err.message}`;
204
201
  cleanup();
205
- resolve8({ stdout, stderr, exitCode: null, timedOut, aborted });
202
+ resolve7({ stdout, stderr, exitCode: null, timedOut, aborted });
206
203
  pool.delete(sessionKey);
207
204
  };
208
205
  proc.on("error", onError);
@@ -2540,7 +2537,7 @@ var ToolExecutor = class {
2540
2537
  rl.resume();
2541
2538
  process.stdout.write(prompt);
2542
2539
  this.confirming = true;
2543
- return new Promise((resolve8) => {
2540
+ return new Promise((resolve7) => {
2544
2541
  let completed = false;
2545
2542
  const cleanup = (result) => {
2546
2543
  if (completed) return;
@@ -2550,7 +2547,7 @@ var ToolExecutor = class {
2550
2547
  rl.pause();
2551
2548
  rlAny.output = savedOutput;
2552
2549
  this.confirming = false;
2553
- resolve8(result);
2550
+ resolve7(result);
2554
2551
  };
2555
2552
  const onLine = (line) => {
2556
2553
  const trimmed = line.trim();
@@ -2724,7 +2721,7 @@ var ToolExecutor = class {
2724
2721
  rl.resume();
2725
2722
  process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
2726
2723
  this.confirming = true;
2727
- return new Promise((resolve8) => {
2724
+ return new Promise((resolve7) => {
2728
2725
  let completed = false;
2729
2726
  const cleanup = (answer) => {
2730
2727
  if (completed) return;
@@ -2734,7 +2731,7 @@ var ToolExecutor = class {
2734
2731
  rl.pause();
2735
2732
  rlAny.output = savedOutput;
2736
2733
  this.confirming = false;
2737
- resolve8(answer === "y");
2734
+ resolve7(answer === "y");
2738
2735
  };
2739
2736
  const onLine = (line) => {
2740
2737
  const trimmed = line.trim();
@@ -3983,7 +3980,7 @@ var runInteractiveTool = {
3983
3980
  PYTHONDONTWRITEBYTECODE: "1"
3984
3981
  };
3985
3982
  const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
3986
- return new Promise((resolve8) => {
3983
+ return new Promise((resolve7) => {
3987
3984
  const child = spawn3(executable, cmdArgs.map(String), {
3988
3985
  cwd: process.cwd(),
3989
3986
  env,
@@ -4016,22 +4013,22 @@ var runInteractiveTool = {
4016
4013
  setTimeout(writeNextLine, 400);
4017
4014
  const timer = setTimeout(() => {
4018
4015
  child.kill();
4019
- resolve8(`${prefixWarnings}[Timeout after ${timeout}ms]
4016
+ resolve7(`${prefixWarnings}[Timeout after ${timeout}ms]
4020
4017
  ${buildOutput(stdout, stderr)}`);
4021
4018
  }, timeout);
4022
4019
  child.on("close", (code) => {
4023
4020
  clearTimeout(timer);
4024
4021
  const output = buildOutput(stdout, stderr);
4025
4022
  if (code !== 0 && code !== null) {
4026
- resolve8(`${prefixWarnings}Exit code ${code}:
4023
+ resolve7(`${prefixWarnings}Exit code ${code}:
4027
4024
  ${output}`);
4028
4025
  } else {
4029
- resolve8(`${prefixWarnings}${output || "(no output)"}`);
4026
+ resolve7(`${prefixWarnings}${output || "(no output)"}`);
4030
4027
  }
4031
4028
  });
4032
4029
  child.on("error", (err) => {
4033
4030
  clearTimeout(timer);
4034
- resolve8(
4031
+ resolve7(
4035
4032
  `${prefixWarnings}Failed to start process "${executable}": ${err.message}
4036
4033
  Hint: On Windows, use the full path to the executable, e.g.:
4037
4034
  C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
@@ -4743,188 +4740,10 @@ Any of these triggers means use save_last_response, NOT write_file:
4743
4740
  };
4744
4741
 
4745
4742
  // src/tools/builtin/save-memory.ts
4746
- import { join as join6 } from "path";
4743
+ import { join as join5 } from "path";
4747
4744
  import { homedir as homedir3 } from "os";
4748
-
4749
- // src/memory/persistent-memory.ts
4750
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync7 } from "fs";
4751
- import { randomUUID, createHash } from "crypto";
4752
- import { dirname as dirname5, join as join5, resolve as resolve6 } from "path";
4753
- function memoryStorePath(configDir) {
4754
- return join5(configDir, MEMORY_STORE_FILE_NAME);
4755
- }
4756
- function memoryMarkdownPath(configDir) {
4757
- return join5(configDir, MEMORY_FILE_NAME);
4758
- }
4759
- function getProjectKey(cwd = process.cwd()) {
4760
- return resolve6(getGitRoot(cwd) ?? cwd);
4761
- }
4762
- function nowIso() {
4763
- return (/* @__PURE__ */ new Date()).toISOString();
4764
- }
4765
- function isValidScope(value) {
4766
- return value === "personal" || value === "project" || value === "session" || value === "team";
4767
- }
4768
- function isValidSensitivity(value) {
4769
- return value === "low" || value === "medium" || value === "high";
4770
- }
4771
- function normalizeEntry(value) {
4772
- if (!value || typeof value !== "object") return null;
4773
- const raw = value;
4774
- const content = typeof raw.content === "string" ? raw.content.trim() : "";
4775
- if (!content) return null;
4776
- const createdAt = typeof raw.createdAt === "string" ? raw.createdAt : nowIso();
4777
- return {
4778
- id: typeof raw.id === "string" && raw.id ? raw.id : randomUUID(),
4779
- content,
4780
- scope: isValidScope(raw.scope) ? raw.scope : "personal",
4781
- sourceSession: typeof raw.sourceSession === "string" && raw.sourceSession ? raw.sourceSession : void 0,
4782
- sourceProject: typeof raw.sourceProject === "string" && raw.sourceProject ? raw.sourceProject : void 0,
4783
- createdAt,
4784
- updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : createdAt,
4785
- expiresAt: typeof raw.expiresAt === "string" && raw.expiresAt ? raw.expiresAt : void 0,
4786
- sensitivity: isValidSensitivity(raw.sensitivity) ? raw.sensitivity : "low",
4787
- approved: raw.approved === true,
4788
- redactedKinds: Array.isArray(raw.redactedKinds) ? raw.redactedKinds.filter((x) => typeof x === "string") : void 0
4789
- };
4790
- }
4791
- function legacyEntriesFromMarkdown(configDir) {
4792
- const file = memoryMarkdownPath(configDir);
4793
- if (!existsSync11(file)) return [];
4794
- const content = readFileSync7(file, "utf-8").trim();
4795
- if (!content) return [];
4796
- const parts = content.split(/\n(?=##\s+\d{4}-\d{2}-\d{2})/g);
4797
- const chunks = parts.length > 1 ? parts : [content];
4798
- return chunks.map((chunk, index) => {
4799
- const match = chunk.match(/^##\s+([^\n]+)\n([\s\S]*)$/);
4800
- const body = (match ? match[2] : chunk).trim();
4801
- const parsed = match ? Date.parse(match[1].replace(" ", "T")) : NaN;
4802
- const createdAt = Number.isFinite(parsed) ? new Date(parsed).toISOString() : nowIso();
4803
- const id = "legacy-" + createHash("sha1").update(`${index}
4804
- ${chunk}`).digest("hex").slice(0, 12);
4805
- return normalizeEntry({ id, content: body, scope: "personal", createdAt, updatedAt: createdAt, sensitivity: "low", approved: true });
4806
- }).filter((entry) => Boolean(entry));
4807
- }
4808
- function loadMemoryEntries(configDir) {
4809
- const file = memoryStorePath(configDir);
4810
- if (!existsSync11(file)) return legacyEntriesFromMarkdown(configDir);
4811
- const text = readFileSync7(file, "utf-8").trim();
4812
- if (!text) return [];
4813
- const entries = [];
4814
- for (const line of text.split("\n")) {
4815
- const trimmed = line.trim();
4816
- if (!trimmed) continue;
4817
- try {
4818
- const entry = normalizeEntry(JSON.parse(trimmed));
4819
- if (entry) entries.push(entry);
4820
- } catch {
4821
- }
4822
- }
4823
- return entries;
4824
- }
4825
- function saveMemoryEntries(configDir, entries) {
4826
- mkdirSync3(configDir, { recursive: true });
4827
- const jsonl = entries.map((entry) => JSON.stringify(entry)).join("\n");
4828
- atomicWriteFileSync(memoryStorePath(configDir), jsonl ? jsonl + "\n" : "");
4829
- }
4830
- function isMemoryExpired(entry, at = /* @__PURE__ */ new Date()) {
4831
- return Boolean(entry.expiresAt && Date.parse(entry.expiresAt) <= at.getTime());
4832
- }
4833
- function isMemoryVisibleInProject(entry, cwd = process.cwd()) {
4834
- if (entry.scope !== "project") return true;
4835
- return entry.sourceProject === getProjectKey(cwd);
4836
- }
4837
- function listMemoryEntries(configDir, options = {}) {
4838
- const cwd = options.cwd ?? process.cwd();
4839
- return loadMemoryEntries(configDir).filter((entry) => options.includeRejected || entry.approved).filter((entry) => options.includeExpired || !isMemoryExpired(entry)).filter((entry) => isMemoryVisibleInProject(entry, cwd));
4840
- }
4841
- function addMemoryEntry(configDir, content, options = {}) {
4842
- const original = content.trim();
4843
- if (!original) throw new Error("memory content is required");
4844
- const redact = options.redact ?? true;
4845
- const redacted = redact ? redactString(original, { enabled: true, patterns: options.patterns ?? DEFAULT_PATTERNS, customRegexes: options.customPatterns }) : { redacted: original, hits: [] };
4846
- const sensitivity = options.sensitivity ?? (redacted.hits.length > 0 ? "high" : "low");
4847
- const approved = options.approved ?? (sensitivity === "low" && redacted.hits.length === 0);
4848
- const scope = options.scope ?? "personal";
4849
- const timestamp = nowIso();
4850
- const entry = {
4851
- id: randomUUID(),
4852
- content: redacted.redacted.trim(),
4853
- scope,
4854
- sourceSession: options.sourceSession,
4855
- sourceProject: scope === "project" ? getProjectKey(options.cwd) : void 0,
4856
- createdAt: timestamp,
4857
- updatedAt: timestamp,
4858
- expiresAt: options.expiresAt,
4859
- sensitivity,
4860
- approved,
4861
- redactedKinds: [...new Set(redacted.hits.map((hit) => hit.kind))]
4862
- };
4863
- const entries = loadMemoryEntries(configDir);
4864
- entries.push(entry);
4865
- saveMemoryEntries(configDir, entries);
4866
- return entry;
4867
- }
4868
- function updateMemoryApproval(configDir, idPrefix, approved) {
4869
- const entries = loadMemoryEntries(configDir);
4870
- const entry = findUnique(entries, idPrefix);
4871
- entry.approved = approved;
4872
- entry.updatedAt = nowIso();
4873
- saveMemoryEntries(configDir, entries);
4874
- return entry;
4875
- }
4876
- function deleteMemoryEntry(configDir, idPrefix) {
4877
- const entries = loadMemoryEntries(configDir);
4878
- const entry = findUnique(entries, idPrefix);
4879
- saveMemoryEntries(configDir, entries.filter((candidate) => candidate.id !== entry.id));
4880
- return entry;
4881
- }
4882
- function expireMemoryEntry(configDir, idPrefix, expiresAt = nowIso()) {
4883
- const entries = loadMemoryEntries(configDir);
4884
- const entry = findUnique(entries, idPrefix);
4885
- entry.expiresAt = expiresAt;
4886
- entry.updatedAt = nowIso();
4887
- saveMemoryEntries(configDir, entries);
4888
- return entry;
4889
- }
4890
- function exportMemoryEntries(configDir, cwd = process.cwd()) {
4891
- return JSON.stringify(listMemoryEntries(configDir, { cwd, includeExpired: true, includeRejected: true }), null, 2);
4892
- }
4893
- function findUnique(entries, idPrefix) {
4894
- const matches = entries.filter((entry) => entry.id.startsWith(idPrefix));
4895
- if (matches.length === 0) throw new Error(`memory id not found: ${idPrefix}`);
4896
- if (matches.length > 1) throw new Error(`memory id is ambiguous: ${idPrefix}`);
4897
- return matches[0];
4898
- }
4899
- function formatMemoryForPrompt(configDir, cwd = process.cwd()) {
4900
- const entries = listMemoryEntries(configDir, { cwd }).filter((entry) => entry.approved && !isMemoryExpired(entry));
4901
- if (entries.length === 0) return null;
4902
- let content = entries.map((entry) => {
4903
- const source = [
4904
- `id ${entry.id.slice(0, 8)}`,
4905
- `scope ${entry.scope}`,
4906
- entry.sourceSession ? `source session ${entry.sourceSession.slice(0, 8)}` : "source manual/tool",
4907
- entry.sourceProject ? `project ${entry.sourceProject}` : "",
4908
- `sensitivity ${entry.sensitivity}`
4909
- ].filter(Boolean).join(" \xB7 ");
4910
- return `## ${entry.createdAt.replace("T", " ").slice(0, 19)}
4911
- Source: ${source}
4912
- ${entry.content}`;
4913
- }).join("\n\n");
4914
- if (content.length > MEMORY_MAX_CHARS) {
4915
- content = content.slice(-MEMORY_MAX_CHARS);
4916
- const firstEntry = content.indexOf("\n## ");
4917
- if (firstEntry !== -1) content = content.slice(firstEntry + 1);
4918
- }
4919
- return { content, entryCount: entries.length };
4920
- }
4921
-
4922
- // src/tools/builtin/save-memory.ts
4923
4745
  function getConfigDir() {
4924
- return join6(homedir3(), CONFIG_DIR_NAME);
4925
- }
4926
- function parseScope(value) {
4927
- return value === "personal" || value === "project" || value === "session" || value === "team" ? value : void 0;
4746
+ return join5(homedir3(), CONFIG_DIR_NAME);
4928
4747
  }
4929
4748
  function parseSensitivity(value) {
4930
4749
  return value === "low" || value === "medium" || value === "high" ? value : void 0;
@@ -4932,7 +4751,7 @@ function parseSensitivity(value) {
4932
4751
  var saveMemoryTool = {
4933
4752
  definition: {
4934
4753
  name: "save_memory",
4935
- 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.",
4754
+ 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.",
4936
4755
  parameters: {
4937
4756
  content: {
4938
4757
  type: "string",
@@ -4941,7 +4760,7 @@ var saveMemoryTool = {
4941
4760
  },
4942
4761
  scope: {
4943
4762
  type: "string",
4944
- description: "Memory scope: personal, project, session, or team. Defaults to personal. Project memories are only injected in the same project.",
4763
+ description: "Memory scope: personal (default, injected everywhere) or project (only injected inside the current project).",
4945
4764
  required: false
4946
4765
  },
4947
4766
  sensitivity: {
@@ -4953,6 +4772,11 @@ var saveMemoryTool = {
4953
4772
  type: "string",
4954
4773
  description: "Optional ISO timestamp after which this memory should no longer be injected.",
4955
4774
  required: false
4775
+ },
4776
+ updateId: {
4777
+ type: "string",
4778
+ 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.",
4779
+ required: false
4956
4780
  }
4957
4781
  },
4958
4782
  dangerous: false
@@ -4960,16 +4784,29 @@ var saveMemoryTool = {
4960
4784
  async execute(args) {
4961
4785
  const content = String(args["content"] ?? "").trim();
4962
4786
  if (!content) throw new ToolError("save_memory", "content is required");
4787
+ const updateId = typeof args["updateId"] === "string" ? args["updateId"].trim() : "";
4788
+ if (updateId) {
4789
+ try {
4790
+ const updated = updateMemoryEntry(getConfigDir(), updateId, content, { source: "tool", redact: true });
4791
+ const redactedNote = updated.redactedKinds?.length ? ` Redacted: ${updated.redactedKinds.join(", ")}.` : "";
4792
+ 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}`;
4793
+ } catch (err) {
4794
+ throw new ToolError("save_memory", err instanceof Error ? err.message : String(err));
4795
+ }
4796
+ }
4797
+ const similar = findSimilarMemory(loadMemoryEntries(getConfigDir()), content);
4963
4798
  const entry = addMemoryEntry(getConfigDir(), content, {
4964
- scope: parseScope(args["scope"]) ?? "personal",
4799
+ scope: parseWritableScope(args["scope"]) ?? "personal",
4800
+ source: "tool",
4965
4801
  sensitivity: parseSensitivity(args["sensitivity"]),
4966
4802
  expiresAt: typeof args["expiresAt"] === "string" ? args["expiresAt"] : void 0,
4967
4803
  cwd: process.cwd(),
4968
4804
  redact: true
4969
4805
  });
4970
- const status = entry.approved ? "approved and active" : "pending approval";
4806
+ 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`;
4971
4807
  const redacted = entry.redactedKinds?.length ? ` Redacted: ${entry.redactedKinds.join(", ")}.` : "";
4972
- return `Memory saved (${status}). id=${entry.id.slice(0, 8)} scope=${entry.scope} sensitivity=${entry.sensitivity}.${redacted}`;
4808
+ 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.` : "";
4809
+ return `Memory saved (${status}). id=${entry.id.slice(0, 8)} scope=${entry.scope} sensitivity=${entry.sensitivity}.${redacted}${similarNote}`;
4973
4810
  }
4974
4811
  };
4975
4812
 
@@ -5013,7 +4850,7 @@ function promptUser(rl, question) {
5013
4850
  console.log();
5014
4851
  console.log(chalk4.cyan("\u2753 ") + chalk4.bold(question));
5015
4852
  process.stdout.write(chalk4.cyan("> "));
5016
- return new Promise((resolve8) => {
4853
+ return new Promise((resolve7) => {
5017
4854
  let completed = false;
5018
4855
  const cleanup = (answer) => {
5019
4856
  if (completed) return;
@@ -5023,7 +4860,7 @@ function promptUser(rl, question) {
5023
4860
  rl.pause();
5024
4861
  rlAny.output = savedOutput;
5025
4862
  askUserContext.prompting = false;
5026
- resolve8(answer);
4863
+ resolve7(answer);
5027
4864
  };
5028
4865
  const onLine = (line) => {
5029
4866
  cleanup(line);
@@ -5255,8 +5092,8 @@ function formatResults2(query, data, _requested) {
5255
5092
  }
5256
5093
 
5257
5094
  // src/agents/agent-config.ts
5258
- import { existsSync as existsSync12, readdirSync as readdirSync6, readFileSync as readFileSync8 } from "fs";
5259
- import { join as join7 } from "path";
5095
+ import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync7 } from "fs";
5096
+ import { join as join6 } from "path";
5260
5097
  import { homedir as homedir4 } from "os";
5261
5098
  var READ_ONLY_TOOLS2 = /* @__PURE__ */ new Set([
5262
5099
  "read_file",
@@ -5351,13 +5188,13 @@ function parseAgentConfig(raw, source, path3) {
5351
5188
  return cfg;
5352
5189
  }
5353
5190
  function loadAgentDir(dir, source) {
5354
- if (!existsSync12(dir)) return [];
5191
+ if (!existsSync11(dir)) return [];
5355
5192
  const out = [];
5356
5193
  for (const file of readdirSync6(dir)) {
5357
5194
  if (!file.endsWith(".json")) continue;
5358
- const path3 = join7(dir, file);
5195
+ const path3 = join6(dir, file);
5359
5196
  try {
5360
- const parsed = JSON.parse(readFileSync8(path3, "utf-8"));
5197
+ const parsed = JSON.parse(readFileSync7(path3, "utf-8"));
5361
5198
  const cfg = parseAgentConfig(parsed, source, path3);
5362
5199
  if (cfg) out.push(cfg);
5363
5200
  } catch {
@@ -5367,8 +5204,8 @@ function loadAgentDir(dir, source) {
5367
5204
  }
5368
5205
  function getAgentDirs(configDir, cwd = process.cwd()) {
5369
5206
  return {
5370
- user: join7(configDir ?? join7(homedir4(), CONFIG_DIR_NAME), "agents"),
5371
- project: join7(cwd, CONFIG_DIR_NAME, "agents")
5207
+ user: join6(configDir ?? join6(homedir4(), CONFIG_DIR_NAME), "agents"),
5208
+ project: join6(cwd, CONFIG_DIR_NAME, "agents")
5372
5209
  };
5373
5210
  }
5374
5211
  function listAgentConfigs(configDir, cwd = process.cwd()) {
@@ -5415,7 +5252,7 @@ var nextId = 1;
5415
5252
  var preferredAgentName = "worker";
5416
5253
  var runs = [];
5417
5254
  var MAX_RUNS = 50;
5418
- function nowIso2() {
5255
+ function nowIso() {
5419
5256
  return (/* @__PURE__ */ new Date()).toISOString();
5420
5257
  }
5421
5258
  function startAgentRun(input) {
@@ -5424,7 +5261,7 @@ function startAgentRun(input) {
5424
5261
  agentName: input.agentName,
5425
5262
  task: input.task,
5426
5263
  status: "running",
5427
- startedAt: nowIso2(),
5264
+ startedAt: nowIso(),
5428
5265
  toolNames: [],
5429
5266
  agentIndex: input.agentIndex,
5430
5267
  stopRequested: false
@@ -5442,7 +5279,7 @@ function finishAgentRun(runId, status, summary, error) {
5442
5279
  const run = runs.find((r) => r.id === runId);
5443
5280
  if (!run) return;
5444
5281
  run.status = status;
5445
- run.finishedAt = nowIso2();
5282
+ run.finishedAt = nowIso();
5446
5283
  if (summary) run.summary = summary;
5447
5284
  if (error) run.error = error;
5448
5285
  }
@@ -5868,7 +5705,7 @@ var spawnAgentTool = {
5868
5705
 
5869
5706
  // src/tools/builtin/task-manager.ts
5870
5707
  import { spawn as spawn4 } from "child_process";
5871
- import { randomUUID as randomUUID2 } from "crypto";
5708
+ import { randomUUID } from "crypto";
5872
5709
  import { platform as platform5 } from "os";
5873
5710
  var MAX_OUTPUT_CHARS = 1e4;
5874
5711
  var MAX_OUTPUT_BYTES = 64 * 1024;
@@ -5901,7 +5738,7 @@ function createTask(command, description) {
5901
5738
  }
5902
5739
  }
5903
5740
  }
5904
- const id = randomUUID2().slice(0, 8);
5741
+ const id = randomUUID().slice(0, 8);
5905
5742
  const { shell, args } = shellInvocation(command);
5906
5743
  const proc = spawn4(shell, args, {
5907
5744
  stdio: ["ignore", "pipe", "pipe"],
@@ -6083,14 +5920,14 @@ var taskStopTool = {
6083
5920
 
6084
5921
  // src/tools/builtin/git-tools.ts
6085
5922
  import { execFileSync as execFileSync2 } from "child_process";
6086
- import { existsSync as existsSync13 } from "fs";
6087
- import { join as join8 } from "path";
5923
+ import { existsSync as existsSync12 } from "fs";
5924
+ import { join as join7 } from "path";
6088
5925
  function assertGitRepo(cwd) {
6089
5926
  let dir = cwd;
6090
5927
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
6091
5928
  while (dir && dir !== root) {
6092
- if (existsSync13(join8(dir, ".git"))) return;
6093
- const parent = join8(dir, "..");
5929
+ if (existsSync12(join7(dir, ".git"))) return;
5930
+ const parent = join7(dir, "..");
6094
5931
  if (parent === dir) break;
6095
5932
  dir = parent;
6096
5933
  }
@@ -6355,9 +6192,9 @@ ${commitOutput.trim()}`;
6355
6192
  };
6356
6193
 
6357
6194
  // src/tools/builtin/notebook-edit.ts
6358
- import { readFileSync as readFileSync9, existsSync as existsSync14 } from "fs";
6195
+ import { readFileSync as readFileSync8, existsSync as existsSync13 } from "fs";
6359
6196
  import { writeFile } from "fs/promises";
6360
- import { resolve as resolve7, extname as extname2 } from "path";
6197
+ import { resolve as resolve6, extname as extname2 } from "path";
6361
6198
  var notebookEditTool = {
6362
6199
  definition: {
6363
6200
  name: "notebook_edit",
@@ -6406,14 +6243,14 @@ var notebookEditTool = {
6406
6243
  if (!Number.isInteger(cellIndexRaw) || cellIndexRaw < 1) {
6407
6244
  throw new ToolError("notebook_edit", "cell_index must be a positive integer (1-based)");
6408
6245
  }
6409
- const absPath = resolve7(filePath);
6246
+ const absPath = resolve6(filePath);
6410
6247
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
6411
6248
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
6412
6249
  }
6413
- if (!existsSync14(absPath)) {
6250
+ if (!existsSync13(absPath)) {
6414
6251
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
6415
6252
  }
6416
- const raw = readFileSync9(absPath, "utf-8");
6253
+ const raw = readFileSync8(absPath, "utf-8");
6417
6254
  const nb = parseNotebook(raw);
6418
6255
  const cellIdx0 = cellIndexRaw - 1;
6419
6256
  undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
@@ -6830,8 +6667,8 @@ function estimateToolDefinitionTokens(def) {
6830
6667
 
6831
6668
  // src/tools/registry.ts
6832
6669
  import { pathToFileURL } from "url";
6833
- import { existsSync as existsSync15, mkdirSync as mkdirSync4, readdirSync as readdirSync7 } from "fs";
6834
- import { join as join9 } from "path";
6670
+ import { existsSync as existsSync14, mkdirSync as mkdirSync3, readdirSync as readdirSync7 } from "fs";
6671
+ import { join as join8 } from "path";
6835
6672
  var ToolRegistry = class {
6836
6673
  tools = /* @__PURE__ */ new Map();
6837
6674
  pluginToolNames = /* @__PURE__ */ new Set();
@@ -6992,9 +6829,9 @@ var ToolRegistry = class {
6992
6829
  * Returns the number of successfully loaded plugins.
6993
6830
  */
6994
6831
  async loadPlugins(pluginsDir, allowPlugins = false) {
6995
- if (!existsSync15(pluginsDir)) {
6832
+ if (!existsSync14(pluginsDir)) {
6996
6833
  try {
6997
- mkdirSync4(pluginsDir, { recursive: true });
6834
+ mkdirSync3(pluginsDir, { recursive: true });
6998
6835
  } catch {
6999
6836
  }
7000
6837
  return 0;
@@ -7018,12 +6855,12 @@ var ToolRegistry = class {
7018
6855
  process.stderr.write(
7019
6856
  `
7020
6857
  [plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
7021
- ` + files.map((f) => ` + ${join9(pluginsDir, f)}`).join("\n") + "\n\n"
6858
+ ` + files.map((f) => ` + ${join8(pluginsDir, f)}`).join("\n") + "\n\n"
7022
6859
  );
7023
6860
  let loaded = 0;
7024
6861
  for (const file of files) {
7025
6862
  try {
7026
- const fileUrl = pathToFileURL(join9(pluginsDir, file)).href;
6863
+ const fileUrl = pathToFileURL(join8(pluginsDir, file)).href;
7027
6864
  const mod = await import(fileUrl);
7028
6865
  const tool = mod.tool ?? mod.default?.tool ?? mod.default;
7029
6866
  if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
@@ -7056,16 +6893,6 @@ export {
7056
6893
  theme,
7057
6894
  undoStack,
7058
6895
  renderDiff,
7059
- memoryStorePath,
7060
- memoryMarkdownPath,
7061
- isMemoryExpired,
7062
- listMemoryEntries,
7063
- addMemoryEntry,
7064
- updateMemoryApproval,
7065
- deleteMemoryEntry,
7066
- expireMemoryEntry,
7067
- exportMemoryEntries,
7068
- formatMemoryForPrompt,
7069
6896
  recordRecentlyDeniedAutoAction,
7070
6897
  getRecentlyDeniedAutoActions,
7071
6898
  clearRecentlyDeniedAutoActions,
@@ -2,6 +2,9 @@
2
2
  import {
3
3
  ProviderRegistry
4
4
  } from "./chunk-QYQI7ZWK.js";
5
+ import {
6
+ ConfigManager
7
+ } from "./chunk-WMAXYDCB.js";
5
8
  import {
6
9
  getActivePluginAssets,
7
10
  getPendingHookTrust,
@@ -11,10 +14,7 @@ import {
11
14
  listHooks,
12
15
  listInstalledPlugins,
13
16
  resetStats
14
- } from "./chunk-2LLQOMMW.js";
15
- import {
16
- ConfigManager
17
- } from "./chunk-IV6GCDVR.js";
17
+ } from "./chunk-J363BGOH.js";
18
18
  import {
19
19
  getGitRoot
20
20
  } from "./chunk-HOSJZMQS.js";
@@ -26,7 +26,7 @@ import {
26
26
  MCP_PROJECT_CONFIG_NAME,
27
27
  MEMORY_FILE_NAME,
28
28
  VERSION
29
- } from "./chunk-SMQHEJSH.js";
29
+ } from "./chunk-Y4CMNXZH.js";
30
30
 
31
31
  // src/diagnostics/doctor-report.ts
32
32
  import { existsSync as existsSync3, statSync as statSync2 } from "fs";
@@ -5,9 +5,11 @@ import {
5
5
  embedOne
6
6
  } from "./chunk-KHYD3WXE.js";
7
7
  import {
8
- getIndexMtimeMap,
8
+ getIndexMtimeMap
9
+ } from "./chunk-PJ3ONBWZ.js";
10
+ import {
9
11
  redactString
10
- } from "./chunk-KCEO2XJ4.js";
12
+ } from "./chunk-B5TYJO7V.js";
11
13
  import {
12
14
  atomicWriteFileSync
13
15
  } from "./chunk-IW3Q7AE5.js";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CONFIG_DIR_NAME,
4
4
  PLUGINS_DIR_NAME
5
- } from "./chunk-SMQHEJSH.js";
5
+ } from "./chunk-Y4CMNXZH.js";
6
6
  import {
7
7
  atomicWriteFileSync
8
8
  } from "./chunk-IW3Q7AE5.js";