jinzd-ai-cli 0.4.215 → 0.4.217

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 (31) hide show
  1. package/README.md +5 -2
  2. package/dist/{batch-74H5SA7P.js → batch-YCOVKTXD.js} +2 -2
  3. package/dist/{chat-index-R2E27VXN.js → chat-index-PS274XM7.js} +1 -1
  4. package/dist/{chunk-ZOF5NFKW.js → chunk-524WZOKS.js} +1 -1
  5. package/dist/{ci-3ALK2XJN.js → chunk-KOPUCJXM.js} +40 -46
  6. package/dist/{chunk-6C3JYNM6.js → chunk-MGBMNCHG.js} +279 -54
  7. package/dist/{chunk-KL7UBVSQ.js → chunk-OUAZOE5U.js} +2 -2
  8. package/dist/{chunk-RS4WBI73.js → chunk-QAYOI57M.js} +3 -1
  9. package/dist/{chunk-X6OXS7KU.js → chunk-SNJAOXFT.js} +1 -1
  10. package/dist/{chunk-OQGVGPEK.js → chunk-VHY6NVMQ.js} +2 -0
  11. package/dist/{chunk-3TSHNZKI.js → chunk-VTH7BLXK.js} +1 -1
  12. package/dist/{chunk-2224JGA6.js → chunk-WKOQ5CYC.js} +1 -1
  13. package/dist/chunk-WZ3VKLF3.js +105 -0
  14. package/dist/{chunk-7JES2NWR.js → chunk-XJGEQIYS.js} +3 -1
  15. package/dist/ci-52RZIYWB.js +19 -0
  16. package/dist/ci-format-73UXKE65.js +18 -0
  17. package/dist/{constants-IN2HXJK7.js → constants-DIXAD35W.js} +3 -1
  18. package/dist/{doctor-cli-7XL4TCVT.js → doctor-cli-7GOQPULZ.js} +4 -4
  19. package/dist/electron-server.js +562 -318
  20. package/dist/{hub-VAOG5EY6.js → hub-GIGBITZN.js} +1 -1
  21. package/dist/index.js +169 -73
  22. package/dist/pr-KPQ5RPKB.js +267 -0
  23. package/dist/{run-tests-J2JQ57R3.js → run-tests-K7QR5QN4.js} +1 -1
  24. package/dist/{run-tests-QJD43AML.js → run-tests-ZDSA3QES.js} +2 -2
  25. package/dist/{server-WDLV3W4F.js → server-22YF3U34.js} +5 -4
  26. package/dist/{server-VGPZOISQ.js → server-QT3SC2KI.js} +129 -86
  27. package/dist/{task-orchestrator-QFOCO3N7.js → task-orchestrator-BHQQCVTY.js} +5 -4
  28. package/dist/{usage-IEB476NE.js → usage-WZZFSFLM.js} +2 -2
  29. package/dist/web/client/app.js +53 -2
  30. package/dist/web/client/index.html +11 -1
  31. package/package.json +1 -1
@@ -24,7 +24,7 @@ import {
24
24
  scanDirTree,
25
25
  scanProject,
26
26
  setupProxy
27
- } from "./chunk-KL7UBVSQ.js";
27
+ } from "./chunk-OUAZOE5U.js";
28
28
  import {
29
29
  buildReviewPrompt,
30
30
  buildSecurityReviewPrompt
@@ -32,21 +32,29 @@ import {
32
32
  import {
33
33
  ToolExecutor,
34
34
  ToolRegistry,
35
+ addMemoryEntry,
35
36
  askUserContext,
36
37
  checkNetworkPolicy,
37
38
  checkPermissionWithProfile,
38
39
  cleanupRejectedTeeFile,
39
40
  clearRecentlyDeniedAutoActions,
40
41
  defaultActionClassifier,
42
+ deleteMemoryEntry,
41
43
  estimateTokens,
44
+ expireMemoryEntry,
45
+ exportMemoryEntries,
46
+ formatMemoryForPrompt,
42
47
  formatPermissionProfileWarning,
43
48
  getPendingHookTrust,
44
49
  getPreferredAgentName,
45
50
  getRecentlyDeniedAutoActions,
46
51
  googleSearchContext,
47
52
  groupCallsByPhase,
53
+ isMemoryExpired,
48
54
  listAgentConfigs,
49
55
  listAgentRuns,
56
+ listMemoryEntries,
57
+ memoryStorePath,
50
58
  recordRecentlyDeniedAutoAction,
51
59
  renderDiff,
52
60
  requestAgentStop,
@@ -60,11 +68,12 @@ import {
60
68
  setPreferredAgentName,
61
69
  spawnAgentContext,
62
70
  truncateOutput,
63
- undoStack
64
- } from "./chunk-6C3JYNM6.js";
71
+ undoStack,
72
+ updateMemoryApproval
73
+ } from "./chunk-MGBMNCHG.js";
65
74
  import "./chunk-T2NL5ZIA.js";
66
75
  import "./chunk-BXP6YZ2P.js";
67
- import "./chunk-3TSHNZKI.js";
76
+ import "./chunk-VTH7BLXK.js";
68
77
  import {
69
78
  SessionManager,
70
79
  getContentText
@@ -78,7 +87,7 @@ import {
78
87
  } from "./chunk-QMXC327F.js";
79
88
  import {
80
89
  runTool
81
- } from "./chunk-X6OXS7KU.js";
90
+ } from "./chunk-SNJAOXFT.js";
82
91
  import {
83
92
  CONTENT_ONLY_STREAM_REMINDER,
84
93
  TEE_FINAL_USER_NUDGE,
@@ -97,7 +106,7 @@ import {
97
106
  } from "./chunk-XPBEJB27.js";
98
107
  import {
99
108
  ConfigManager
100
- } from "./chunk-2224JGA6.js";
109
+ } from "./chunk-WKOQ5CYC.js";
101
110
  import "./chunk-TZQHYZKT.js";
102
111
  import {
103
112
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -109,14 +118,13 @@ import {
109
118
  DESCRIPTION,
110
119
  MCP_PROJECT_CONFIG_NAME,
111
120
  MEMORY_FILE_NAME,
112
- MEMORY_MAX_CHARS,
113
121
  PLAN_MODE_READONLY_TOOLS,
114
122
  PLAN_MODE_SYSTEM_ADDON,
115
123
  PLUGINS_DIR_NAME,
116
124
  SKILLS_DIR_NAME,
117
125
  VERSION,
118
126
  buildUserIdentityPrompt
119
- } from "./chunk-7JES2NWR.js";
127
+ } from "./chunk-XJGEQIYS.js";
120
128
  import {
121
129
  formatGitContextForPrompt,
122
130
  getGitContext,
@@ -138,8 +146,8 @@ import {
138
146
  import express from "express";
139
147
  import { createServer } from "http";
140
148
  import { WebSocketServer } from "ws";
141
- import { join as join3, dirname as dirname2, resolve as resolve2, relative, sep } from "path";
142
- import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync2, realpathSync } from "fs";
149
+ import { join as join2, dirname as dirname2, resolve as resolve2, relative, sep } from "path";
150
+ import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, realpathSync } from "fs";
143
151
  import { networkInterfaces } from "os";
144
152
 
145
153
  // src/web/tool-executor-web.ts
@@ -589,8 +597,6 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
589
597
  };
590
598
 
591
599
  // src/core/system-prompt-builder.ts
592
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
593
- import { join } from "path";
594
600
  function buildSystemPromptParts(ctx) {
595
601
  const now = /* @__PURE__ */ new Date();
596
602
  const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
@@ -644,24 +650,12 @@ ${ctx.activeSkill.content}`);
644
650
  return { stable: stableParts.join("\n\n---\n\n"), volatile };
645
651
  }
646
652
  function loadMemoryContent(configDir) {
647
- const memoryPath = join(configDir, MEMORY_FILE_NAME);
648
- if (!existsSync2(memoryPath)) return null;
649
- let content = readFileSync2(memoryPath, "utf-8").trim();
650
- if (!content) return null;
651
- if (content.length > MEMORY_MAX_CHARS) {
652
- content = content.slice(-MEMORY_MAX_CHARS);
653
- const firstEntry = content.indexOf("\n## ");
654
- if (firstEntry !== -1) {
655
- content = content.slice(firstEntry + 1);
656
- }
657
- }
658
- const entryCount = (content.match(/^## \d{4}-\d{2}-\d{2}/gm) ?? []).length;
659
- return { content, entryCount };
653
+ return formatMemoryForPrompt(configDir, process.cwd());
660
654
  }
661
655
 
662
656
  // src/web/session-handler.ts
663
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream } from "fs";
664
- import { join as join2, resolve, dirname } from "path";
657
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, mkdirSync, readdirSync, statSync, createWriteStream } from "fs";
658
+ import { join, resolve, dirname } from "path";
665
659
  function lastAssistantText(messages) {
666
660
  for (let i = messages.length - 1; i >= 0; i--) {
667
661
  const m = messages[i];
@@ -867,6 +861,8 @@ var SessionHandler = class {
867
861
  return this.handleMemoryStatus();
868
862
  case "memory_rebuild":
869
863
  return this.handleMemoryRebuild(Boolean(msg.full));
864
+ case "memory_manage":
865
+ return this.handleMemoryManage(msg.action, msg.id, msg.expiresAt);
870
866
  case "auto_pause_response": {
871
867
  const resolve3 = this.pendingAutoPause.get(msg.requestId);
872
868
  if (resolve3) {
@@ -2237,9 +2233,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2237
2233
  let modifiedFiles = 0;
2238
2234
  const diffLines = [];
2239
2235
  for (const [filePath, { earliest }] of fileMap) {
2240
- const currentContent = existsSync3(filePath) ? (() => {
2236
+ const currentContent = existsSync2(filePath) ? (() => {
2241
2237
  try {
2242
- return readFileSync3(filePath, "utf-8");
2238
+ return readFileSync2(filePath, "utf-8");
2243
2239
  } catch {
2244
2240
  return null;
2245
2241
  }
@@ -2755,7 +2751,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2755
2751
  case "test": {
2756
2752
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2757
2753
  try {
2758
- const { executeTests } = await import("./run-tests-QJD43AML.js");
2754
+ const { executeTests } = await import("./run-tests-ZDSA3QES.js");
2759
2755
  const argStr = args.join(" ").trim();
2760
2756
  let testArgs = {};
2761
2757
  if (argStr) {
@@ -2772,9 +2768,9 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2772
2768
  // ── /init ───────────────────────────────────────────────────────
2773
2769
  case "init": {
2774
2770
  const cwd = process.cwd();
2775
- const targetPath = join2(cwd, "AICLI.md");
2771
+ const targetPath = join(cwd, "AICLI.md");
2776
2772
  const force = args.includes("--force");
2777
- if (existsSync3(targetPath) && !force) {
2773
+ if (existsSync2(targetPath) && !force) {
2778
2774
  this.send({ type: "info", message: `AICLI.md already exists at ${targetPath}
2779
2775
  Use /init --force to overwrite.` });
2780
2776
  break;
@@ -2807,7 +2803,7 @@ Use /context reload to load it.` });
2807
2803
  lines.push("**Config Files:**");
2808
2804
  lines.push(` Dir: ${configDir}`);
2809
2805
  const checkFile = (label, filePath) => {
2810
- const exists = existsSync3(filePath);
2806
+ const exists = existsSync2(filePath);
2811
2807
  let extra = "";
2812
2808
  if (exists) {
2813
2809
  try {
@@ -2817,9 +2813,9 @@ Use /context reload to load it.` });
2817
2813
  }
2818
2814
  lines.push(` ${exists ? "\u2713" : "\u2013"} ${label.padEnd(14)} ${exists ? filePath + extra : "(not found)"}`);
2819
2815
  };
2820
- checkFile("config.json", join2(configDir, "config.json"));
2821
- checkFile("memory.md", join2(configDir, MEMORY_FILE_NAME));
2822
- checkFile("dev-state.md", join2(configDir, "dev-state.md"));
2816
+ checkFile("config.json", join(configDir, "config.json"));
2817
+ checkFile("memory.md", join(configDir, MEMORY_FILE_NAME));
2818
+ checkFile("dev-state.md", join(configDir, "dev-state.md"));
2823
2819
  lines.push("");
2824
2820
  if (this.mcpManager) {
2825
2821
  lines.push("**MCP Servers:**");
@@ -3007,7 +3003,7 @@ Use /add-dir remove to clear.` : "No directories added.\nUsage: /add-dir <path>
3007
3003
  break;
3008
3004
  }
3009
3005
  const dirPath = resolve(sub);
3010
- if (!existsSync3(dirPath)) {
3006
+ if (!existsSync2(dirPath)) {
3011
3007
  this.send({ type: "error", message: `Directory not found: ${dirPath}` });
3012
3008
  break;
3013
3009
  }
@@ -3023,8 +3019,8 @@ It will be included in AI context for subsequent messages.` });
3023
3019
  // ── /commands ───────────────────────────────────────────────────
3024
3020
  case "commands": {
3025
3021
  const configDir = this.config.getConfigDir();
3026
- const commandsDir = join2(configDir, CUSTOM_COMMANDS_DIR_NAME);
3027
- if (!existsSync3(commandsDir)) {
3022
+ const commandsDir = join(configDir, CUSTOM_COMMANDS_DIR_NAME);
3023
+ if (!existsSync2(commandsDir)) {
3028
3024
  this.send({ type: "info", message: `No custom commands directory.
3029
3025
  Create: ${commandsDir}/ with .md files.` });
3030
3026
  break;
@@ -3050,7 +3046,7 @@ Add .md files to create commands.` });
3050
3046
  // ── /plugins ────────────────────────────────────────────────────
3051
3047
  case "plugins": {
3052
3048
  const configDir = this.config.getConfigDir();
3053
- const pluginsDir = join2(configDir, PLUGINS_DIR_NAME);
3049
+ const pluginsDir = join(configDir, PLUGINS_DIR_NAME);
3054
3050
  const pluginTools = this.toolRegistry.listPluginTools();
3055
3051
  const lines = [`\u{1F50C} **Plugins:**`, `Dir: ${pluginsDir}`, ""];
3056
3052
  if (pluginTools.length === 0) {
@@ -3247,48 +3243,95 @@ Add .md files to create commands.` });
3247
3243
  }
3248
3244
  memoryShow() {
3249
3245
  const configDir = this.config.getConfigDir();
3250
- const memPath = join2(configDir, MEMORY_FILE_NAME);
3251
- let content = "";
3252
- try {
3253
- if (existsSync3(memPath)) {
3254
- content = readFileSync3(memPath, "utf-8");
3255
- }
3256
- } catch (err) {
3257
- process.stderr.write(`[web] Failed to read memory file: ${err instanceof Error ? err.message : err}
3258
- `);
3259
- }
3246
+ const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
3247
+ const content = entries.length === 0 ? "(empty \u2014 no persistent memory entries yet)" : entries.map((entry) => {
3248
+ const status = isMemoryExpired(entry) ? "expired" : entry.approved ? "approved" : "pending";
3249
+ const source = entry.sourceSession ? `session ${entry.sourceSession.slice(0, 8)}` : entry.sourceProject ? `project ${entry.sourceProject}` : "manual/tool";
3250
+ return `## ${entry.id.slice(0, 8)} \xB7 ${status} \xB7 scope:${entry.scope} \xB7 sensitivity:${entry.sensitivity}
3251
+ Source: ${source}
3252
+ ${entry.content}`;
3253
+ }).join("\n\n");
3260
3254
  this.send({
3261
3255
  type: "memory_content",
3262
- content: content || "(empty \u2014 no persistent memory entries yet)",
3263
- filePath: memPath
3256
+ content,
3257
+ filePath: memoryStorePath(configDir)
3264
3258
  });
3259
+ this.sendMemoryEntries();
3265
3260
  }
3266
3261
  memoryAdd(text) {
3267
- const configDir = this.config.getConfigDir();
3268
- const memPath = join2(configDir, MEMORY_FILE_NAME);
3269
3262
  try {
3270
- mkdirSync(configDir, { recursive: true });
3271
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
3272
- const previous = existsSync3(memPath) ? readFileSync3(memPath, "utf-8") : "";
3273
- const entry = `
3274
- - [${timestamp}] ${text}
3275
- `;
3276
- atomicWriteFileSync(memPath, previous + entry);
3277
- this.send({ type: "info", message: `\u{1F4DD} Memory entry added: "${text}"` });
3263
+ const entry = addMemoryEntry(this.config.getConfigDir(), text, {
3264
+ scope: "personal",
3265
+ cwd: process.cwd(),
3266
+ redact: true
3267
+ });
3268
+ const status = entry.approved ? "approved" : "pending approval";
3269
+ this.send({ type: "info", message: `\u{1F4DD} Memory entry ${entry.id.slice(0, 8)} added (${status}).` });
3270
+ this.sendMemoryEntries();
3278
3271
  } catch (err) {
3279
3272
  this.send({ type: "error", message: `Failed to write memory: ${err.message}` });
3280
3273
  }
3281
3274
  }
3282
3275
  memoryClear() {
3283
3276
  const configDir = this.config.getConfigDir();
3284
- const memPath = join2(configDir, MEMORY_FILE_NAME);
3285
3277
  try {
3286
- atomicWriteFileSync(memPath, "");
3278
+ atomicWriteFileSync(memoryStorePath(configDir), "");
3279
+ atomicWriteFileSync(join(configDir, MEMORY_FILE_NAME), "");
3287
3280
  this.send({ type: "info", message: "\u{1F5D1}\uFE0F Persistent memory cleared." });
3281
+ this.sendMemoryEntries();
3288
3282
  } catch (err) {
3289
3283
  this.send({ type: "error", message: `Failed to clear memory: ${err.message}` });
3290
3284
  }
3291
3285
  }
3286
+ sendMemoryEntries() {
3287
+ const configDir = this.config.getConfigDir();
3288
+ const entries = listMemoryEntries(configDir, { includeExpired: true, includeRejected: true });
3289
+ this.send({
3290
+ type: "memory_entries",
3291
+ storePath: memoryStorePath(configDir),
3292
+ entries: entries.map((entry) => ({
3293
+ id: entry.id,
3294
+ content: entry.content,
3295
+ scope: entry.scope,
3296
+ sourceSession: entry.sourceSession,
3297
+ sourceProject: entry.sourceProject,
3298
+ createdAt: entry.createdAt,
3299
+ updatedAt: entry.updatedAt,
3300
+ expiresAt: entry.expiresAt,
3301
+ sensitivity: entry.sensitivity,
3302
+ approved: entry.approved,
3303
+ expired: isMemoryExpired(entry),
3304
+ redactedKinds: entry.redactedKinds
3305
+ }))
3306
+ });
3307
+ }
3308
+ handleMemoryManage(action, id, expiresAt) {
3309
+ const configDir = this.config.getConfigDir();
3310
+ try {
3311
+ if (action === "approve" || action === "reject") {
3312
+ if (!id) throw new Error(`${action} requires id`);
3313
+ updateMemoryApproval(configDir, id, action === "approve");
3314
+ } else if (action === "delete") {
3315
+ if (!id) throw new Error("delete requires id");
3316
+ deleteMemoryEntry(configDir, id);
3317
+ } else if (action === "expire") {
3318
+ if (!id) throw new Error("expire requires id");
3319
+ expireMemoryEntry(configDir, id, expiresAt);
3320
+ } else if (action === "export") {
3321
+ this.send({
3322
+ type: "export_data",
3323
+ format: "json",
3324
+ filename: `aicli-memory-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`,
3325
+ content: exportMemoryEntries(configDir, process.cwd())
3326
+ });
3327
+ } else if (action !== "list") {
3328
+ throw new Error(`unknown memory action: ${action}`);
3329
+ }
3330
+ this.sendMemoryEntries();
3331
+ } catch (err) {
3332
+ this.send({ type: "error", message: `Memory management failed: ${err instanceof Error ? err.message : String(err)}` });
3333
+ }
3334
+ }
3292
3335
  // ── B4 chat memory recall (v0.4.90+) ──────────────────────────────
3293
3336
  // Lazy-imported so the 117 MB embedder stays out of the load path for
3294
3337
  // clients that never open the Memory panel.
@@ -3578,8 +3621,8 @@ async function startWebServer(options = {}) {
3578
3621
  }
3579
3622
  }
3580
3623
  let skillManager = null;
3581
- const skillsDir = join3(config.getConfigDir(), SKILLS_DIR_NAME);
3582
- if (existsSync4(skillsDir)) {
3624
+ const skillsDir = join2(config.getConfigDir(), SKILLS_DIR_NAME);
3625
+ if (existsSync3(skillsDir)) {
3583
3626
  skillManager = new SkillManager(skillsDir, config.get("ui").skillSizeWarn);
3584
3627
  skillManager.loadSkills();
3585
3628
  const count = skillManager.listSkills().length;
@@ -3650,18 +3693,18 @@ async function startWebServer(options = {}) {
3650
3693
  next();
3651
3694
  };
3652
3695
  const moduleDir = getModuleDir();
3653
- let clientDir = join3(moduleDir, "web", "client");
3654
- if (!existsSync4(clientDir)) {
3655
- clientDir = join3(moduleDir, "client");
3696
+ let clientDir = join2(moduleDir, "web", "client");
3697
+ if (!existsSync3(clientDir)) {
3698
+ clientDir = join2(moduleDir, "client");
3656
3699
  }
3657
- if (!existsSync4(clientDir)) {
3658
- clientDir = join3(moduleDir, "..", "..", "src", "web", "client");
3700
+ if (!existsSync3(clientDir)) {
3701
+ clientDir = join2(moduleDir, "..", "..", "src", "web", "client");
3659
3702
  }
3660
- if (!existsSync4(clientDir)) {
3661
- clientDir = join3(process.cwd(), "src", "web", "client");
3703
+ if (!existsSync3(clientDir)) {
3704
+ clientDir = join2(process.cwd(), "src", "web", "client");
3662
3705
  }
3663
3706
  console.log(` Static files: ${clientDir}`);
3664
- app.use("/vendor", express.static(join3(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
3707
+ app.use("/vendor", express.static(join2(clientDir, "vendor"), { maxAge: "1y", immutable: true }));
3665
3708
  app.use(express.static(clientDir));
3666
3709
  app.get("/api/status", (_req, res) => {
3667
3710
  res.json({
@@ -3728,7 +3771,7 @@ async function startWebServer(options = {}) {
3728
3771
  app.get("/api/files", requireAuth, (req, res) => {
3729
3772
  const cwd = process.cwd();
3730
3773
  const prefix = req.query.prefix || "";
3731
- const targetDir = join3(cwd, prefix);
3774
+ const targetDir = join2(cwd, prefix);
3732
3775
  try {
3733
3776
  const canonicalTarget = realpathSync(resolve2(targetDir));
3734
3777
  const canonicalCwd = realpathSync(resolve2(cwd));
@@ -3745,7 +3788,7 @@ async function startWebServer(options = {}) {
3745
3788
  const entries = readdirSync2(targetDir, { withFileTypes: true });
3746
3789
  const files = entries.filter((e) => !SKIP.has(e.name) && !e.name.startsWith(".")).slice(0, 50).map((e) => ({
3747
3790
  name: e.name,
3748
- path: relative(cwd, join3(targetDir, e.name)).replace(/\\/g, "/"),
3791
+ path: relative(cwd, join2(targetDir, e.name)).replace(/\\/g, "/"),
3749
3792
  isDir: e.isDirectory()
3750
3793
  }));
3751
3794
  res.json({ files });
@@ -3781,8 +3824,8 @@ async function startWebServer(options = {}) {
3781
3824
  try {
3782
3825
  const authUser = req._authUser;
3783
3826
  const histDir = authUser ? getUserShared(authUser).config.getHistoryDir() : config.getHistoryDir();
3784
- const filePath = join3(histDir, `${id}.json`);
3785
- if (!existsSync4(filePath)) {
3827
+ const filePath = join2(histDir, `${id}.json`);
3828
+ if (!existsSync3(filePath)) {
3786
3829
  res.status(404).json({ error: "Session not found" });
3787
3830
  return;
3788
3831
  }
@@ -3797,7 +3840,7 @@ async function startWebServer(options = {}) {
3797
3840
  res.status(404).json({ error: "Session not found" });
3798
3841
  return;
3799
3842
  }
3800
- const data = JSON.parse(readFileSync4(filePath, "utf-8"));
3843
+ const data = JSON.parse(readFileSync3(filePath, "utf-8"));
3801
3844
  res.json({ session: data });
3802
3845
  } catch (err) {
3803
3846
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
@@ -3810,7 +3853,7 @@ async function startWebServer(options = {}) {
3810
3853
  return;
3811
3854
  }
3812
3855
  const cwd = process.cwd();
3813
- const fullPath = resolve2(join3(cwd, filePath));
3856
+ const fullPath = resolve2(join2(cwd, filePath));
3814
3857
  try {
3815
3858
  const canonicalFull = realpathSync(fullPath);
3816
3859
  const canonicalCwd = realpathSync(resolve2(cwd));
@@ -3828,7 +3871,7 @@ async function startWebServer(options = {}) {
3828
3871
  res.json({ error: `File too large (${(stat.size / 1024).toFixed(0)} KB, max 512 KB)` });
3829
3872
  return;
3830
3873
  }
3831
- const content = readFileSync4(fullPath, "utf-8");
3874
+ const content = readFileSync3(fullPath, "utf-8");
3832
3875
  res.json({ content, size: stat.size });
3833
3876
  } catch {
3834
3877
  res.json({ error: "Cannot read file" });
@@ -4083,17 +4126,17 @@ function resolveProjectMcpPath() {
4083
4126
  const cwd = process.cwd();
4084
4127
  const gitRoot = getGitRoot(cwd);
4085
4128
  const projectRoot = gitRoot ?? cwd;
4086
- const configPath = join3(projectRoot, MCP_PROJECT_CONFIG_NAME);
4087
- return existsSync4(configPath) ? configPath : null;
4129
+ const configPath = join2(projectRoot, MCP_PROJECT_CONFIG_NAME);
4130
+ return existsSync3(configPath) ? configPath : null;
4088
4131
  }
4089
4132
  function loadProjectMcpConfig() {
4090
4133
  const cwd = process.cwd();
4091
4134
  const gitRoot = getGitRoot(cwd);
4092
4135
  const projectRoot = gitRoot ?? cwd;
4093
- const configPath = join3(projectRoot, MCP_PROJECT_CONFIG_NAME);
4094
- if (!existsSync4(configPath)) return null;
4136
+ const configPath = join2(projectRoot, MCP_PROJECT_CONFIG_NAME);
4137
+ if (!existsSync3(configPath)) return null;
4095
4138
  try {
4096
- const raw = JSON.parse(readFileSync4(configPath, "utf-8"));
4139
+ const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
4097
4140
  return raw.mcpServers ?? raw;
4098
4141
  } catch {
4099
4142
  return null;
@@ -3,13 +3,13 @@ import {
3
3
  ToolRegistry,
4
4
  googleSearchContext,
5
5
  truncateOutput
6
- } from "./chunk-6C3JYNM6.js";
6
+ } from "./chunk-MGBMNCHG.js";
7
7
  import "./chunk-T2NL5ZIA.js";
8
8
  import "./chunk-BXP6YZ2P.js";
9
- import "./chunk-3TSHNZKI.js";
9
+ import "./chunk-VTH7BLXK.js";
10
10
  import {
11
11
  runTool
12
- } from "./chunk-X6OXS7KU.js";
12
+ } from "./chunk-SNJAOXFT.js";
13
13
  import {
14
14
  getDangerLevel,
15
15
  runLeanAgentLoop
@@ -17,7 +17,8 @@ import {
17
17
  import "./chunk-TZQHYZKT.js";
18
18
  import {
19
19
  SUBAGENT_ALLOWED_TOOLS
20
- } from "./chunk-7JES2NWR.js";
20
+ } from "./chunk-XJGEQIYS.js";
21
+ import "./chunk-HOSJZMQS.js";
21
22
  import "./chunk-4BKXL7SM.js";
22
23
  import "./chunk-TB4W4Y4T.js";
23
24
  import "./chunk-KHYD3WXE.js";
@@ -8,9 +8,9 @@ import {
8
8
  } from "./chunk-E44DTERW.js";
9
9
  import {
10
10
  ConfigManager
11
- } from "./chunk-2224JGA6.js";
11
+ } from "./chunk-WKOQ5CYC.js";
12
12
  import "./chunk-TZQHYZKT.js";
13
- import "./chunk-7JES2NWR.js";
13
+ import "./chunk-XJGEQIYS.js";
14
14
  import "./chunk-IW3Q7AE5.js";
15
15
 
16
16
  // src/cli/usage.ts
@@ -200,6 +200,7 @@ function handleServerMessage(msg) {
200
200
  case 'tools_list': renderToolsList(msg); switchSidebarTab('tools'); break;
201
201
  case 'export_data': handleExportData(msg); break;
202
202
  case 'memory_content': handleMemoryContent(msg); break;
203
+ case 'memory_entries': renderMemoryEntries(msg); break;
203
204
  case 'memory_hits': renderMemoryHits(msg); break;
204
205
  case 'memory_status': renderMemoryStatus(msg); break;
205
206
  case 'memory_rebuild_progress': renderMemoryRebuildProgress(msg); break;
@@ -2943,7 +2944,9 @@ function initMemoryPanel() {
2943
2944
  const input = document.getElementById('memory-search-input');
2944
2945
  const btnSearch = document.getElementById('btn-memory-search');
2945
2946
  const btnRefresh = document.getElementById('btn-memory-refresh');
2946
- const btnRebuild = document.getElementById('btn-memory-rebuild');
2947
+ const btnRebuild = document.getElementById('btn-memory-rebuild');
2948
+ const btnManagedRefresh = document.getElementById('btn-memory-managed-refresh');
2949
+ const btnManagedExport = document.getElementById('btn-memory-managed-export');
2947
2950
 
2948
2951
  const runSearch = () => {
2949
2952
  const q = (input?.value || '').trim();
@@ -2978,7 +2981,11 @@ function initMemoryPanel() {
2978
2981
  send({ type: 'memory_rebuild', full: true });
2979
2982
  });
2980
2983
 
2981
- send({ type: 'memory_status_request' });
2984
+ btnManagedRefresh?.addEventListener('click', () => send({ type: 'memory_manage', action: 'list' }));
2985
+ btnManagedExport?.addEventListener('click', () => send({ type: 'memory_manage', action: 'export' }));
2986
+
2987
+ send({ type: 'memory_status_request' });
2988
+ send({ type: 'memory_manage', action: 'list' });
2982
2989
  }
2983
2990
 
2984
2991
  function renderMemoryStatus(msg) {
@@ -2997,6 +3004,50 @@ function renderMemoryStatus(msg) {
2997
3004
  el.title = `model: ${msg.model || '-'}`;
2998
3005
  }
2999
3006
 
3007
+ function renderMemoryEntries(msg) {
3008
+ const el = document.getElementById('memory-managed-list');
3009
+ if (!el) return;
3010
+ const entries = msg.entries || [];
3011
+ if (entries.length === 0) {
3012
+ el.innerHTML = '<div class="opacity-40 py-1">No persistent memories.</div>';
3013
+ return;
3014
+ }
3015
+ el.innerHTML = entries.map((m) => {
3016
+ const status = m.expired ? 'expired' : (m.approved ? 'approved' : 'pending');
3017
+ const statusClass = m.expired ? 'badge-ghost' : (m.approved ? 'badge-success' : 'badge-warning');
3018
+ const source = m.sourceSession ? `session ${m.sourceSession.slice(0, 8)}` : (m.sourceProject ? 'project' : 'manual/tool');
3019
+ const preview = m.content.length > 180 ? m.content.slice(0, 180) + '…' : m.content;
3020
+ const actions = [];
3021
+ if (!m.approved && !m.expired) actions.push(`<button class="btn btn-xs btn-ghost memory-managed-approve" title="Approve">Approve</button>`);
3022
+ if (m.approved && !m.expired) actions.push(`<button class="btn btn-xs btn-ghost memory-managed-reject" title="Reject">Reject</button>`);
3023
+ if (!m.expired) actions.push(`<button class="btn btn-xs btn-ghost memory-managed-expire" title="Expire now">Expire</button>`);
3024
+ actions.push(`<button class="btn btn-xs btn-ghost memory-managed-delete" title="Delete">Delete</button>`);
3025
+ return `
3026
+ <div class="memory-managed-item border border-base-content/10 rounded p-2" data-memory-id="${escapeHtml(m.id)}">
3027
+ <div class="flex items-center gap-1 mb-1">
3028
+ <span class="badge badge-xs ${statusClass}">${status}</span>
3029
+ <span class="badge badge-xs badge-ghost">${escapeHtml(m.scope)}</span>
3030
+ <span class="badge badge-xs badge-ghost">${escapeHtml(m.sensitivity)}</span>
3031
+ <span class="opacity-50 truncate flex-1">${escapeHtml(m.id.slice(0, 8))}</span>
3032
+ </div>
3033
+ <div class="opacity-50 mb-1">source: ${escapeHtml(source)} · ${escapeHtml((m.createdAt || '').replace('T', ' ').slice(0, 16))}</div>
3034
+ <div class="whitespace-pre-wrap leading-snug mb-1">${escapeHtml(preview)}</div>
3035
+ <div class="flex flex-wrap gap-1">${actions.join('')}</div>
3036
+ </div>
3037
+ `;
3038
+ }).join('');
3039
+
3040
+ el.querySelectorAll('.memory-managed-item').forEach((card) => {
3041
+ const id = card.getAttribute('data-memory-id');
3042
+ if (!id) return;
3043
+ card.querySelector('.memory-managed-approve')?.addEventListener('click', () => send({ type: 'memory_manage', action: 'approve', id }));
3044
+ card.querySelector('.memory-managed-reject')?.addEventListener('click', () => send({ type: 'memory_manage', action: 'reject', id }));
3045
+ card.querySelector('.memory-managed-expire')?.addEventListener('click', () => send({ type: 'memory_manage', action: 'expire', id }));
3046
+ card.querySelector('.memory-managed-delete')?.addEventListener('click', () => {
3047
+ if (confirm('Delete this memory entry?')) send({ type: 'memory_manage', action: 'delete', id });
3048
+ });
3049
+ });
3050
+ }
3000
3051
  function renderMemoryHits(msg) {
3001
3052
  const el = document.getElementById('memory-hits');
3002
3053
  if (!el) return;
@@ -192,7 +192,17 @@
192
192
  <button id="btn-memory-refresh" class="btn btn-xs btn-ghost" title="Incremental refresh">↻</button>
193
193
  <button id="btn-memory-rebuild" class="btn btn-xs btn-ghost" title="Full rebuild (slow first run)">⟳ Rebuild</button>
194
194
  </div>
195
- <div id="memory-status" class="text-xs opacity-50 truncate">Loading index status…</div>
195
+ <div id="memory-status" class="text-xs opacity-50 truncate">Loading index status…</div>
196
+ <div id="memory-governance" class="border-t border-base-content/10 pt-2 mt-1">
197
+ <div class="flex items-center gap-1 mb-1">
198
+ <span class="text-xs font-semibold flex-1">Persistent memory</span>
199
+ <button id="btn-memory-managed-refresh" class="btn btn-xs btn-ghost" title="Refresh persistent memory">↻</button>
200
+ <button id="btn-memory-managed-export" class="btn btn-xs btn-ghost" title="Export persistent memory">Export</button>
201
+ </div>
202
+ <div id="memory-managed-list" class="flex flex-col gap-1 max-h-48 overflow-y-auto text-xs">
203
+ <div class="opacity-40 py-1">Loading persistent memory…</div>
204
+ </div>
205
+ </div>
196
206
  </div>
197
207
  <div id="memory-hits" class="flex-1 overflow-y-auto p-2 flex flex-col gap-2 text-sm">
198
208
  <div class="text-xs opacity-40 text-center py-4">Type a query above to recall past chats.</div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.4.215",
3
+ "version": "0.4.217",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",