chat-recall 0.5.31 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4259,7 +4259,7 @@ var require_pino = __commonJS({
4259
4259
  messageKey,
4260
4260
  errorKey,
4261
4261
  nestedKey,
4262
- base: base2,
4262
+ base: base3,
4263
4263
  name,
4264
4264
  level: level2,
4265
4265
  customLevels,
@@ -4298,11 +4298,11 @@ var require_pino = __commonJS({
4298
4298
  [formattersSym]: allFormatters
4299
4299
  });
4300
4300
  let chindings = "";
4301
- if (base2 !== null) {
4301
+ if (base3 !== null) {
4302
4302
  if (name === void 0) {
4303
- chindings = coreChindings(base2);
4303
+ chindings = coreChindings(base3);
4304
4304
  } else {
4305
- chindings = coreChindings(Object.assign({}, base2, { name }));
4305
+ chindings = coreChindings(Object.assign({}, base3, { name }));
4306
4306
  }
4307
4307
  }
4308
4308
  const time2 = timestamp instanceof Function ? timestamp : timestamp ? epochTime : nullTime;
@@ -4743,56 +4743,56 @@ function freshDefaults() {
4743
4743
  team: defaultTeam()
4744
4744
  };
4745
4745
  }
4746
- function mergeSources(base2, partial) {
4746
+ function mergeSources(base3, partial) {
4747
4747
  if (!partial)
4748
- return base2;
4748
+ return base3;
4749
4749
  const enabled = {
4750
- claude: { ...base2.enabled.claude, ...partial.enabled?.claude ?? {} },
4751
- gemini: { ...base2.enabled.gemini, ...partial.enabled?.gemini ?? {} },
4752
- opencode: { ...base2.enabled.opencode, ...partial.enabled?.opencode ?? {} },
4753
- codex: { ...base2.enabled.codex, ...partial.enabled?.codex ?? {} },
4754
- agy: { ...base2.enabled.agy, ...partial.enabled?.agy ?? {} },
4755
- cursor: { ...base2.enabled.cursor, ...partial.enabled?.cursor ?? {} },
4756
- shared: { ...base2.enabled.shared, ...partial.enabled?.shared ?? {} },
4757
- common: { ...base2.enabled.common, ...partial.enabled?.common ?? {} }
4750
+ claude: { ...base3.enabled.claude, ...partial.enabled?.claude ?? {} },
4751
+ gemini: { ...base3.enabled.gemini, ...partial.enabled?.gemini ?? {} },
4752
+ opencode: { ...base3.enabled.opencode, ...partial.enabled?.opencode ?? {} },
4753
+ codex: { ...base3.enabled.codex, ...partial.enabled?.codex ?? {} },
4754
+ agy: { ...base3.enabled.agy, ...partial.enabled?.agy ?? {} },
4755
+ cursor: { ...base3.enabled.cursor, ...partial.enabled?.cursor ?? {} },
4756
+ shared: { ...base3.enabled.shared, ...partial.enabled?.shared ?? {} },
4757
+ common: { ...base3.enabled.common, ...partial.enabled?.common ?? {} }
4758
4758
  };
4759
4759
  return {
4760
- claudeHome: partial.claudeHome ?? base2.claudeHome,
4761
- geminiHome: partial.geminiHome ?? base2.geminiHome,
4762
- codexHome: partial.codexHome ?? base2.codexHome,
4763
- opencodeDbPath: partial.opencodeDbPath ?? base2.opencodeDbPath,
4764
- agyHome: partial.agyHome ?? base2.agyHome,
4765
- cursorHome: partial.cursorHome ?? base2.cursorHome,
4766
- cursorIdeHome: partial.cursorIdeHome ?? base2.cursorIdeHome,
4767
- extraClaudeHomes: partial.extraClaudeHomes ?? base2.extraClaudeHomes,
4768
- approvedHomes: partial.approvedHomes ?? base2.approvedHomes,
4769
- declinedHomes: partial.declinedHomes ?? base2.declinedHomes,
4770
- homesGrandfathered: partial.homesGrandfathered ?? base2.homesGrandfathered,
4760
+ claudeHome: partial.claudeHome ?? base3.claudeHome,
4761
+ geminiHome: partial.geminiHome ?? base3.geminiHome,
4762
+ codexHome: partial.codexHome ?? base3.codexHome,
4763
+ opencodeDbPath: partial.opencodeDbPath ?? base3.opencodeDbPath,
4764
+ agyHome: partial.agyHome ?? base3.agyHome,
4765
+ cursorHome: partial.cursorHome ?? base3.cursorHome,
4766
+ cursorIdeHome: partial.cursorIdeHome ?? base3.cursorIdeHome,
4767
+ extraClaudeHomes: partial.extraClaudeHomes ?? base3.extraClaudeHomes,
4768
+ approvedHomes: partial.approvedHomes ?? base3.approvedHomes,
4769
+ declinedHomes: partial.declinedHomes ?? base3.declinedHomes,
4770
+ homesGrandfathered: partial.homesGrandfathered ?? base3.homesGrandfathered,
4771
4771
  enabled
4772
4772
  };
4773
4773
  }
4774
- function mergePrivacy(base2, partial) {
4774
+ function mergePrivacy(base3, partial) {
4775
4775
  if (!partial)
4776
- return base2;
4777
- return { ...base2, ...partial };
4776
+ return base3;
4777
+ return { ...base3, ...partial };
4778
4778
  }
4779
- function mergeSync(base2, partial) {
4779
+ function mergeSync(base3, partial) {
4780
4780
  if (!partial)
4781
- return base2;
4781
+ return base3;
4782
4782
  return {
4783
- ...base2,
4783
+ ...base3,
4784
4784
  ...partial,
4785
- upload: { ...base2.upload, ...partial.upload ?? {} }
4785
+ upload: { ...base3.upload, ...partial.upload ?? {} }
4786
4786
  };
4787
4787
  }
4788
- function mergeTeam(base2, partial) {
4788
+ function mergeTeam(base3, partial) {
4789
4789
  if (!partial)
4790
- return base2;
4790
+ return base3;
4791
4791
  return {
4792
- ...base2,
4792
+ ...base3,
4793
4793
  ...partial,
4794
- publishAllowed: { ...base2.publishAllowed, ...partial.publishAllowed ?? {} },
4795
- vault: { ...base2.vault, ...partial.vault ?? {} }
4794
+ publishAllowed: { ...base3.publishAllowed, ...partial.publishAllowed ?? {} },
4795
+ vault: { ...base3.vault, ...partial.vault ?? {} }
4796
4796
  };
4797
4797
  }
4798
4798
  function settingsFilePath() {
@@ -4910,15 +4910,15 @@ function loadSettings() {
4910
4910
  }
4911
4911
  try {
4912
4912
  const parsed = JSON.parse(readFileSync(path, "utf-8"));
4913
- const base2 = freshDefaults();
4913
+ const base3 = freshDefaults();
4914
4914
  const result = {
4915
4915
  v: SCHEMA_VERSION,
4916
- embedding: { ...base2.embedding, ...parsed.embedding ?? {} },
4917
- summary: { ...base2.summary, ...parsed.summary ?? {} },
4918
- sources: mergeSources(base2.sources, parsed.sources),
4919
- privacy: mergePrivacy(base2.privacy, parsed.privacy),
4920
- sync: mergeSync(base2.sync, parsed.sync),
4921
- team: mergeTeam(base2.team, parsed.team)
4916
+ embedding: { ...base3.embedding, ...parsed.embedding ?? {} },
4917
+ summary: { ...base3.summary, ...parsed.summary ?? {} },
4918
+ sources: mergeSources(base3.sources, parsed.sources),
4919
+ privacy: mergePrivacy(base3.privacy, parsed.privacy),
4920
+ sync: mergeSync(base3.sync, parsed.sync),
4921
+ team: mergeTeam(base3.team, parsed.team)
4922
4922
  };
4923
4923
  if (mtimeMs >= 0)
4924
4924
  _settingsCache = { mtimeMs, size, value: structuredClone(result) };
@@ -5125,10 +5125,10 @@ function legacyAutoSyncedHomes() {
5125
5125
  { base: join3(home, ".local", "share", "opencode"), marker: "opencode.db" },
5126
5126
  { base: join3(home, ".cursor"), marker: "chats" }
5127
5127
  ];
5128
- for (const { base: base2, marker } of bases) {
5129
- add(base2, marker);
5130
- const parent = dirname3(base2);
5131
- const leaf = base2.slice(parent.length + 1);
5128
+ for (const { base: base3, marker } of bases) {
5129
+ add(base3, marker);
5130
+ const parent = dirname3(base3);
5131
+ const leaf = base3.slice(parent.length + 1);
5132
5132
  try {
5133
5133
  for (const e of readdirSync2(parent, { withFileTypes: true })) {
5134
5134
  if (!e.isDirectory())
@@ -5326,17 +5326,17 @@ function cursorHomeDir() {
5326
5326
  function cursorIdeHomeDir() {
5327
5327
  return process.env.CHAT_RECALL_CURSOR_IDE_HOME || sources().cursorIdeHome || (process.platform === "darwin" ? join4(homedir3(), "Library", "Application Support", "Cursor") : join4(homedir3(), ".config", "Cursor"));
5328
5328
  }
5329
- function siblingHomes(base2, overridden, suffix) {
5329
+ function siblingHomes(base3, overridden, suffix) {
5330
5330
  const roots = [];
5331
5331
  const add = (home) => {
5332
5332
  const root = suffix ? join4(home, suffix) : home;
5333
5333
  if (existsSync4(root) && !roots.includes(root))
5334
5334
  roots.push(root);
5335
5335
  };
5336
- add(base2);
5336
+ add(base3);
5337
5337
  if (!overridden) {
5338
- const parent = dirname4(base2);
5339
- const leaf = basename(base2);
5338
+ const parent = dirname4(base3);
5339
+ const leaf = basename(base3);
5340
5340
  try {
5341
5341
  for (const entry of readdirSync3(parent, { withFileTypes: true })) {
5342
5342
  if (!entry.isDirectory())
@@ -5594,8 +5594,8 @@ function _clearServerRulePack() {
5594
5594
  serverPack = null;
5595
5595
  }
5596
5596
  function activeRules() {
5597
- const base2 = settingsView().rules;
5598
- return serverPack && serverPack.rules.length > 0 ? [...base2, ...serverPack.rules] : base2;
5597
+ const base3 = settingsView().rules;
5598
+ return serverPack && serverPack.rules.length > 0 ? [...base3, ...serverPack.rules] : base3;
5599
5599
  }
5600
5600
  function isRedactionEnabled() {
5601
5601
  const env = isEnvRedactionRequested();
@@ -6063,6 +6063,64 @@ var init_chunker = __esm({
6063
6063
  }
6064
6064
  });
6065
6065
 
6066
+ // ../engine/dist/core/project-dir-name.js
6067
+ import { readdirSync as readdirSync4 } from "node:fs";
6068
+ function looksWindowsEncoded(dirName) {
6069
+ return /^[A-Za-z]--/.test(dirName);
6070
+ }
6071
+ function decodeProjectDirName(dirName) {
6072
+ if (!dirName)
6073
+ return "";
6074
+ if (looksWindowsEncoded(dirName)) {
6075
+ const drive = dirName[0].toUpperCase();
6076
+ const rest = dirName.slice(3).replace(/-/g, "\\");
6077
+ return `${drive}:\\${rest}`;
6078
+ }
6079
+ return "/" + dirName.replace(/^-/, "").replace(/-/g, "/");
6080
+ }
6081
+ function resolveProjectDirName(dirName, deps = {}) {
6082
+ const structural = decodeProjectDirName(dirName);
6083
+ if (!dirName)
6084
+ return structural;
6085
+ if (looksWindowsEncoded(dirName))
6086
+ return structural;
6087
+ const readdir3 = deps.readdir ?? ((p) => readdirSync4(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name));
6088
+ const encodeName = (name) => name.replace(/[-_.]/g, "-");
6089
+ let remaining = dirName.replace(/^-/, "");
6090
+ let current = "/";
6091
+ while (remaining) {
6092
+ let children;
6093
+ try {
6094
+ children = readdir3(current);
6095
+ } catch {
6096
+ break;
6097
+ }
6098
+ let best = "";
6099
+ for (const child of children) {
6100
+ const encoded = encodeName(child);
6101
+ if (!encoded || encoded.length < best.length)
6102
+ continue;
6103
+ if (!remaining.startsWith(encoded))
6104
+ continue;
6105
+ const next = remaining[encoded.length];
6106
+ if (next !== void 0 && next !== "-")
6107
+ continue;
6108
+ if (encoded.length > best.length)
6109
+ best = child;
6110
+ }
6111
+ if (!best)
6112
+ break;
6113
+ current = current === "/" ? `/${best}` : `${current}/${best}`;
6114
+ remaining = remaining.slice(encodeName(best).length).replace(/^-/, "");
6115
+ }
6116
+ return remaining ? structural : current;
6117
+ }
6118
+ var init_project_dir_name = __esm({
6119
+ "../engine/dist/core/project-dir-name.js"() {
6120
+ "use strict";
6121
+ }
6122
+ });
6123
+
6066
6124
  // ../engine/dist/core/model-pricing.js
6067
6125
  function pricingFor(modelId) {
6068
6126
  if (!modelId)
@@ -6234,7 +6292,7 @@ var init_model_pricing = __esm({
6234
6292
  });
6235
6293
 
6236
6294
  // ../engine/dist/parsers/session.js
6237
- import { createReadStream, existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync2, statSync as statSync4, openSync, readSync, closeSync } from "fs";
6295
+ import { createReadStream, existsSync as existsSync5, readdirSync as readdirSync5, readFileSync as readFileSync2, statSync as statSync4, openSync, readSync, closeSync } from "fs";
6238
6296
  import { createInterface } from "readline";
6239
6297
  import { join as join5, basename as basename2 } from "path";
6240
6298
  function removeCodeBlocks(text) {
@@ -6305,7 +6363,7 @@ function* iterAllSessionIndices(claudeDir) {
6305
6363
  if (!existsSync5(dir)) {
6306
6364
  return;
6307
6365
  }
6308
- const entries = readdirSync4(dir, { withFileTypes: true });
6366
+ const entries = readdirSync5(dir, { withFileTypes: true });
6309
6367
  for (const entry of entries) {
6310
6368
  if (!entry.isDirectory())
6311
6369
  continue;
@@ -6327,7 +6385,7 @@ function getSubagentPaths(sessionPath) {
6327
6385
  const subagentsDir = join5(sessionPath.slice(0, -6), "subagents");
6328
6386
  if (!existsSync5(subagentsDir))
6329
6387
  return [];
6330
- return readdirSync4(subagentsDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join5(subagentsDir, f));
6388
+ return readdirSync5(subagentsDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join5(subagentsDir, f));
6331
6389
  }
6332
6390
  function getSubagentProjectPath(sessionPath, folderDerivedPath) {
6333
6391
  const subagentPaths = getSubagentPaths(sessionPath);
@@ -6644,40 +6702,6 @@ ${resultText}` : resultText;
6644
6702
  });
6645
6703
  return content;
6646
6704
  }
6647
- function decodeDirName(dirName) {
6648
- const segments = dirName.replace(/^-/, "").split("-");
6649
- let currentPath = "/";
6650
- let i = 0;
6651
- while (i < segments.length) {
6652
- let matched = false;
6653
- const singleCandidate = join5(currentPath, segments[i]);
6654
- if (existsSync5(singleCandidate)) {
6655
- currentPath = singleCandidate;
6656
- i++;
6657
- matched = true;
6658
- continue;
6659
- }
6660
- for (let len = 2; len <= segments.length - i; len++) {
6661
- const joinedSegments = segments.slice(i, i + len);
6662
- for (const sep4 of ["-", "_", "."]) {
6663
- const candidate = join5(currentPath, joinedSegments.join(sep4));
6664
- if (existsSync5(candidate)) {
6665
- currentPath = candidate;
6666
- i += len;
6667
- matched = true;
6668
- break;
6669
- }
6670
- }
6671
- if (matched)
6672
- break;
6673
- }
6674
- if (!matched) {
6675
- currentPath = join5(currentPath, segments[i]);
6676
- i++;
6677
- }
6678
- }
6679
- return currentPath;
6680
- }
6681
6705
  function* getAllSessions(claudeDir) {
6682
6706
  const seenSessionIds = /* @__PURE__ */ new Set();
6683
6707
  const projectDirs = claudeDir ? [claudeDir] : claudeProjectDirs();
@@ -6692,12 +6716,12 @@ function* getAllSessions(claudeDir) {
6692
6716
  }
6693
6717
  if (!existsSync5(dir))
6694
6718
  continue;
6695
- const dirEntries = readdirSync4(dir, { withFileTypes: true });
6719
+ const dirEntries = readdirSync5(dir, { withFileTypes: true });
6696
6720
  for (const projDirEntry of dirEntries) {
6697
6721
  if (!projDirEntry.isDirectory())
6698
6722
  continue;
6699
6723
  const projectPath = join5(dir, projDirEntry.name);
6700
- const files = readdirSync4(projectPath);
6724
+ const files = readdirSync5(projectPath);
6701
6725
  for (const file of files) {
6702
6726
  if (!file.endsWith(".jsonl"))
6703
6727
  continue;
@@ -6711,7 +6735,7 @@ function* getAllSessions(claudeDir) {
6711
6735
  const fullPath = join5(projectPath, file);
6712
6736
  try {
6713
6737
  const stat3 = statSync4(fullPath);
6714
- const derivedProjectPath = decodeDirName(projDirEntry.name);
6738
+ const derivedProjectPath = resolveProjectDirName(projDirEntry.name);
6715
6739
  const effectiveMtime = getSessionMtime(fullPath);
6716
6740
  let projectPath2 = "";
6717
6741
  if (hasSubagentsDir(fullPath)) {
@@ -6750,6 +6774,7 @@ var init_session = __esm({
6750
6774
  init_chunker();
6751
6775
  init_claude2();
6752
6776
  init_tool_paths();
6777
+ init_project_dir_name();
6753
6778
  init_model_pricing();
6754
6779
  }
6755
6780
  });
@@ -7487,7 +7512,7 @@ async function archiveRawSession(store, sessionId) {
7487
7512
  return await store.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
7488
7513
  }
7489
7514
  function detectForkPredecessor(sessionPath) {
7490
- const { readFileSync: readFileSync50, readdirSync: readdirSync36 } = requireFs();
7515
+ const { readFileSync: readFileSync50, readdirSync: readdirSync37 } = requireFs();
7491
7516
  const { dirname: dirname28, join: join61, basename: basename30 } = requirePath();
7492
7517
  let text;
7493
7518
  try {
@@ -7517,7 +7542,7 @@ function detectForkPredecessor(sessionPath) {
7517
7542
  const self = basename30(sessionPath);
7518
7543
  let entries;
7519
7544
  try {
7520
- entries = readdirSync36(dir);
7545
+ entries = readdirSync37(dir);
7521
7546
  } catch {
7522
7547
  return null;
7523
7548
  }
@@ -8342,7 +8367,7 @@ var init_flat_string = __esm({
8342
8367
 
8343
8368
  // ../engine/dist/core/backends/gemini.js
8344
8369
  import { createHash as createHash3 } from "crypto";
8345
- import { existsSync as existsSync7, readdirSync as readdirSync5, readFileSync as readFileSync3, statSync as statSync6 } from "fs";
8370
+ import { existsSync as existsSync7, readdirSync as readdirSync6, readFileSync as readFileSync3, statSync as statSync6 } from "fs";
8346
8371
  import { basename as basename7, join as join8 } from "path";
8347
8372
  function readSessionId(path, format) {
8348
8373
  try {
@@ -8456,7 +8481,7 @@ var init_gemini2 = __esm({
8456
8481
  const out = [];
8457
8482
  const seen = /* @__PURE__ */ new Map();
8458
8483
  for (const root of geminiTmpDirs()) {
8459
- for (const projDir of readdirSync5(root, { withFileTypes: true })) {
8484
+ for (const projDir of readdirSync6(root, { withFileTypes: true })) {
8460
8485
  if (!projDir.isDirectory())
8461
8486
  continue;
8462
8487
  const projectPath = projMap.get(projDir.name) || "";
@@ -8467,7 +8492,7 @@ var init_gemini2 = __esm({
8467
8492
  continue;
8468
8493
  let files;
8469
8494
  try {
8470
- files = readdirSync5(chatsDir);
8495
+ files = readdirSync6(chatsDir);
8471
8496
  } catch {
8472
8497
  continue;
8473
8498
  }
@@ -8690,7 +8715,7 @@ var init_gemini2 = __esm({
8690
8715
  return [];
8691
8716
  const projMap = this.loadProjectMap();
8692
8717
  const candidates2 = [];
8693
- for (const proj of readdirSync5(tmpRoot, { withFileTypes: true })) {
8718
+ for (const proj of readdirSync6(tmpRoot, { withFileTypes: true })) {
8694
8719
  if (!proj.isDirectory())
8695
8720
  continue;
8696
8721
  const projectPath = projMap.get(proj.name) || "";
@@ -8704,7 +8729,7 @@ var init_gemini2 = __esm({
8704
8729
  continue;
8705
8730
  let files;
8706
8731
  try {
8707
- files = readdirSync5(chats);
8732
+ files = readdirSync6(chats);
8708
8733
  } catch {
8709
8734
  continue;
8710
8735
  }
@@ -9370,7 +9395,7 @@ var init_opencode = __esm({
9370
9395
  });
9371
9396
 
9372
9397
  // ../engine/dist/core/backends/codex.js
9373
- import { readdirSync as readdirSync6, readFileSync as readFileSync4, statSync as statSync7, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
9398
+ import { readdirSync as readdirSync7, readFileSync as readFileSync4, statSync as statSync7, openSync as openSync3, readSync as readSync3, closeSync as closeSync3 } from "fs";
9374
9399
  import { basename as basename8, dirname as dirname9, join as join10 } from "path";
9375
9400
  function parseShellWrites(cmd) {
9376
9401
  const out = [];
@@ -9466,7 +9491,7 @@ function parseCodexPatch(input) {
9466
9491
  function* walkRollouts(root) {
9467
9492
  let entries;
9468
9493
  try {
9469
- entries = readdirSync6(root, { withFileTypes: true });
9494
+ entries = readdirSync7(root, { withFileTypes: true });
9470
9495
  } catch {
9471
9496
  return;
9472
9497
  }
@@ -9758,7 +9783,7 @@ var init_codex2 = __esm({
9758
9783
  const dayDir = located.path.substring(0, located.path.lastIndexOf("/"));
9759
9784
  let entries = [];
9760
9785
  try {
9761
- entries = readdirSync6(dayDir);
9786
+ entries = readdirSync7(dayDir);
9762
9787
  } catch {
9763
9788
  }
9764
9789
  for (const entry of entries) {
@@ -9944,7 +9969,7 @@ var init_codex2 = __esm({
9944
9969
  const scanDir = (dir) => {
9945
9970
  let entries;
9946
9971
  try {
9947
- entries = readdirSync6(dir, { withFileTypes: true });
9972
+ entries = readdirSync7(dir, { withFileTypes: true });
9948
9973
  } catch {
9949
9974
  return;
9950
9975
  }
@@ -10037,7 +10062,7 @@ var init_codex2 = __esm({
10037
10062
  if (parentId) {
10038
10063
  const dayDir = dirname9(loc.path);
10039
10064
  try {
10040
- for (const f of readdirSync6(dayDir)) {
10065
+ for (const f of readdirSync7(dayDir)) {
10041
10066
  if (!f.endsWith(".jsonl") || !f.startsWith("rollout-"))
10042
10067
  continue;
10043
10068
  const p = join10(dayDir, f);
@@ -10084,7 +10109,7 @@ var init_codex2 = __esm({
10084
10109
  });
10085
10110
 
10086
10111
  // ../engine/dist/core/backends/agy.js
10087
- import { existsSync as existsSync9, readdirSync as readdirSync7, readFileSync as readFileSync5, statSync as statSync8 } from "fs";
10112
+ import { existsSync as existsSync9, readdirSync as readdirSync8, readFileSync as readFileSync5, statSync as statSync8 } from "fs";
10088
10113
  import { basename as basename9, dirname as dirname10, join as join11 } from "path";
10089
10114
  function agyTranscriptPath(logsDir) {
10090
10115
  const full = join11(logsDir, "transcript_full.jsonl");
@@ -10253,7 +10278,7 @@ var init_agy = __esm({
10253
10278
  for (const brainDir of agyBrainDirs()) {
10254
10279
  let sessionDirs;
10255
10280
  try {
10256
- sessionDirs = readdirSync7(brainDir);
10281
+ sessionDirs = readdirSync8(brainDir);
10257
10282
  } catch {
10258
10283
  continue;
10259
10284
  }
@@ -10528,7 +10553,7 @@ var init_agy = __esm({
10528
10553
  const candidates2 = [];
10529
10554
  let sessionDirs;
10530
10555
  try {
10531
- sessionDirs = readdirSync7(brainDir);
10556
+ sessionDirs = readdirSync8(brainDir);
10532
10557
  } catch {
10533
10558
  return [];
10534
10559
  }
@@ -10604,7 +10629,7 @@ var init_agy = __esm({
10604
10629
  });
10605
10630
 
10606
10631
  // ../engine/dist/core/backends/cursor-store.js
10607
- import { existsSync as existsSync10, readdirSync as readdirSync8, readFileSync as readFileSync6, statSync as statSync9 } from "fs";
10632
+ import { existsSync as existsSync10, readdirSync as readdirSync9, readFileSync as readFileSync6, statSync as statSync9 } from "fs";
10608
10633
  import { join as join12 } from "path";
10609
10634
  function cursorProjectSlug(absPath) {
10610
10635
  return absPath.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
@@ -10613,7 +10638,7 @@ function listChatsIn(chatsRoot) {
10613
10638
  const out = [];
10614
10639
  let hashDirs;
10615
10640
  try {
10616
- hashDirs = readdirSync8(chatsRoot);
10641
+ hashDirs = readdirSync9(chatsRoot);
10617
10642
  } catch {
10618
10643
  return out;
10619
10644
  }
@@ -10623,7 +10648,7 @@ function listChatsIn(chatsRoot) {
10623
10648
  try {
10624
10649
  if (!statSync9(hashPath3).isDirectory())
10625
10650
  continue;
10626
- chatIds = readdirSync8(hashPath3);
10651
+ chatIds = readdirSync9(hashPath3);
10627
10652
  } catch {
10628
10653
  continue;
10629
10654
  }
@@ -10668,7 +10693,7 @@ function findChat(chatsRoots, chatId) {
10668
10693
  for (const root of chatsRoots) {
10669
10694
  let hashDirs;
10670
10695
  try {
10671
- hashDirs = readdirSync8(root);
10696
+ hashDirs = readdirSync9(root);
10672
10697
  } catch {
10673
10698
  continue;
10674
10699
  }
@@ -10989,7 +11014,7 @@ var init_cursor_store = __esm({
10989
11014
  });
10990
11015
 
10991
11016
  // ../engine/dist/core/backends/cursor-ide.js
10992
- import { existsSync as existsSync11, readFileSync as readFileSync7, readdirSync as readdirSync9, statSync as statSync10 } from "fs";
11017
+ import { existsSync as existsSync11, readFileSync as readFileSync7, readdirSync as readdirSync10, statSync as statSync10 } from "fs";
10993
11018
  import { dirname as dirname11, join as join13 } from "path";
10994
11019
  function jsonOf(value) {
10995
11020
  if (value == null)
@@ -11052,7 +11077,7 @@ function workspaceIndex(workspaceRoots) {
11052
11077
  for (const root of workspaceRoots) {
11053
11078
  let entries;
11054
11079
  try {
11055
- entries = readdirSync9(root);
11080
+ entries = readdirSync10(root);
11056
11081
  } catch {
11057
11082
  continue;
11058
11083
  }
@@ -11139,7 +11164,7 @@ function listComposers(globalDbPath, workspaceRoots) {
11139
11164
  for (const root of workspaceRoots) {
11140
11165
  let entries;
11141
11166
  try {
11142
- entries = readdirSync9(root);
11167
+ entries = readdirSync10(root);
11143
11168
  } catch {
11144
11169
  continue;
11145
11170
  }
@@ -12039,28 +12064,8 @@ var init_shadow = __esm({
12039
12064
  }
12040
12065
  });
12041
12066
 
12042
- // ../engine/dist/core/project-dir-name.js
12043
- function looksWindowsEncoded(dirName) {
12044
- return /^[A-Za-z]--/.test(dirName);
12045
- }
12046
- function decodeProjectDirName(dirName) {
12047
- if (!dirName)
12048
- return "";
12049
- if (looksWindowsEncoded(dirName)) {
12050
- const drive = dirName[0].toUpperCase();
12051
- const rest = dirName.slice(3).replace(/-/g, "\\");
12052
- return `${drive}:\\${rest}`;
12053
- }
12054
- return "/" + dirName.replace(/^-/, "").replace(/-/g, "/");
12055
- }
12056
- var init_project_dir_name = __esm({
12057
- "../engine/dist/core/project-dir-name.js"() {
12058
- "use strict";
12059
- }
12060
- });
12061
-
12062
12067
  // ../engine/dist/core/live-session-scan.js
12063
- import { existsSync as existsSync14, readdirSync as readdirSync10, readFileSync as readFileSync10, statSync as statSync13, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
12068
+ import { existsSync as existsSync14, readdirSync as readdirSync11, readFileSync as readFileSync10, statSync as statSync13, openSync as openSync4, readSync as readSync4, closeSync as closeSync4 } from "fs";
12064
12069
  import { join as join16, basename as basename10 } from "path";
12065
12070
  import { createHash as createHash5 } from "crypto";
12066
12071
  function lazyGeminiProjectsJson() {
@@ -12097,7 +12102,7 @@ function sessionFileIndex() {
12097
12102
  seenRoots.add(root);
12098
12103
  let entries;
12099
12104
  try {
12100
- entries = readdirSync10(root, { withFileTypes: true });
12105
+ entries = readdirSync11(root, { withFileTypes: true });
12101
12106
  } catch {
12102
12107
  continue;
12103
12108
  }
@@ -12107,7 +12112,7 @@ function sessionFileIndex() {
12107
12112
  const dir = join16(root, entry.name);
12108
12113
  let names;
12109
12114
  try {
12110
- names = readdirSync10(dir);
12115
+ names = readdirSync11(dir);
12111
12116
  } catch {
12112
12117
  continue;
12113
12118
  }
@@ -12118,7 +12123,7 @@ function sessionFileIndex() {
12118
12123
  const located = {
12119
12124
  path: join16(dir, name),
12120
12125
  projectDir: entry.name,
12121
- projectPath: decodeProjectDirName(entry.name)
12126
+ projectPath: resolveProjectDirName(entry.name)
12122
12127
  };
12123
12128
  const prior = byId.get(id);
12124
12129
  if (prior)
@@ -12205,7 +12210,7 @@ function resolveSessionContentPaths(sessionFile) {
12205
12210
  const subDir = join16(sessionFile.slice(0, -6), "subagents");
12206
12211
  if (!existsSync14(subDir))
12207
12212
  return [sessionFile];
12208
- const subPaths = readdirSync10(subDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join16(subDir, f));
12213
+ const subPaths = readdirSync11(subDir).filter((f) => f.endsWith(".jsonl")).sort().map((f) => join16(subDir, f));
12209
12214
  return [sessionFile, ...subPaths];
12210
12215
  }
12211
12216
  function loadGeminiProjectMap() {
@@ -12253,7 +12258,7 @@ function geminiIndex() {
12253
12258
  continue;
12254
12259
  let projs;
12255
12260
  try {
12256
- projs = readdirSync10(tmpRoot, { withFileTypes: true });
12261
+ projs = readdirSync11(tmpRoot, { withFileTypes: true });
12257
12262
  } catch {
12258
12263
  continue;
12259
12264
  }
@@ -12265,14 +12270,14 @@ function geminiIndex() {
12265
12270
  continue;
12266
12271
  let files;
12267
12272
  try {
12268
- files = readdirSync10(chats);
12273
+ files = readdirSync11(chats);
12269
12274
  } catch {
12270
12275
  continue;
12271
12276
  }
12272
12277
  for (const f of files) {
12273
12278
  if (!isGeminiChat(f))
12274
12279
  continue;
12275
- const base2 = f.replace(/\.jsonl?$/, "");
12280
+ const base3 = f.replace(/\.jsonl?$/, "");
12276
12281
  const located = {
12277
12282
  path: join16(chats, f),
12278
12283
  projectDir: proj.name,
@@ -12280,9 +12285,9 @@ function geminiIndex() {
12280
12285
  format: f.endsWith(".jsonl") ? "jsonl" : "json"
12281
12286
  };
12282
12287
  all.push(located);
12283
- if (!byBase.has(base2))
12284
- byBase.set(base2, located);
12285
- const tail = base2.split("-").pop() || "";
12288
+ if (!byBase.has(base3))
12289
+ byBase.set(base3, located);
12290
+ const tail = base3.split("-").pop() || "";
12286
12291
  if (tail.length >= 4 && !byTail.has(tail))
12287
12292
  byTail.set(tail, located);
12288
12293
  }
@@ -12384,21 +12389,21 @@ function findCodexSessionFile(sessionId) {
12384
12389
  function findCodexSessionFileIn(root, sessionId) {
12385
12390
  if (!existsSync14(root))
12386
12391
  return null;
12387
- for (const year of readdirSync10(root, { withFileTypes: true })) {
12392
+ for (const year of readdirSync11(root, { withFileTypes: true })) {
12388
12393
  if (!year.isDirectory())
12389
12394
  continue;
12390
12395
  const yearPath = join16(root, year.name);
12391
- for (const month of readdirSync10(yearPath, { withFileTypes: true })) {
12396
+ for (const month of readdirSync11(yearPath, { withFileTypes: true })) {
12392
12397
  if (!month.isDirectory())
12393
12398
  continue;
12394
12399
  const monthPath = join16(yearPath, month.name);
12395
- for (const day of readdirSync10(monthPath, { withFileTypes: true })) {
12400
+ for (const day of readdirSync11(monthPath, { withFileTypes: true })) {
12396
12401
  if (!day.isDirectory())
12397
12402
  continue;
12398
12403
  const dayPath = join16(monthPath, day.name);
12399
12404
  let files;
12400
12405
  try {
12401
- files = readdirSync10(dayPath);
12406
+ files = readdirSync11(dayPath);
12402
12407
  } catch {
12403
12408
  continue;
12404
12409
  }
@@ -12413,7 +12418,7 @@ function findCodexSessionFileIn(root, sessionId) {
12413
12418
  }
12414
12419
  }
12415
12420
  function scanDir(dir) {
12416
- for (const entry of readdirSync10(dir, { withFileTypes: true })) {
12421
+ for (const entry of readdirSync11(dir, { withFileTypes: true })) {
12417
12422
  if (entry.isDirectory()) {
12418
12423
  const found = scanDir(join16(dir, entry.name));
12419
12424
  if (found)
@@ -12568,7 +12573,7 @@ var init_first_prompt = __esm({
12568
12573
  });
12569
12574
 
12570
12575
  // ../engine/dist/core/backends/claude.js
12571
- import { existsSync as existsSync16, readdirSync as readdirSync11, readFileSync as readFileSync11, statSync as statSync14, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
12576
+ import { existsSync as existsSync16, readdirSync as readdirSync12, readFileSync as readFileSync11, statSync as statSync14, openSync as openSync6, readSync as readSync6, closeSync as closeSync6 } from "fs";
12572
12577
  import { join as join17, basename as basename11 } from "path";
12573
12578
  function stripReminders(text) {
12574
12579
  return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
@@ -12723,13 +12728,13 @@ var init_claude2 = __esm({
12723
12728
  collectSessionsFromRoot(root, cutoff, filter, seen, out) {
12724
12729
  if (!existsSync16(root))
12725
12730
  return;
12726
- for (const proj of readdirSync11(root, { withFileTypes: true })) {
12731
+ for (const proj of readdirSync12(root, { withFileTypes: true })) {
12727
12732
  if (!proj.isDirectory())
12728
12733
  continue;
12729
12734
  if (filter && !proj.name.toLowerCase().includes(filter))
12730
12735
  continue;
12731
12736
  const projPath = join17(root, proj.name);
12732
- const projectPath = decodeProjectDirName(proj.name);
12737
+ const projectPath = resolveProjectDirName(proj.name);
12733
12738
  const indexPath = join17(projPath, "sessions-index.json");
12734
12739
  if (existsSync16(indexPath)) {
12735
12740
  try {
@@ -12760,7 +12765,7 @@ var init_claude2 = __esm({
12760
12765
  }
12761
12766
  let files;
12762
12767
  try {
12763
- files = readdirSync11(projPath);
12768
+ files = readdirSync12(projPath);
12764
12769
  } catch {
12765
12770
  continue;
12766
12771
  }
@@ -13029,7 +13034,7 @@ var init_claude2 = __esm({
13029
13034
  for (const root of claudeProjectDirs()) {
13030
13035
  if (!existsSync16(root))
13031
13036
  continue;
13032
- for (const projEntry of readdirSync11(root, { withFileTypes: true })) {
13037
+ for (const projEntry of readdirSync12(root, { withFileTypes: true })) {
13033
13038
  if (!projEntry.isDirectory())
13034
13039
  continue;
13035
13040
  if (opts.projectFilter && !projEntry.name.toLowerCase().includes(opts.projectFilter.toLowerCase()))
@@ -13037,7 +13042,7 @@ var init_claude2 = __esm({
13037
13042
  const projPath = join17(root, projEntry.name);
13038
13043
  let files;
13039
13044
  try {
13040
- files = readdirSync11(projPath);
13045
+ files = readdirSync12(projPath);
13041
13046
  } catch {
13042
13047
  continue;
13043
13048
  }
@@ -13050,7 +13055,7 @@ var init_claude2 = __esm({
13050
13055
  let mtime = st.mtimeMs;
13051
13056
  const subDir = join17(projPath, f.slice(0, -6), "subagents");
13052
13057
  if (existsSync16(subDir)) {
13053
- for (const sub of readdirSync11(subDir)) {
13058
+ for (const sub of readdirSync12(subDir)) {
13054
13059
  try {
13055
13060
  const subSt = statSync14(join17(subDir, sub));
13056
13061
  if (subSt.mtimeMs > mtime)
@@ -13167,7 +13172,7 @@ __export(project_resolver_exports, {
13167
13172
  });
13168
13173
  import { execSync } from "child_process";
13169
13174
  import { createHash as createHash6 } from "crypto";
13170
- import { existsSync as existsSync17, readFileSync as readFileSync12, realpathSync as realpathSync2, readdirSync as readdirSync12, statSync as statSync15 } from "fs";
13175
+ import { existsSync as existsSync17, readFileSync as readFileSync12, realpathSync as realpathSync2, readdirSync as readdirSync13, statSync as statSync15 } from "fs";
13171
13176
  import { basename as basename12, dirname as dirname13, isAbsolute, join as join18, resolve, sep } from "path";
13172
13177
  function getProjectsConfigPath() {
13173
13178
  return join18(getDataDir(), "projects.json");
@@ -13440,7 +13445,7 @@ function countGitChildren(dir) {
13440
13445
  let count = 0;
13441
13446
  let entries;
13442
13447
  try {
13443
- entries = readdirSync12(dir);
13448
+ entries = readdirSync13(dir);
13444
13449
  } catch {
13445
13450
  return 0;
13446
13451
  }
@@ -13555,7 +13560,7 @@ var init_credentials_env = __esm({
13555
13560
 
13556
13561
  // ../engine/dist/core/source-discovery.js
13557
13562
  import { createHash as createHash7 } from "crypto";
13558
- import { existsSync as existsSync18, readdirSync as readdirSync13, statSync as statSync16 } from "fs";
13563
+ import { existsSync as existsSync18, readdirSync as readdirSync14, statSync as statSync16 } from "fs";
13559
13564
  import { join as join19 } from "path";
13560
13565
  function sourceId(path) {
13561
13566
  return "src_" + createHash7("sha256").update(path).digest("hex").slice(0, 12);
@@ -13592,7 +13597,7 @@ function countTranscripts(root, tool) {
13592
13597
  return;
13593
13598
  let entries;
13594
13599
  try {
13595
- entries = readdirSync13(dir, { withFileTypes: true });
13600
+ entries = readdirSync14(dir, { withFileTypes: true });
13596
13601
  } catch {
13597
13602
  return;
13598
13603
  }
@@ -13671,7 +13676,7 @@ __export(home_discovery_exports, {
13671
13676
  sessionIdsInHome: () => sessionIdsInHome
13672
13677
  });
13673
13678
  import { execFileSync as execFileSync2 } from "child_process";
13674
- import { existsSync as existsSync19, readdirSync as readdirSync14, readFileSync as readFileSync13, realpathSync as realpathSync3, statSync as statSync17 } from "fs";
13679
+ import { existsSync as existsSync19, readdirSync as readdirSync15, readFileSync as readFileSync13, realpathSync as realpathSync3, statSync as statSync17 } from "fs";
13675
13680
  import { homedir as homedir5, platform as osPlatform } from "os";
13676
13681
  import { join as join20 } from "path";
13677
13682
  function candidateRoots(plat = osPlatform(), env = process.env) {
@@ -13715,7 +13720,7 @@ function dirHasTranscript(root, match, depth) {
13715
13720
  return false;
13716
13721
  let entries;
13717
13722
  try {
13718
- entries = readdirSync14(root, { withFileTypes: true });
13723
+ entries = readdirSync15(root, { withFileTypes: true });
13719
13724
  } catch {
13720
13725
  return false;
13721
13726
  }
@@ -13747,7 +13752,7 @@ function countSessions(dir, tool) {
13747
13752
  return;
13748
13753
  let entries;
13749
13754
  try {
13750
- entries = readdirSync14(d, { withFileTypes: true });
13755
+ entries = readdirSync15(d, { withFileTypes: true });
13751
13756
  } catch {
13752
13757
  return;
13753
13758
  }
@@ -13799,7 +13804,7 @@ function sessionIdsInHome(dir, tool) {
13799
13804
  return [];
13800
13805
  if (tool === "agy") {
13801
13806
  try {
13802
- for (const e of readdirSync14(join20(dir, "brain"), { withFileTypes: true })) {
13807
+ for (const e of readdirSync15(join20(dir, "brain"), { withFileTypes: true })) {
13803
13808
  if (e.isDirectory())
13804
13809
  out.add(`agy_${e.name}`);
13805
13810
  }
@@ -13809,11 +13814,11 @@ function sessionIdsInHome(dir, tool) {
13809
13814
  }
13810
13815
  if (tool === "cursor") {
13811
13816
  try {
13812
- for (const hash of readdirSync14(join20(dir, "chats"), { withFileTypes: true })) {
13817
+ for (const hash of readdirSync15(join20(dir, "chats"), { withFileTypes: true })) {
13813
13818
  if (!hash.isDirectory())
13814
13819
  continue;
13815
13820
  try {
13816
- for (const chat of readdirSync14(join20(dir, "chats", hash.name), { withFileTypes: true })) {
13821
+ for (const chat of readdirSync15(join20(dir, "chats", hash.name), { withFileTypes: true })) {
13817
13822
  if (chat.isDirectory())
13818
13823
  out.add(`cursor_${chat.name}`);
13819
13824
  }
@@ -13829,7 +13834,7 @@ function sessionIdsInHome(dir, tool) {
13829
13834
  return;
13830
13835
  let entries;
13831
13836
  try {
13832
- entries = readdirSync14(d, { withFileTypes: true });
13837
+ entries = readdirSync15(d, { withFileTypes: true });
13833
13838
  } catch {
13834
13839
  return;
13835
13840
  }
@@ -13901,7 +13906,7 @@ function runningToolHomes(plat = osPlatform()) {
13901
13906
  if (plat === "linux") {
13902
13907
  let pids;
13903
13908
  try {
13904
- pids = readdirSync14("/proc").filter((p) => /^\d+$/.test(p));
13909
+ pids = readdirSync15("/proc").filter((p) => /^\d+$/.test(p));
13905
13910
  } catch {
13906
13911
  return out;
13907
13912
  }
@@ -13981,7 +13986,7 @@ function walkForHomes(dir, depth, visit) {
13981
13986
  return;
13982
13987
  let entries;
13983
13988
  try {
13984
- entries = readdirSync14(dir, { withFileTypes: true });
13989
+ entries = readdirSync15(dir, { withFileTypes: true });
13985
13990
  } catch {
13986
13991
  return;
13987
13992
  }
@@ -15657,7 +15662,7 @@ var init_session_source = __esm({
15657
15662
  });
15658
15663
 
15659
15664
  // ../engine/dist/core/utils.js
15660
- import { existsSync as existsSync22, readdirSync as readdirSync15 } from "fs";
15665
+ import { existsSync as existsSync22, readdirSync as readdirSync16 } from "fs";
15661
15666
  import { homedir as homedir6 } from "os";
15662
15667
  import { join as join23 } from "path";
15663
15668
  function splitByHeaders(markdown) {
@@ -15708,7 +15713,7 @@ function getClaudeDirs() {
15708
15713
  }
15709
15714
  add(join23(home, ".claude"));
15710
15715
  try {
15711
- const entries = readdirSync15(home, { withFileTypes: true });
15716
+ const entries = readdirSync16(home, { withFileTypes: true });
15712
15717
  for (const entry of entries) {
15713
15718
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
15714
15719
  const candidate = join23(home, entry.name);
@@ -15738,7 +15743,7 @@ var init_utils = __esm({
15738
15743
  });
15739
15744
 
15740
15745
  // ../engine/dist/parsers/plan-source.js
15741
- import { existsSync as existsSync23, readdirSync as readdirSync16, readFileSync as readFileSync16, statSync as statSync21 } from "fs";
15746
+ import { existsSync as existsSync23, readdirSync as readdirSync17, readFileSync as readFileSync16, statSync as statSync21 } from "fs";
15742
15747
  import { homedir as homedir7 } from "os";
15743
15748
  import { join as join24, basename as basename13 } from "path";
15744
15749
  import { createHash as createHash9 } from "crypto";
@@ -15765,7 +15770,7 @@ function parseFrontmatter(content) {
15765
15770
  }
15766
15771
  function safeReaddir(dir) {
15767
15772
  try {
15768
- return readdirSync16(dir);
15773
+ return readdirSync17(dir);
15769
15774
  } catch {
15770
15775
  return [];
15771
15776
  }
@@ -15801,11 +15806,11 @@ var init_plan_source = __esm({
15801
15806
  if (!existsSync23(projectsDir))
15802
15807
  return this._knownProjectDirs;
15803
15808
  try {
15804
- const entries = readdirSync16(projectsDir, { withFileTypes: true });
15809
+ const entries = readdirSync17(projectsDir, { withFileTypes: true });
15805
15810
  for (const entry of entries) {
15806
15811
  if (!entry.isDirectory() || entry.name.startsWith("."))
15807
15812
  continue;
15808
- const projectPath = decodeProjectDirName(entry.name);
15813
+ const projectPath = resolveProjectDirName(entry.name);
15809
15814
  if (existsSync23(projectPath)) {
15810
15815
  this._knownProjectDirs.push(projectPath);
15811
15816
  }
@@ -15833,7 +15838,7 @@ var init_plan_source = __esm({
15833
15838
  for (const plansDir of this.plansDirs) {
15834
15839
  if (!existsSync23(plansDir))
15835
15840
  continue;
15836
- const files = readdirSync16(plansDir).filter((f) => f.endsWith(".md"));
15841
+ const files = readdirSync17(plansDir).filter((f) => f.endsWith(".md"));
15837
15842
  for (const file of files) {
15838
15843
  if (seenClaude.has(file))
15839
15844
  continue;
@@ -15877,7 +15882,7 @@ var init_plan_source = __esm({
15877
15882
  return;
15878
15883
  let files;
15879
15884
  try {
15880
- files = readdirSync16(plansDir).filter((f) => f.endsWith(".md"));
15885
+ files = readdirSync17(plansDir).filter((f) => f.endsWith(".md"));
15881
15886
  } catch {
15882
15887
  return;
15883
15888
  }
@@ -15924,7 +15929,7 @@ var init_plan_source = __esm({
15924
15929
  }
15925
15930
  let projDirs;
15926
15931
  try {
15927
- projDirs = readdirSync16(tmpRoot);
15932
+ projDirs = readdirSync17(tmpRoot);
15928
15933
  } catch {
15929
15934
  return;
15930
15935
  }
@@ -15933,7 +15938,7 @@ var init_plan_source = __esm({
15933
15938
  const projRoot = join24(tmpRoot, projDir);
15934
15939
  let sessions;
15935
15940
  try {
15936
- sessions = readdirSync16(projRoot);
15941
+ sessions = readdirSync17(projRoot);
15937
15942
  } catch {
15938
15943
  continue;
15939
15944
  }
@@ -15943,7 +15948,7 @@ var init_plan_source = __esm({
15943
15948
  continue;
15944
15949
  let files;
15945
15950
  try {
15946
- files = readdirSync16(plansDir).filter((f) => f.endsWith(".md"));
15951
+ files = readdirSync17(plansDir).filter((f) => f.endsWith(".md"));
15947
15952
  } catch {
15948
15953
  continue;
15949
15954
  }
@@ -16152,7 +16157,7 @@ var init_plan_source = __esm({
16152
16157
  return;
16153
16158
  let sessionDirs;
16154
16159
  try {
16155
- sessionDirs = readdirSync16(brainDir);
16160
+ sessionDirs = readdirSync17(brainDir);
16156
16161
  } catch {
16157
16162
  return;
16158
16163
  }
@@ -16166,7 +16171,7 @@ var init_plan_source = __esm({
16166
16171
  }
16167
16172
  let files;
16168
16173
  try {
16169
- files = readdirSync16(sessionPath).filter((f) => f.endsWith(".md"));
16174
+ files = readdirSync17(sessionPath).filter((f) => f.endsWith(".md"));
16170
16175
  } catch {
16171
16176
  continue;
16172
16177
  }
@@ -16203,7 +16208,7 @@ var init_plan_source = __esm({
16203
16208
  });
16204
16209
 
16205
16210
  // ../engine/dist/parsers/task-source.js
16206
- import { existsSync as existsSync24, readdirSync as readdirSync17, readFileSync as readFileSync17, statSync as statSync22 } from "fs";
16211
+ import { existsSync as existsSync24, readdirSync as readdirSync18, readFileSync as readFileSync17, statSync as statSync22 } from "fs";
16207
16212
  import { join as join25 } from "path";
16208
16213
  var MAX_CHUNK_CHARS2, TaskSource;
16209
16214
  var init_task_source = __esm({
@@ -16237,7 +16242,7 @@ var init_task_source = __esm({
16237
16242
  return;
16238
16243
  let files;
16239
16244
  try {
16240
- files = readdirSync17(todosDir).filter((f) => f.endsWith(".json"));
16245
+ files = readdirSync18(todosDir).filter((f) => f.endsWith(".json"));
16241
16246
  } catch {
16242
16247
  return;
16243
16248
  }
@@ -16286,7 +16291,7 @@ var init_task_source = __esm({
16286
16291
  async *discoverInDir(tasksDir) {
16287
16292
  if (!existsSync24(tasksDir))
16288
16293
  return;
16289
- const entries = readdirSync17(tasksDir, { withFileTypes: true });
16294
+ const entries = readdirSync18(tasksDir, { withFileTypes: true });
16290
16295
  for (const entry of entries) {
16291
16296
  if (!entry.isDirectory())
16292
16297
  continue;
@@ -16294,7 +16299,7 @@ var init_task_source = __esm({
16294
16299
  const taskDir = join25(tasksDir, sessionUuid);
16295
16300
  try {
16296
16301
  const stat3 = statSync22(taskDir);
16297
- const taskFiles = readdirSync17(taskDir).filter((f) => f.endsWith(".json"));
16302
+ const taskFiles = readdirSync18(taskDir).filter((f) => f.endsWith(".json"));
16298
16303
  if (taskFiles.length === 0)
16299
16304
  continue;
16300
16305
  const tasks2 = this.readTasks(taskDir, taskFiles);
@@ -16328,7 +16333,7 @@ var init_task_source = __esm({
16328
16333
  const taskDir = item.filePath;
16329
16334
  if (!existsSync24(taskDir))
16330
16335
  return [];
16331
- const taskFiles = readdirSync17(taskDir).filter((f) => f.endsWith(".json"));
16336
+ const taskFiles = readdirSync18(taskDir).filter((f) => f.endsWith(".json"));
16332
16337
  const tasks2 = this.readTasks(taskDir, taskFiles);
16333
16338
  if (tasks2.length === 0)
16334
16339
  return [];
@@ -16439,7 +16444,7 @@ ${task.description}`.slice(0, MAX_CHUNK_CHARS2);
16439
16444
  });
16440
16445
 
16441
16446
  // ../engine/dist/parsers/claude-md-source.js
16442
- import { existsSync as existsSync25, readFileSync as readFileSync18, readdirSync as readdirSync18, statSync as statSync23 } from "fs";
16447
+ import { existsSync as existsSync25, readFileSync as readFileSync18, readdirSync as readdirSync19, statSync as statSync23 } from "fs";
16443
16448
  import { homedir as homedir8 } from "os";
16444
16449
  import { join as join26, basename as basename14, dirname as dirname14 } from "path";
16445
16450
  import { createHash as createHash10 } from "crypto";
@@ -16456,11 +16461,11 @@ function discoverProjectDirs(claudeDir) {
16456
16461
  if (!existsSync25(projectsDir))
16457
16462
  return;
16458
16463
  try {
16459
- const entries = readdirSync18(projectsDir, { withFileTypes: true });
16464
+ const entries = readdirSync19(projectsDir, { withFileTypes: true });
16460
16465
  for (const entry of entries) {
16461
16466
  if (!entry.isDirectory() || entry.name.startsWith("."))
16462
16467
  continue;
16463
- const projectPath = decodeProjectDirName(entry.name);
16468
+ const projectPath = resolveProjectDirName(entry.name);
16464
16469
  addProject(projectPath);
16465
16470
  }
16466
16471
  } catch {
@@ -16473,7 +16478,7 @@ function discoverProjectDirs(claudeDir) {
16473
16478
  const home = homedir8();
16474
16479
  scanProjectsDir(claudeBackend.projectsDir());
16475
16480
  try {
16476
- for (const entry of readdirSync18(home, { withFileTypes: true })) {
16481
+ for (const entry of readdirSync19(home, { withFileTypes: true })) {
16477
16482
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
16478
16483
  scanProjectsDir(join26(home, entry.name, "projects"));
16479
16484
  }
@@ -16575,7 +16580,7 @@ var init_claude_md_source = __esm({
16575
16580
  }
16576
16581
  }
16577
16582
  try {
16578
- const entries = readdirSync18(searchDir, { withFileTypes: true });
16583
+ const entries = readdirSync19(searchDir, { withFileTypes: true });
16579
16584
  for (const entry of entries) {
16580
16585
  if (!entry.isDirectory())
16581
16586
  continue;
@@ -16665,7 +16670,7 @@ var init_claude_md_source = __esm({
16665
16670
  });
16666
16671
 
16667
16672
  // ../engine/dist/parsers/agent-memory-source.js
16668
- import { existsSync as existsSync26, readFileSync as readFileSync19, readdirSync as readdirSync19, statSync as statSync24 } from "fs";
16673
+ import { existsSync as existsSync26, readFileSync as readFileSync19, readdirSync as readdirSync20, statSync as statSync24 } from "fs";
16669
16674
  import { homedir as homedir9 } from "os";
16670
16675
  import { join as join27, basename as basename15 } from "path";
16671
16676
  function parseFrontmatter2(content) {
@@ -16693,7 +16698,7 @@ function parseFrontmatter2(content) {
16693
16698
  return { fm, body };
16694
16699
  }
16695
16700
  function projectHashToPath(hashDir) {
16696
- return decodeProjectDirName(hashDir);
16701
+ return resolveProjectDirName(hashDir);
16697
16702
  }
16698
16703
  function discoverMemoryDirs() {
16699
16704
  const out = [];
@@ -16707,7 +16712,7 @@ function discoverMemoryDirs() {
16707
16712
  const home = homedir9();
16708
16713
  const claudeRoots = [claudeBackend.projectsDir()];
16709
16714
  try {
16710
- for (const entry of readdirSync19(home, { withFileTypes: true })) {
16715
+ for (const entry of readdirSync20(home, { withFileTypes: true })) {
16711
16716
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
16712
16717
  claudeRoots.push(join27(home, entry.name, "projects"));
16713
16718
  }
@@ -16718,7 +16723,7 @@ function discoverMemoryDirs() {
16718
16723
  if (!existsSync26(projectsDir))
16719
16724
  continue;
16720
16725
  try {
16721
- for (const entry of readdirSync19(projectsDir, { withFileTypes: true })) {
16726
+ for (const entry of readdirSync20(projectsDir, { withFileTypes: true })) {
16722
16727
  if (!entry.isDirectory() || entry.name.startsWith("."))
16723
16728
  continue;
16724
16729
  add(join27(projectsDir, entry.name, "memory"), entry.name);
@@ -16747,7 +16752,7 @@ var init_agent_memory_source = __esm({
16747
16752
  for (const { memoryDir, projectHash } of discoverMemoryDirs()) {
16748
16753
  const projectPath = projectHashToPath(projectHash);
16749
16754
  try {
16750
- for (const entry of readdirSync19(memoryDir, { withFileTypes: true })) {
16755
+ for (const entry of readdirSync20(memoryDir, { withFileTypes: true })) {
16751
16756
  if (!entry.isFile() || !entry.name.endsWith(".md"))
16752
16757
  continue;
16753
16758
  const filePath = join27(memoryDir, entry.name);
@@ -16850,7 +16855,7 @@ var init_agent_memory_source = __esm({
16850
16855
  });
16851
16856
 
16852
16857
  // ../engine/dist/parsers/history-source.js
16853
- import { existsSync as existsSync27, readdirSync as readdirSync20, statSync as statSync25 } from "fs";
16858
+ import { existsSync as existsSync27, readdirSync as readdirSync21, statSync as statSync25 } from "fs";
16854
16859
  import { createReadStream as createReadStream3 } from "fs";
16855
16860
  import { createInterface as createInterface2 } from "readline";
16856
16861
  import { homedir as homedir10 } from "os";
@@ -16877,7 +16882,7 @@ var init_history_source = __esm({
16877
16882
  };
16878
16883
  tryAdd(claudeBackend.historyFile());
16879
16884
  try {
16880
- for (const entry of readdirSync20(home, { withFileTypes: true })) {
16885
+ for (const entry of readdirSync21(home, { withFileTypes: true })) {
16881
16886
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
16882
16887
  tryAdd(join28(home, entry.name, "history.jsonl"));
16883
16888
  }
@@ -17034,7 +17039,7 @@ var init_history_source = __esm({
17034
17039
  });
17035
17040
 
17036
17041
  // ../engine/dist/parsers/paste-source.js
17037
- import { existsSync as existsSync28, readdirSync as readdirSync21, readFileSync as readFileSync20, statSync as statSync26 } from "fs";
17042
+ import { existsSync as existsSync28, readdirSync as readdirSync22, readFileSync as readFileSync20, statSync as statSync26 } from "fs";
17038
17043
  import { homedir as homedir11 } from "os";
17039
17044
  import { join as join29, basename as basename16 } from "path";
17040
17045
  var MAX_CHUNK_CHARS6, MAX_FILE_SIZE, PasteSource;
@@ -17060,7 +17065,7 @@ var init_paste_source = __esm({
17060
17065
  };
17061
17066
  tryAdd(claudeBackend.pasteCacheDir());
17062
17067
  try {
17063
- for (const entry of readdirSync21(home, { withFileTypes: true })) {
17068
+ for (const entry of readdirSync22(home, { withFileTypes: true })) {
17064
17069
  if (entry.isDirectory() && entry.name.startsWith(".claude-") && entry.name !== ".claude-code") {
17065
17070
  tryAdd(join29(home, entry.name, "paste-cache"));
17066
17071
  }
@@ -17077,7 +17082,7 @@ var init_paste_source = __esm({
17077
17082
  for (const cacheDir of this.cacheDirs) {
17078
17083
  if (!existsSync28(cacheDir))
17079
17084
  continue;
17080
- const files = readdirSync21(cacheDir).filter((f) => f.endsWith(".txt"));
17085
+ const files = readdirSync22(cacheDir).filter((f) => f.endsWith(".txt"));
17081
17086
  for (const file of files) {
17082
17087
  if (seen.has(file))
17083
17088
  continue;
@@ -17134,7 +17139,7 @@ var init_paste_source = __esm({
17134
17139
  });
17135
17140
 
17136
17141
  // ../engine/dist/parsers/gemini-source.js
17137
- import { existsSync as existsSync29, readdirSync as readdirSync22, readFileSync as readFileSync21, statSync as statSync27 } from "fs";
17142
+ import { existsSync as existsSync29, readdirSync as readdirSync23, readFileSync as readFileSync21, statSync as statSync27 } from "fs";
17138
17143
  import { join as join30, basename as basename17 } from "path";
17139
17144
  import { createHash as createHash11 } from "crypto";
17140
17145
  function loadGeminiProjectMap2() {
@@ -17176,13 +17181,13 @@ var init_gemini_source = __esm({
17176
17181
  return;
17177
17182
  const seen = /* @__PURE__ */ new Set();
17178
17183
  try {
17179
- for (const projectDirEntry of readdirSync22(tmpDir, { withFileTypes: true })) {
17184
+ for (const projectDirEntry of readdirSync23(tmpDir, { withFileTypes: true })) {
17180
17185
  if (!projectDirEntry.isDirectory())
17181
17186
  continue;
17182
17187
  const chatsDir = join30(tmpDir, projectDirEntry.name, "chats");
17183
17188
  if (!existsSync29(chatsDir))
17184
17189
  continue;
17185
- for (const file of readdirSync22(chatsDir)) {
17190
+ for (const file of readdirSync23(chatsDir)) {
17186
17191
  if (!file.endsWith(".json") || !file.startsWith("session-"))
17187
17192
  continue;
17188
17193
  const filePath = join30(chatsDir, file);
@@ -17317,7 +17322,7 @@ var init_gemini_source = __esm({
17317
17322
  });
17318
17323
 
17319
17324
  // ../engine/dist/parsers/gemini-brain-source.js
17320
- import { existsSync as existsSync30, readdirSync as readdirSync23, readFileSync as readFileSync22, statSync as statSync28 } from "fs";
17325
+ import { existsSync as existsSync30, readdirSync as readdirSync24, readFileSync as readFileSync22, statSync as statSync28 } from "fs";
17321
17326
  import { join as join31 } from "path";
17322
17327
  var MAX_CHUNK_CHARS8, GeminiBrainSource;
17323
17328
  var init_gemini_brain_source = __esm({
@@ -17338,12 +17343,12 @@ var init_gemini_brain_source = __esm({
17338
17343
  return;
17339
17344
  if (!existsSync30(this.brainDir))
17340
17345
  return;
17341
- const sessions = readdirSync23(this.brainDir, { withFileTypes: true });
17346
+ const sessions = readdirSync24(this.brainDir, { withFileTypes: true });
17342
17347
  for (const entry of sessions) {
17343
17348
  if (!entry.isDirectory())
17344
17349
  continue;
17345
17350
  const sessionDir = join31(this.brainDir, entry.name);
17346
- const files = readdirSync23(sessionDir).filter((f) => f.endsWith(".md") && !f.includes(".resolved") && !f.includes(".metadata"));
17351
+ const files = readdirSync24(sessionDir).filter((f) => f.endsWith(".md") && !f.includes(".resolved") && !f.includes(".metadata"));
17347
17352
  if (files.length === 0)
17348
17353
  continue;
17349
17354
  for (const file of files) {
@@ -17731,7 +17736,7 @@ var init_opencode_todo_source = __esm({
17731
17736
  });
17732
17737
 
17733
17738
  // ../engine/dist/parsers/codex-session-source.js
17734
- import { existsSync as existsSync33, readdirSync as readdirSync24, readFileSync as readFileSync23, statSync as statSync29 } from "fs";
17739
+ import { existsSync as existsSync33, readdirSync as readdirSync25, readFileSync as readFileSync23, statSync as statSync29 } from "fs";
17735
17740
  import { join as join32, basename as basename18 } from "path";
17736
17741
  function getCodexSessionsDir() {
17737
17742
  return codexBackend.sessionsDir();
@@ -17952,7 +17957,7 @@ var init_codex_session_source = __esm({
17952
17957
  const results = [];
17953
17958
  if (!existsSync33(dir))
17954
17959
  return results;
17955
- for (const entry of readdirSync24(dir, { withFileTypes: true })) {
17960
+ for (const entry of readdirSync25(dir, { withFileTypes: true })) {
17956
17961
  const fullPath = join32(dir, entry.name);
17957
17962
  if (entry.isDirectory()) {
17958
17963
  results.push(...this.collectJsonlFiles(fullPath));
@@ -17967,7 +17972,7 @@ var init_codex_session_source = __esm({
17967
17972
  });
17968
17973
 
17969
17974
  // ../engine/dist/parsers/diary-source.js
17970
- import { existsSync as existsSync34, mkdirSync as mkdirSync5, readdirSync as readdirSync25, readFileSync as readFileSync24, writeFileSync as writeFileSync4, statSync as statSync30 } from "fs";
17975
+ import { existsSync as existsSync34, mkdirSync as mkdirSync5, readdirSync as readdirSync26, readFileSync as readFileSync24, writeFileSync as writeFileSync4, statSync as statSync30 } from "fs";
17971
17976
  import { join as join33, basename as basename19 } from "path";
17972
17977
  var MAX_CHUNK_CHARS11, DiarySource;
17973
17978
  var init_diary_source = __esm({
@@ -17985,10 +17990,10 @@ var init_diary_source = __esm({
17985
17990
  async *discover() {
17986
17991
  if (!existsSync34(this.diaryDir))
17987
17992
  return;
17988
- const agents = readdirSync25(this.diaryDir, { withFileTypes: true }).filter((d) => d.isDirectory());
17993
+ const agents = readdirSync26(this.diaryDir, { withFileTypes: true }).filter((d) => d.isDirectory());
17989
17994
  for (const agentDir of agents) {
17990
17995
  const agentPath2 = join33(this.diaryDir, agentDir.name);
17991
- const files = readdirSync25(agentPath2).filter((f) => f.endsWith(".json"));
17996
+ const files = readdirSync26(agentPath2).filter((f) => f.endsWith(".json"));
17992
17997
  for (const file of files) {
17993
17998
  const filePath = join33(agentPath2, file);
17994
17999
  try {
@@ -18079,7 +18084,7 @@ var init_diary_source = __esm({
18079
18084
  const agentDir = join33(dir, agentName.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
18080
18085
  if (!existsSync34(agentDir))
18081
18086
  return [];
18082
- const files = readdirSync25(agentDir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, lastN);
18087
+ const files = readdirSync26(agentDir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, lastN);
18083
18088
  const entries = [];
18084
18089
  for (const file of files) {
18085
18090
  try {
@@ -18128,7 +18133,7 @@ var init_redact_inline = __esm({
18128
18133
  });
18129
18134
 
18130
18135
  // ../engine/dist/parsers/skills-source.js
18131
- import { existsSync as existsSync35, readdirSync as readdirSync26, readFileSync as readFileSync25, statSync as statSync31 } from "fs";
18136
+ import { existsSync as existsSync35, readdirSync as readdirSync27, readFileSync as readFileSync25, statSync as statSync31 } from "fs";
18132
18137
  import { homedir as homedir12 } from "os";
18133
18138
  import { join as join34 } from "path";
18134
18139
  function rebuildBody(content) {
@@ -18209,7 +18214,7 @@ var init_skills_source = __esm({
18209
18214
  return;
18210
18215
  const codexPluginsDir = codexBackend.pluginsDir();
18211
18216
  if (existsSync35(codexPluginsDir)) {
18212
- for (const pluginName of readdirSync26(codexPluginsDir)) {
18217
+ for (const pluginName of readdirSync27(codexPluginsDir)) {
18213
18218
  const skillsDir = join34(codexPluginsDir, pluginName, "skills");
18214
18219
  if (!existsSync35(skillsDir))
18215
18220
  continue;
@@ -18220,7 +18225,7 @@ var init_skills_source = __esm({
18220
18225
  async *fromSkillRoot(root, pluginName) {
18221
18226
  let entries;
18222
18227
  try {
18223
- entries = readdirSync26(root.path);
18228
+ entries = readdirSync27(root.path);
18224
18229
  } catch {
18225
18230
  return;
18226
18231
  }
@@ -18254,14 +18259,14 @@ var init_skills_source = __esm({
18254
18259
  }
18255
18260
  const subdirs = {};
18256
18261
  try {
18257
- for (const sub of readdirSync26(skillDir)) {
18262
+ for (const sub of readdirSync27(skillDir)) {
18258
18263
  const subPath = join34(skillDir, sub);
18259
18264
  try {
18260
18265
  const ss = statSync31(subPath);
18261
18266
  if (ss.isDirectory()) {
18262
18267
  let count = 0;
18263
18268
  try {
18264
- count = readdirSync26(subPath).length;
18269
+ count = readdirSync27(subPath).length;
18265
18270
  } catch {
18266
18271
  }
18267
18272
  subdirs[sub] = count;
@@ -18327,7 +18332,7 @@ var init_skills_source = __esm({
18327
18332
  });
18328
18333
 
18329
18334
  // ../engine/dist/parsers/mcps-source.js
18330
- import { existsSync as existsSync36, readdirSync as readdirSync27, readFileSync as readFileSync26, statSync as statSync32 } from "fs";
18335
+ import { existsSync as existsSync36, readdirSync as readdirSync28, readFileSync as readFileSync26, statSync as statSync32 } from "fs";
18331
18336
  import { homedir as homedir13 } from "os";
18332
18337
  import { join as join35 } from "path";
18333
18338
  function readJson(path) {
@@ -18441,7 +18446,7 @@ var init_mcps_source = __esm({
18441
18446
  yield* this.fromCodexToml(codexBackend.configToml());
18442
18447
  const pluginsDir = codexBackend.pluginsDir();
18443
18448
  if (existsSync36(pluginsDir)) {
18444
- for (const plugin of readdirSync27(pluginsDir)) {
18449
+ for (const plugin of readdirSync28(pluginsDir)) {
18445
18450
  const mcpPath = join35(pluginsDir, plugin, ".mcp.json");
18446
18451
  yield* this.fromObject(readJson(mcpPath)?.mcpServers, {
18447
18452
  tool: "codex",
@@ -18665,7 +18670,7 @@ var init_toolkit_format = __esm({
18665
18670
  });
18666
18671
 
18667
18672
  // ../engine/dist/parsers/slash-commands-source.js
18668
- import { existsSync as existsSync37, readdirSync as readdirSync28, readFileSync as readFileSync27, statSync as statSync33 } from "fs";
18673
+ import { existsSync as existsSync37, readdirSync as readdirSync29, readFileSync as readFileSync27, statSync as statSync33 } from "fs";
18669
18674
  import { join as join36, basename as basename20 } from "path";
18670
18675
  function rebuildBody2(content) {
18671
18676
  const { text, redacted } = redactInlineSecrets(content);
@@ -18720,10 +18725,10 @@ var init_slash_commands_source = __esm({
18720
18725
  const projectsRoot = claudeBackend.projectsDir();
18721
18726
  if (existsSync37(projectsRoot)) {
18722
18727
  try {
18723
- for (const entry of readdirSync28(projectsRoot, { withFileTypes: true })) {
18728
+ for (const entry of readdirSync29(projectsRoot, { withFileTypes: true })) {
18724
18729
  if (!entry.isDirectory() || entry.name.startsWith("."))
18725
18730
  continue;
18726
- const projectPath = decodeProjectDirName(entry.name);
18731
+ const projectPath = resolveProjectDirName(entry.name);
18727
18732
  const cmdDir = join36(projectPath, ".claude", "commands");
18728
18733
  if (existsSync37(cmdDir))
18729
18734
  roots.push({ path: cmdDir, tool: "claude", scope: "project", projectPath, format: "md" });
@@ -18751,7 +18756,7 @@ var init_slash_commands_source = __esm({
18751
18756
  const ext = root.format === "toml" ? ".toml" : ".md";
18752
18757
  let files;
18753
18758
  try {
18754
- files = readdirSync28(root.path).filter((f) => f.endsWith(ext));
18759
+ files = readdirSync29(root.path).filter((f) => f.endsWith(ext));
18755
18760
  } catch {
18756
18761
  continue;
18757
18762
  }
@@ -18811,7 +18816,7 @@ var init_slash_commands_source = __esm({
18811
18816
  });
18812
18817
 
18813
18818
  // ../engine/dist/parsers/subagents-source.js
18814
- import { existsSync as existsSync38, readdirSync as readdirSync29, readFileSync as readFileSync28, statSync as statSync34 } from "fs";
18819
+ import { existsSync as existsSync38, readdirSync as readdirSync30, readFileSync as readFileSync28, statSync as statSync34 } from "fs";
18815
18820
  import { join as join37, basename as basename21 } from "path";
18816
18821
  function rebuildBody3(content) {
18817
18822
  const { text, redacted } = redactInlineSecrets(content);
@@ -18868,10 +18873,10 @@ var init_subagents_source = __esm({
18868
18873
  const projectsRoot = claudeBackend.projectsDir();
18869
18874
  if (existsSync38(projectsRoot)) {
18870
18875
  try {
18871
- for (const entry of readdirSync29(projectsRoot, { withFileTypes: true })) {
18876
+ for (const entry of readdirSync30(projectsRoot, { withFileTypes: true })) {
18872
18877
  if (!entry.isDirectory() || entry.name.startsWith("."))
18873
18878
  continue;
18874
- const projectPath = decodeProjectDirName(entry.name);
18879
+ const projectPath = resolveProjectDirName(entry.name);
18875
18880
  const agentDir = join37(projectPath, ".claude", "agents");
18876
18881
  if (existsSync38(agentDir))
18877
18882
  roots.push({ path: agentDir, tool: "claude", scope: "project", projectPath, format: "md" });
@@ -18899,7 +18904,7 @@ var init_subagents_source = __esm({
18899
18904
  const ext = root.format === "toml" ? ".toml" : ".md";
18900
18905
  let files;
18901
18906
  try {
18902
- files = readdirSync29(root.path).filter((f) => f.endsWith(ext));
18907
+ files = readdirSync30(root.path).filter((f) => f.endsWith(ext));
18903
18908
  } catch {
18904
18909
  continue;
18905
18910
  }
@@ -18960,7 +18965,7 @@ var init_subagents_source = __esm({
18960
18965
  });
18961
18966
 
18962
18967
  // ../engine/dist/parsers/hooks-source.js
18963
- import { existsSync as existsSync39, readFileSync as readFileSync29, readdirSync as readdirSync30, statSync as statSync35 } from "fs";
18968
+ import { existsSync as existsSync39, readFileSync as readFileSync29, readdirSync as readdirSync31, statSync as statSync35 } from "fs";
18964
18969
  import { join as join38 } from "path";
18965
18970
  function readJson2(path) {
18966
18971
  if (!existsSync39(path))
@@ -18987,10 +18992,10 @@ var init_hooks_source = __esm({
18987
18992
  const projectsRoot = claudeBackend.projectsDir();
18988
18993
  if (existsSync39(projectsRoot)) {
18989
18994
  try {
18990
- for (const entry of readdirSync30(projectsRoot, { withFileTypes: true })) {
18995
+ for (const entry of readdirSync31(projectsRoot, { withFileTypes: true })) {
18991
18996
  if (!entry.isDirectory() || entry.name.startsWith("."))
18992
18997
  continue;
18993
- const projectPath = decodeProjectDirName(entry.name);
18998
+ const projectPath = resolveProjectDirName(entry.name);
18994
18999
  const settingsPath2 = join38(projectPath, ".claude", "settings.json");
18995
19000
  if (existsSync39(settingsPath2)) {
18996
19001
  yield* this.fromSettings(settingsPath2, "project", projectPath);
@@ -19083,7 +19088,7 @@ var init_hooks_source = __esm({
19083
19088
  });
19084
19089
 
19085
19090
  // ../engine/dist/parsers/plugins-source.js
19086
- import { existsSync as existsSync40, readFileSync as readFileSync30, readdirSync as readdirSync31, statSync as statSync36 } from "fs";
19091
+ import { existsSync as existsSync40, readFileSync as readFileSync30, readdirSync as readdirSync32, statSync as statSync36 } from "fs";
19087
19092
  import { homedir as homedir14 } from "os";
19088
19093
  import { join as join39 } from "path";
19089
19094
  function readJson3(path) {
@@ -19145,7 +19150,7 @@ var init_plugins_source = __esm({
19145
19150
  if (isSourceEnabled("gemini", "extensions") && existsSync40(gemRoot)) {
19146
19151
  let entries;
19147
19152
  try {
19148
- entries = readdirSync31(gemRoot);
19153
+ entries = readdirSync32(gemRoot);
19149
19154
  } catch {
19150
19155
  entries = [];
19151
19156
  }
@@ -19193,7 +19198,7 @@ var init_plugins_source = __esm({
19193
19198
  if (isSourceEnabled("codex", "plugins") && existsSync40(codexPluginsRoot)) {
19194
19199
  let entries;
19195
19200
  try {
19196
- entries = readdirSync31(codexPluginsRoot);
19201
+ entries = readdirSync32(codexPluginsRoot);
19197
19202
  } catch {
19198
19203
  entries = [];
19199
19204
  }
@@ -19489,13 +19494,13 @@ function markSynced(server, rows) {
19489
19494
  const o = (cursorFromCaller ? r.offset : void 0) ?? prevOS.o ?? 0;
19490
19495
  const s = (cursorFromCaller ? r.size : void 0) ?? prevOS.s ?? 0;
19491
19496
  const ev = extractorVersionForId(r.id);
19492
- const base2 = prevF ? { m: Math.floor(r.mtime), v: ev, f: prevF } : { m: Math.floor(r.mtime), v: ev };
19497
+ const base3 = prevF ? { m: Math.floor(r.mtime), v: ev, f: prevF } : { m: Math.floor(r.mtime), v: ev };
19493
19498
  if (o > 0 || s > 0) {
19494
- base2.o = o;
19495
- base2.s = s;
19499
+ base3.o = o;
19500
+ base3.s = s;
19496
19501
  }
19497
- if (r.hash !== void 0) base2.h = r.hash;
19498
- forServer[r.id] = base2;
19502
+ if (r.hash !== void 0) base3.h = r.hash;
19503
+ forServer[r.id] = base3;
19499
19504
  }
19500
19505
  persist(data);
19501
19506
  }
@@ -19737,7 +19742,7 @@ var init_http = __esm({
19737
19742
  CLI_VERSION = JSON.parse(readFileSync33(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19738
19743
  } catch {
19739
19744
  try {
19740
- if (true) CLI_VERSION = "0.5.31";
19745
+ if (true) CLI_VERSION = "0.6.0";
19741
19746
  } catch {
19742
19747
  }
19743
19748
  }
@@ -20257,7 +20262,7 @@ var init_telemetry = __esm({
20257
20262
  init_sync_client();
20258
20263
  cliVersion2 = () => {
20259
20264
  try {
20260
- return true ? "0.5.31" : "dev";
20265
+ return true ? "0.6.0" : "dev";
20261
20266
  } catch {
20262
20267
  return "dev";
20263
20268
  }
@@ -20394,11 +20399,11 @@ __export(server_probe_exports, {
20394
20399
  function probeOk(r) {
20395
20400
  return r.kind === "ok" && r.apiVersion >= 2;
20396
20401
  }
20397
- async function probeServer(base2, opts = {}) {
20402
+ async function probeServer(base3, opts = {}) {
20398
20403
  const f = opts.fetchImpl || fetch;
20399
20404
  let res;
20400
20405
  try {
20401
- res = await f(`${base2.replace(/\/+$/, "")}/api/capabilities`, {
20406
+ res = await f(`${base3.replace(/\/+$/, "")}/api/capabilities`, {
20402
20407
  signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
20403
20408
  });
20404
20409
  } catch (err) {
@@ -20435,34 +20440,34 @@ async function probeServer(base2, opts = {}) {
20435
20440
  raw: parsed
20436
20441
  };
20437
20442
  }
20438
- function probeAdvice(r, base2) {
20443
+ function probeAdvice(r, base3) {
20439
20444
  switch (r.kind) {
20440
20445
  case "ok":
20441
20446
  return r.apiVersion >= 2 ? `apiVersion ${r.apiVersion}${r.edition ? `, edition ${r.edition}` : ""}` : `apiVersion ${r.apiVersion} is too old for this CLI \u2014 upgrade the server`;
20442
20447
  case "moved":
20443
- return `HTTP ${r.status} \u2014 nothing is served at ${base2}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
20448
+ return `HTTP ${r.status} \u2014 nothing is served at ${base3}. If the host moved, re-point it: \`chat-recall login <new-server-url>\``;
20444
20449
  case "unauthorized":
20445
- return `HTTP ${r.status} \u2014 ${base2} rejected the credential. Re-login: \`chat-recall login ` + base2 + "`";
20450
+ return `HTTP ${r.status} \u2014 ${base3} rejected the credential. Re-login: \`chat-recall login ` + base3 + "`";
20446
20451
  case "server_error":
20447
- return `HTTP ${r.status} \u2014 ${base2} is up but failing. Nothing to fix locally; check the server.`;
20452
+ return `HTTP ${r.status} \u2014 ${base3} is up but failing. Nothing to fix locally; check the server.`;
20448
20453
  case "not_api":
20449
- return `${base2} answered ${r.status} with something that is not the chat-recall API (${JSON.stringify(r.snippet)}) \u2014 check the URL, a proxy or a captive portal`;
20454
+ return `${base3} answered ${r.status} with something that is not the chat-recall API (${JSON.stringify(r.snippet)}) \u2014 check the URL, a proxy or a captive portal`;
20450
20455
  case "unreachable":
20451
20456
  switch (r.reason) {
20452
20457
  case "dns":
20453
- return `cannot resolve the host in ${base2} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
20458
+ return `cannot resolve the host in ${base3} (${r.error}) \u2014 check the URL for a typo, and your DNS or VPN`;
20454
20459
  case "refused":
20455
- return `${base2} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
20460
+ return `${base3} resolves but refused the connection (${r.error}) \u2014 the server is not running, or the port is wrong`;
20456
20461
  case "tls":
20457
- return `the TLS certificate at ${base2} was rejected (${r.error}) \u2014 expired, self-signed, or issued for another host. A corporate proxy that re-signs traffic does this.`;
20462
+ return `the TLS certificate at ${base3} was rejected (${r.error}) \u2014 expired, self-signed, or issued for another host. A corporate proxy that re-signs traffic does this.`;
20458
20463
  case "timeout":
20459
- return `${base2} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
20464
+ return `${base3} did not answer in time (${r.error}) \u2014 a firewall or proxy that drops traffic silently looks exactly like this`;
20460
20465
  case "reset":
20461
- return `the connection to ${base2} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
20466
+ return `the connection to ${base3} was cut (${r.error}) \u2014 often a proxy or a captive portal`;
20462
20467
  case "bad_port":
20463
- return `${base2} names a port that Node and every browser refuse to open (the WHATWG bad-port list) \u2014 use the server's real port`;
20468
+ return `${base3} names a port that Node and every browser refuse to open (the WHATWG bad-port list) \u2014 use the server's real port`;
20464
20469
  default:
20465
- return `no HTTP response from ${base2} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
20470
+ return `no HTTP response from ${base3} (${r.error}) \u2014 check the URL, DNS, or that the server is running`;
20466
20471
  }
20467
20472
  }
20468
20473
  }
@@ -20495,8 +20500,8 @@ function describeCause(err) {
20495
20500
  const code2 = e.code;
20496
20501
  if (typeof code2 === "string" && code2 && !parts.includes(code2)) parts.push(code2);
20497
20502
  }
20498
- const base2 = err instanceof Error ? err.message : String(err);
20499
- return parts.length ? `${base2}: ${parts.join(" \u2192 ")}` : base2;
20503
+ const base3 = err instanceof Error ? err.message : String(err);
20504
+ return parts.length ? `${base3}: ${parts.join(" \u2192 ")}` : base3;
20500
20505
  }
20501
20506
  function describeUnreachable(err) {
20502
20507
  const reason = classifyUnreachable(err);
@@ -20600,7 +20605,7 @@ __export(auto_update_exports, {
20600
20605
  import { execSync as execSync2 } from "node:child_process";
20601
20606
  import { readFileSync as readFileSync36 } from "node:fs";
20602
20607
  import { fileURLToPath as fileURLToPath2 } from "node:url";
20603
- import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync32, statSync as statSync38 } from "node:fs";
20608
+ import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync33, statSync as statSync38 } from "node:fs";
20604
20609
  import { join as join43, dirname as dirname18, sep as sep2 } from "node:path";
20605
20610
  import { tmpdir as tmpdir2 } from "node:os";
20606
20611
  import { createHash as createHash12 } from "node:crypto";
@@ -20632,12 +20637,12 @@ function installedVersion() {
20632
20637
  return null;
20633
20638
  }
20634
20639
  }
20635
- function planAutoUpdate(base2, caps, ownVersion3, flag) {
20640
+ function planAutoUpdate(base3, caps, ownVersion3, flag) {
20636
20641
  const rel = caps.cli;
20637
20642
  if (!rel || !rel.version || !rel.sha256) return { update: false, reason: "server advertises no CLI release" };
20638
20643
  if (!isAutoUpdateEnabled(caps.edition, flag)) return { update: false, reason: `auto-update disabled (edition ${caps.edition ?? "?"})` };
20639
20644
  if (compareVersions(rel.version, ownVersion3) <= 0) return { update: false, reason: `already current (own ${ownVersion3} >= server ${rel.version})` };
20640
- return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(base2), sha256: rel.sha256, from: ownVersion3, to: rel.version };
20645
+ return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(base3), sha256: rel.sha256, from: ownVersion3, to: rel.version };
20641
20646
  }
20642
20647
  function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
20643
20648
  const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
@@ -20645,7 +20650,7 @@ function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
20645
20650
  let removed = 0;
20646
20651
  let entries;
20647
20652
  try {
20648
- entries = readdirSync32(root);
20653
+ entries = readdirSync33(root);
20649
20654
  } catch {
20650
20655
  return 0;
20651
20656
  }
@@ -20655,7 +20660,7 @@ function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
20655
20660
  try {
20656
20661
  const st = statSync38(dir);
20657
20662
  if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
20658
- const inside = readdirSync32(dir);
20663
+ const inside = readdirSync33(dir);
20659
20664
  if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
20660
20665
  rmSync5(dir, { recursive: true, force: true });
20661
20666
  removed++;
@@ -20733,20 +20738,20 @@ function realRestart(platform2) {
20733
20738
  else if (platform2 === "darwin") execSync2("launchctl kickstart -k gui/$(id -u)/com.chat-recall.watch", { stdio: "ignore", shell: "/bin/bash" });
20734
20739
  else if (platform2 === "win32") execSync2("schtasks /End /TN chat-recall-watch & schtasks /Run /TN chat-recall-watch", { stdio: "ignore" });
20735
20740
  }
20736
- async function runAutoUpdate(base2, authHeaders, ownVersion3, deps) {
20741
+ async function runAutoUpdate(base3, authHeaders, ownVersion3, deps) {
20737
20742
  let caps;
20738
20743
  try {
20739
- const res = await fetchWithTimeout(`${base2.replace(/\/+$/, "")}/api/capabilities`, { headers: authHeaders });
20744
+ const res = await fetchWithTimeout(`${base3.replace(/\/+$/, "")}/api/capabilities`, { headers: authHeaders });
20740
20745
  if (!res.ok) return { updated: false, reason: `capabilities HTTP ${res.status}` };
20741
20746
  caps = await res.json();
20742
20747
  } catch (e) {
20743
20748
  return { updated: false, reason: `capabilities unreachable: ${e instanceof Error ? e.message : e}` };
20744
20749
  }
20745
20750
  const effectiveOwn = newerOf(ownVersion3, installedVersion());
20746
- const plan = planAutoUpdate(base2, caps, effectiveOwn, process.env.CHAT_RECALL_AUTO_UPDATE);
20751
+ const plan = planAutoUpdate(base3, caps, effectiveOwn, process.env.CHAT_RECALL_AUTO_UPDATE);
20747
20752
  if (!plan.update) {
20748
- if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(base2)) {
20749
- reportedStandingSkip.add(base2);
20753
+ if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(base3)) {
20754
+ reportedStandingSkip.add(base3);
20750
20755
  try {
20751
20756
  const { reportClientEvent: reportClientEvent2 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
20752
20757
  reportClientEvent2("auto_update_skipped", { message: `${plan.reason} (server offers ${caps.cli?.version ?? "?"}, this device runs ${effectiveOwn})` });
@@ -20775,7 +20780,7 @@ var init_auto_update = __esm({
20775
20780
  "src/auto-update.ts"() {
20776
20781
  "use strict";
20777
20782
  init_http();
20778
- tarballUrl = (base2) => `${base2.replace(/\/+$/, "")}/install/chat-recall.tgz`;
20783
+ tarballUrl = (base3) => `${base3.replace(/\/+$/, "")}/install/chat-recall.tgz`;
20779
20784
  reportedStandingSkip = /* @__PURE__ */ new Set();
20780
20785
  }
20781
20786
  });
@@ -20810,7 +20815,7 @@ __export(sync_client_exports, {
20810
20815
  syncSessions: () => syncSessions
20811
20816
  });
20812
20817
  import { join as join44, dirname as dirname19, basename as basename22 } from "node:path";
20813
- import { readFileSync as readFileSync37, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync33, statSync as statSync39 } from "node:fs";
20818
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync34, statSync as statSync39 } from "node:fs";
20814
20819
  import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
20815
20820
  import { createHash as createHash13 } from "node:crypto";
20816
20821
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -20873,15 +20878,15 @@ function loadAllCredentials() {
20873
20878
  return [];
20874
20879
  }
20875
20880
  async function fetchTenantSecurityConfig(cred) {
20876
- const base2 = cred.serverUrl.replace(/\/+$/, "");
20877
- const cached = _securityConfigCache.get(base2);
20881
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
20882
+ const cached = _securityConfigCache.get(base3);
20878
20883
  if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
20879
20884
  const headers = { "content-type": "application/json" };
20880
20885
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
20881
20886
  try {
20882
20887
  const [rulesRes, settingsRes] = await Promise.all([
20883
- fetchWithTimeout(`${base2}/api/secrets/rules`, { headers }),
20884
- fetchWithTimeout(`${base2}/api/teams/security-config`, { headers }).catch(() => null)
20888
+ fetchWithTimeout(`${base3}/api/secrets/rules`, { headers }),
20889
+ fetchWithTimeout(`${base3}/api/teams/security-config`, { headers }).catch(() => null)
20885
20890
  ]);
20886
20891
  let tenantRules = [];
20887
20892
  let rulePackVersion = null;
@@ -20906,18 +20911,18 @@ async function fetchTenantSecurityConfig(cred) {
20906
20911
  const body = await settingsRes.json().catch(() => ({}));
20907
20912
  if (typeof body.verifySecrets === "boolean") verifySecrets = body.verifySecrets;
20908
20913
  if (typeof body.telemetry === "boolean") {
20909
- setTelemetryEligible(base2, body.telemetry);
20914
+ setTelemetryEligible(base3, body.telemetry);
20910
20915
  try {
20911
20916
  const prior = readCollectorHealth()?.telemetryEligible ?? {};
20912
20917
  updateCollectorHealth({
20913
- telemetryEligible: { ...prior, [base2]: { allowed: body.telemetry, at: Date.now() } }
20918
+ telemetryEligible: { ...prior, [base3]: { allowed: body.telemetry, at: Date.now() } }
20914
20919
  });
20915
20920
  } catch {
20916
20921
  }
20917
20922
  }
20918
20923
  }
20919
20924
  const config2 = { tenantRules, verifySecrets, rulePackVersion };
20920
- _securityConfigCache.set(base2, { fetchedAt: Date.now(), config: config2 });
20925
+ _securityConfigCache.set(base3, { fetchedAt: Date.now(), config: config2 });
20921
20926
  return config2;
20922
20927
  } catch {
20923
20928
  return { tenantRules: [], verifySecrets: true, rulePackVersion: null };
@@ -20936,18 +20941,18 @@ function readPersistedSyncConfigs() {
20936
20941
  return {};
20937
20942
  }
20938
20943
  }
20939
- function persistSyncConfig(base2, config2) {
20944
+ function persistSyncConfig(base3, config2) {
20940
20945
  try {
20941
20946
  const all = readPersistedSyncConfigs();
20942
- all[base2] = { fetchedAt: Date.now(), config: config2 };
20947
+ all[base3] = { fetchedAt: Date.now(), config: config2 };
20943
20948
  mkdirSync9(dirname19(syncConfigCachePath()), { recursive: true });
20944
20949
  writeFileSync9(syncConfigCachePath(), JSON.stringify(all, null, 2));
20945
20950
  } catch {
20946
20951
  }
20947
20952
  }
20948
20953
  async function fetchTenantSyncConfig(cred) {
20949
- const base2 = cred.serverUrl.replace(/\/+$/, "");
20950
- const cached = _syncConfigCache.get(base2);
20954
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
20955
+ const cached = _syncConfigCache.get(base3);
20951
20956
  if (cached && Date.now() - cached.fetchedAt < SECURITY_CONFIG_TTL_MS) return cached.config;
20952
20957
  const headers = { "content-type": "application/json" };
20953
20958
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
@@ -20955,7 +20960,7 @@ async function fetchTenantSyncConfig(cred) {
20955
20960
  try {
20956
20961
  const sources3 = discoverSessionSources();
20957
20962
  if (sources3.length > 0) {
20958
- await fetchWithTimeout(`${base2}/api/sync-config/sources`, {
20963
+ await fetchWithTimeout(`${base3}/api/sync-config/sources`, {
20959
20964
  method: "POST",
20960
20965
  headers,
20961
20966
  body: JSON.stringify({ device: deviceLabel(), sources: sources3 })
@@ -20963,12 +20968,12 @@ async function fetchTenantSyncConfig(cred) {
20963
20968
  }
20964
20969
  } catch {
20965
20970
  }
20966
- const res = await fetchWithTimeout(`${base2}/api/sync-config`, { headers });
20971
+ const res = await fetchWithTimeout(`${base3}/api/sync-config`, { headers });
20967
20972
  if (res.status === 404) {
20968
- _syncConfigCache.set(base2, { fetchedAt: Date.now(), config: EMPTY_SYNC_CONFIG });
20973
+ _syncConfigCache.set(base3, { fetchedAt: Date.now(), config: EMPTY_SYNC_CONFIG });
20969
20974
  return EMPTY_SYNC_CONFIG;
20970
20975
  }
20971
- if (!res.ok) return lastKnownSyncConfig(base2, `HTTP ${res.status}`);
20976
+ if (!res.ok) return lastKnownSyncConfig(base3, `HTTP ${res.status}`);
20972
20977
  const body = await res.json().catch(() => ({}));
20973
20978
  const config2 = {
20974
20979
  excludeTools: Array.isArray(body.excludeTools) ? body.excludeTools.filter((t) => typeof t === "string") : [],
@@ -21005,22 +21010,22 @@ async function fetchTenantSyncConfig(cred) {
21005
21010
  if (decided > 0) console.error(`[sync] applied ${decided} folder decision(s) from the dashboard`);
21006
21011
  } catch {
21007
21012
  }
21008
- _syncConfigCache.set(base2, { fetchedAt: Date.now(), config: config2 });
21009
- persistSyncConfig(base2, config2);
21013
+ _syncConfigCache.set(base3, { fetchedAt: Date.now(), config: config2 });
21014
+ persistSyncConfig(base3, config2);
21010
21015
  return config2;
21011
21016
  } catch (err) {
21012
21017
  if (isSyncRulesRefusal(err)) throw err;
21013
- return lastKnownSyncConfig(base2, err instanceof Error ? err.message : "request failed");
21018
+ return lastKnownSyncConfig(base3, err instanceof Error ? err.message : "request failed");
21014
21019
  }
21015
21020
  }
21016
- function lastKnownSyncConfig(base2, reason) {
21017
- const remembered = _syncConfigCache.get(base2) ?? readPersistedSyncConfigs()[base2];
21021
+ function lastKnownSyncConfig(base3, reason) {
21022
+ const remembered = _syncConfigCache.get(base3) ?? readPersistedSyncConfigs()[base3];
21018
21023
  if (remembered) {
21019
21024
  console.error(`[sync] could not read server-side sync rules (${reason}) \u2014 applying the last known set from ${new Date(remembered.fetchedAt).toISOString()}`);
21020
21025
  return remembered.config;
21021
21026
  }
21022
21027
  const refusal = new Error(
21023
- `Cannot read the server-side sync rules from ${base2} (${reason}), and this machine has never successfully read them. Refusing to sync: uploading without knowing your exclusions could ship a project you told the dashboard to hold back. Fix the server or retry.`
21028
+ `Cannot read the server-side sync rules from ${base3} (${reason}), and this machine has never successfully read them. Refusing to sync: uploading without knowing your exclusions could ship a project you told the dashboard to hold back. Fix the server or retry.`
21024
21029
  );
21025
21030
  refusal[SYNC_RULES_REFUSAL] = true;
21026
21031
  throw refusal;
@@ -21137,25 +21142,25 @@ async function syncSessions(opts = {}) {
21137
21142
  ${errors.join("\n ")}`);
21138
21143
  return { ...agg, perTarget };
21139
21144
  }
21140
- async function postFieldBatch(base2, token, rows) {
21145
+ async function postFieldBatch(base3, token, rows) {
21141
21146
  const headers = { "content-type": "application/json" };
21142
21147
  if (token) headers.authorization = `Bearer ${token}`;
21143
21148
  const sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
21144
21149
  for (let attempt = 0; ; attempt++) {
21145
- const res = await fetchWithTimeout(`${base2}/api/sync`, { method: "POST", headers, body: JSON.stringify({ fields: rows }) }, 6e4);
21150
+ const res = await fetchWithTimeout(`${base3}/api/sync`, { method: "POST", headers, body: JSON.stringify({ fields: rows }) }, 6e4);
21146
21151
  if (res.ok) return;
21147
21152
  const retryable = res.status === 429 || res.status >= 500;
21148
- if (!retryable || attempt >= 5) throw new Error(`${base2}: fields batch HTTP ${res.status} ${await res.text().catch(() => "")}`);
21153
+ if (!retryable || attempt >= 5) throw new Error(`${base3}: fields batch HTTP ${res.status} ${await res.text().catch(() => "")}`);
21149
21154
  const ra = Number(res.headers.get("retry-after")) || 0;
21150
21155
  await sleep2(ra > 0 ? ra * 1e3 : [500, 1500, 4e3, 8e3, 15e3][Math.min(attempt, 4)]);
21151
21156
  }
21152
21157
  }
21153
21158
  async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
21154
- const base2 = cred.serverUrl.replace(/\/+$/, "");
21155
- const ledger = getSyncedRows(base2);
21159
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
21160
+ const ledger = getSyncedRows(base3);
21156
21161
  let pushed = 0, absent = 0, scanned = 0;
21157
21162
  for (const field of SYNC_FIELDS) {
21158
- const needFull = !!opts.force || fieldNeedsFullPass(base2, field);
21163
+ const needFull = !!opts.force || fieldNeedsFullPass(base3, field);
21159
21164
  const candidates2 = needFull ? fullRefs() : opts.convRefs ?? fullRefs();
21160
21165
  const send = [];
21161
21166
  const mtimeById = /* @__PURE__ */ new Map();
@@ -21171,15 +21176,15 @@ async function reconcileFieldsForTarget(cred, fullRefs, opts = {}) {
21171
21176
  }
21172
21177
  for (let i = 0; i < send.length; i += 200) {
21173
21178
  const chunk = send.slice(i, i + 200);
21174
- await postFieldBatch(base2, cred.token, chunk);
21175
- markFieldCoverage(base2, field, chunk.map((r) => ({ id: r.session_id, present: true, mtime: mtimeById.get(r.session_id) ?? 0 })));
21179
+ await postFieldBatch(base3, cred.token, chunk);
21180
+ markFieldCoverage(base3, field, chunk.map((r) => ({ id: r.session_id, present: true, mtime: mtimeById.get(r.session_id) ?? 0 })));
21176
21181
  pushed += chunk.length;
21177
21182
  }
21178
21183
  if (absentIds.length) {
21179
- markFieldCoverage(base2, field, absentIds.map((id) => ({ id, present: false, mtime: mtimeById.get(id) ?? 0 })));
21184
+ markFieldCoverage(base3, field, absentIds.map((id) => ({ id, present: false, mtime: mtimeById.get(id) ?? 0 })));
21180
21185
  absent += absentIds.length;
21181
21186
  }
21182
- if (needFull) markFieldFullPassDone(base2, field);
21187
+ if (needFull) markFieldFullPassDone(base3, field);
21183
21188
  }
21184
21189
  return { pushed, absent, scanned };
21185
21190
  }
@@ -21209,7 +21214,7 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
21209
21214
  let reaped = 0;
21210
21215
  let names;
21211
21216
  try {
21212
- names = readdirSync33(dir);
21217
+ names = readdirSync34(dir);
21213
21218
  } catch {
21214
21219
  return 0;
21215
21220
  }
@@ -21296,25 +21301,25 @@ function serverIngestConcurrency(serverUrl) {
21296
21301
  return advertisedIngestConc.get(serverUrl.replace(/\/$/, "")) ?? null;
21297
21302
  }
21298
21303
  async function verifyServerApi(serverUrl, minApi) {
21299
- const base2 = serverUrl.replace(/\/$/, "");
21300
- const seen = capsOk.get(base2);
21304
+ const base3 = serverUrl.replace(/\/$/, "");
21305
+ const seen = capsOk.get(base3);
21301
21306
  if (seen !== void 0 && Date.now() - seen < CAPS_TTL_MS) return true;
21302
21307
  const { probeServer: probeServer2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
21303
- const probe = await probeServer2(base2, {
21308
+ const probe = await probeServer2(base3, {
21304
21309
  fetchImpl: ((url, init) => fetchWithTimeout(String(url), init ?? {}))
21305
21310
  });
21306
- if (probe.kind !== "ok") throw new Error(probeAdvice2(probe, base2));
21311
+ if (probe.kind !== "ok") throw new Error(probeAdvice2(probe, base3));
21307
21312
  const caps = probe.raw;
21308
21313
  if ((caps.apiVersion ?? 0) < minApi) {
21309
21314
  throw new Error(`server too old: apiVersion ${caps.apiVersion ?? "none"} < required ${minApi} \u2014 update the server image before syncing`);
21310
21315
  }
21311
21316
  const adv = caps.limits?.ingestConcurrencyPerTenant;
21312
21317
  if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
21313
- advertisedIngestConc.set(base2, Math.floor(adv));
21318
+ advertisedIngestConc.set(base3, Math.floor(adv));
21314
21319
  }
21315
- if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(base2);
21316
- else advertisedGzipBody.delete(base2);
21317
- capsOk.set(base2, Date.now());
21320
+ if (caps.limits?.acceptsGzipBody === true) advertisedGzipBody.add(base3);
21321
+ else advertisedGzipBody.delete(base3);
21322
+ capsOk.set(base3, Date.now());
21318
21323
  return true;
21319
21324
  }
21320
21325
  async function syncToTarget(cred, opts = {}) {
@@ -21379,7 +21384,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21379
21384
  const ac = new AbortController();
21380
21385
  const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
21381
21386
  try {
21382
- const res = await fetch(`${base2}/api/sync`, {
21387
+ const res = await fetch(`${base3}/api/sync`, {
21383
21388
  method: "POST",
21384
21389
  // No token ⇒ no auth header: a no-auth local server (AUTH_PROVIDER=none)
21385
21390
  // accepts it as the single 'default' tenant.
@@ -21394,26 +21399,26 @@ async function syncToTargetInScope(cred, opts = {}) {
21394
21399
  if (res.ok) {
21395
21400
  const body2 = await res.json().catch(() => ({}));
21396
21401
  if (typeof body2.telemetry === "boolean") {
21397
- setTelemetryEligible(base2, body2.telemetry);
21402
+ setTelemetryEligible(base3, body2.telemetry);
21398
21403
  try {
21399
21404
  const prior = readCollectorHealth()?.telemetryEligible ?? {};
21400
21405
  updateCollectorHealth({
21401
- telemetryEligible: { ...prior, [base2]: { allowed: body2.telemetry, at: Date.now() } }
21406
+ telemetryEligible: { ...prior, [base3]: { allowed: body2.telemetry, at: Date.now() } }
21402
21407
  });
21403
21408
  } catch {
21404
21409
  }
21405
21410
  }
21406
21411
  const adv = body2.limits?.ingestConcurrencyPerTenant;
21407
21412
  if (typeof adv === "number" && Number.isFinite(adv) && adv >= 1) {
21408
- setServerIngestConcurrency(base2, Math.floor(adv));
21413
+ setServerIngestConcurrency(base3, Math.floor(adv));
21409
21414
  }
21410
21415
  if (body2.cli && body2.cli.version) {
21411
21416
  const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
21412
21417
  const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
21413
- const plan = planAutoUpdate2(base2, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21418
+ const plan = planAutoUpdate2(base3, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21414
21419
  sweepStaleStaging2();
21415
21420
  if (plan.update) {
21416
- void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
21421
+ void runAutoUpdate2(base3, authHeaders, ownVersion).catch(() => {
21417
21422
  });
21418
21423
  }
21419
21424
  }
@@ -21422,7 +21427,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21422
21427
  const retryable = res.status === 429 || res.status >= 500;
21423
21428
  const text = await res.text().catch(() => "");
21424
21429
  if (res.status === 401 || res.status === 403) {
21425
- throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base2}`);
21430
+ throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base3}`);
21426
21431
  }
21427
21432
  if (res.status === 402) {
21428
21433
  let detail = text;
@@ -21442,7 +21447,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21442
21447
  if (attempt >= RETRY_DELAYS_MS.length) throw new Error(`sync failed after ${attempt + 1} attempts: HTTP ${res.status} ${text}`);
21443
21448
  const retryAfterS = Number(res.headers.get("retry-after"));
21444
21449
  const waitMs = res.status === 429 && retryAfterS > 0 ? retryAfterS * 1e3 : RETRY_DELAYS_MS[attempt];
21445
- console.error(`[sync] HTTP ${res.status} from ${base2} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
21450
+ console.error(`[sync] HTTP ${res.status} from ${base3} \u2014 backing off ${Math.round(waitMs / 1e3)}s (attempt ${attempt + 1})`);
21446
21451
  await sleep2(waitMs);
21447
21452
  continue;
21448
21453
  } catch (err) {
@@ -21509,11 +21514,11 @@ async function syncToTargetInScope(cred, opts = {}) {
21509
21514
  }
21510
21515
  };
21511
21516
  };
21512
- const base2 = cred.serverUrl.replace(/\/$/, "");
21513
- const ledger = opts.useLedger ? getSyncedRows(base2) : null;
21517
+ const base3 = cred.serverUrl.replace(/\/$/, "");
21518
+ const ledger = opts.useLedger ? getSyncedRows(base3) : null;
21514
21519
  const convBatch = makeBatcher("conversations", 25, false, (rows) => {
21515
21520
  try {
21516
- markSynced(base2, rows.map((r) => {
21521
+ markSynced(base3, rows.map((r) => {
21517
21522
  const off = typeof r.from_offset === "number" ? r.from_offset : 0;
21518
21523
  const hash = shippedHash.get(r.session_id);
21519
21524
  return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true } : { id: r.session_id, mtime: r.mtime, hash };
@@ -21528,7 +21533,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21528
21533
  const ac = new AbortController();
21529
21534
  const timer = setTimeout(() => ac.abort(new Error(`upload timed out after ${UPLOAD_TIMEOUT_MS}ms`)), UPLOAD_TIMEOUT_MS);
21530
21535
  try {
21531
- const res = await fetch(`${base2}/api/sync`, {
21536
+ const res = await fetch(`${base3}/api/sync`, {
21532
21537
  method: "POST",
21533
21538
  headers,
21534
21539
  body: JSON.stringify({ conversations: convs }),
@@ -21539,10 +21544,10 @@ async function syncToTargetInScope(cred, opts = {}) {
21539
21544
  if (body.cli && body.cli.version) {
21540
21545
  const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
21541
21546
  const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
21542
- const plan = planAutoUpdate2(base2, { cli: body.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21547
+ const plan = planAutoUpdate2(base3, { cli: body.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21543
21548
  sweepStaleStaging2();
21544
21549
  if (plan.update) {
21545
- void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
21550
+ void runAutoUpdate2(base3, authHeaders, ownVersion).catch(() => {
21546
21551
  });
21547
21552
  }
21548
21553
  }
@@ -21550,7 +21555,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21550
21555
  const acked = convs.filter((c) => !fullNeeded.has(c.session_id));
21551
21556
  if (acked.length > 0) {
21552
21557
  try {
21553
- markSynced(base2, acked.map((c) => ({
21558
+ markSynced(base3, acked.map((c) => ({
21554
21559
  id: c.session_id,
21555
21560
  mtime: c.mtime,
21556
21561
  offset: c.from_offset,
@@ -21620,7 +21625,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21620
21625
  const m = process.memoryUsage();
21621
21626
  console.error(`[sync-trace] ${msg} \xB7 heap=${(m.heapUsed / 1048576).toFixed(0)}MB rss=${(m.rss / 1048576).toFixed(0)}MB`);
21622
21627
  } : null;
21623
- trace?.(`walk start: ${slice.length} ref(s) \u2192 ${base2}`);
21628
+ trace?.(`walk start: ${slice.length} ref(s) \u2192 ${base3}`);
21624
21629
  if (upload.sessionMeta) {
21625
21630
  try {
21626
21631
  await reconcileFieldsForTarget(
@@ -21635,7 +21640,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21635
21640
  { convRefs: slice }
21636
21641
  );
21637
21642
  } catch (e) {
21638
- console.error(`[sync] field reconcile (${base2}): ${e instanceof Error ? e.message : e}`);
21643
+ console.error(`[sync] field reconcile (${base3}): ${e instanceof Error ? e.message : e}`);
21639
21644
  }
21640
21645
  }
21641
21646
  trace?.("field reconcile done");
@@ -21710,7 +21715,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21710
21715
  continue;
21711
21716
  } else {
21712
21717
  try {
21713
- markSynced(base2, [{ id: ref.prefixedId, mtime: ref.mtime }]);
21718
+ markSynced(base3, [{ id: ref.prefixedId, mtime: ref.mtime }]);
21714
21719
  } catch {
21715
21720
  }
21716
21721
  skipped++;
@@ -21733,7 +21738,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21733
21738
  const off = getBackendForId(ref.prefixedId)?.fileSize?.(ref.rawId) ?? 0;
21734
21739
  const priorCursor = ackRow?.s ?? 0;
21735
21740
  const safeToAck = off > 0 && off <= priorCursor;
21736
- markSynced(base2, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
21741
+ markSynced(base3, [safeToAck ? { id: ref.prefixedId, mtime: ref.mtime, offset: off, size: off, hash: built.srcHash, acked: true } : { id: ref.prefixedId, mtime: ref.mtime, hash: built.srcHash }]);
21737
21742
  } catch {
21738
21743
  }
21739
21744
  }
@@ -21743,7 +21748,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21743
21748
  if (!built) {
21744
21749
  if (ledger) {
21745
21750
  try {
21746
- markSynced(base2, [{ id: ref.prefixedId, mtime: ref.mtime }]);
21751
+ markSynced(base3, [{ id: ref.prefixedId, mtime: ref.mtime }]);
21747
21752
  } catch {
21748
21753
  }
21749
21754
  }
@@ -21799,7 +21804,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21799
21804
  walked++;
21800
21805
  if (walked % 250 === 0) console.error(`[sync] ${walked}/${slice.length} sessions\u2026`);
21801
21806
  }
21802
- const itemVersions = loadItemVersions(base2);
21807
+ const itemVersions = loadItemVersions(base3);
21803
21808
  const seenTools = /* @__PURE__ */ new Set();
21804
21809
  const seenItemKeys = /* @__PURE__ */ new Map();
21805
21810
  const registry = buildSourceRegistry();
@@ -21885,7 +21890,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21885
21890
  await appendFlush();
21886
21891
  for (const id of appendResults.fullResyncNeeded) {
21887
21892
  try {
21888
- markFullResync(base2, id);
21893
+ markFullResync(base3, id);
21889
21894
  } catch {
21890
21895
  }
21891
21896
  }
@@ -21902,7 +21907,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21902
21907
  const rec = {};
21903
21908
  for (const t of seenTools) rec[t] = extractorVersionForTool(t);
21904
21909
  for (const [k, v] of seenItemKeys) rec[k] = v;
21905
- saveItemVersions(base2, rec);
21910
+ saveItemVersions(base3, rec);
21906
21911
  } catch {
21907
21912
  }
21908
21913
  }
@@ -21915,9 +21920,9 @@ async function syncToTargetInScope(cred, opts = {}) {
21915
21920
  }
21916
21921
  if (tombstones.length > 0) {
21917
21922
  await post({ tombstones });
21918
- const data = getLedgerData(base2);
21923
+ const data = getLedgerData(base3);
21919
21924
  for (const t of tombstones) delete data[t.session_id];
21920
- persistLedgerData(base2, data);
21925
+ persistLedgerData(base3, data);
21921
21926
  }
21922
21927
  }
21923
21928
  if (opts.prune) await post({ prune_empty_sessions: true });
@@ -22347,7 +22352,7 @@ var init_sync_client = __esm({
22347
22352
  ownVersion = JSON.parse(readFileSync37(join44(dir, "../package.json"), "utf-8")).version || ownVersion;
22348
22353
  } catch {
22349
22354
  try {
22350
- if (true) ownVersion = "0.5.31";
22355
+ if (true) ownVersion = "0.6.0";
22351
22356
  } catch {
22352
22357
  }
22353
22358
  }
@@ -22382,12 +22387,17 @@ var init_sync_client = __esm({
22382
22387
  // src/update-notice.ts
22383
22388
  var update_notice_exports = {};
22384
22389
  __export(update_notice_exports, {
22390
+ newestKnown: () => newestKnown,
22385
22391
  printUpdateNotice: () => printUpdateNotice,
22386
22392
  refreshUpdateCheck: () => refreshUpdateCheck,
22387
22393
  updateNotice: () => updateNotice
22388
22394
  });
22389
22395
  import { readFileSync as readFileSync38, writeFileSync as writeFileSync10 } from "node:fs";
22390
22396
  import { join as join45 } from "node:path";
22397
+ function newestKnown(c) {
22398
+ const npm = c.npmVersion ?? "0.0.0";
22399
+ return compareVersions(npm, c.serverVersion) > 0 ? { version: npm, source: "npm" } : { version: c.serverVersion, source: "server" };
22400
+ }
22391
22401
  function readCheck() {
22392
22402
  try {
22393
22403
  const c = JSON.parse(readFileSync38(checkPath(), "utf-8"));
@@ -22406,8 +22416,24 @@ function updateNotice() {
22406
22416
  const c = readCheck();
22407
22417
  if (!c) return null;
22408
22418
  const own = cliVersion();
22409
- if (compareVersions(c.serverVersion, own) <= 0) return null;
22410
- return `chat-recall ${c.serverVersion} is available (you have ${own}). Auto-update should apply it; force it now with: chat-recall update`;
22419
+ const { version: version2, source } = newestKnown(c);
22420
+ if (compareVersions(version2, own) <= 0) return null;
22421
+ const how = source === "npm" ? "The server you sync to has not rolled it yet \u2014 install it with: npm install -g chat-recall@latest" : "Auto-update should apply it; force it now with: chat-recall update";
22422
+ return `chat-recall ${version2} is available (you have ${own}). ${how}`;
22423
+ }
22424
+ async function fetchNpmLatest() {
22425
+ if (process.env.CHAT_RECALL_NO_NPM_CHECK === "1") return null;
22426
+ try {
22427
+ const res = await fetchWithTimeout(NPM_LATEST_URL, {
22428
+ headers: { accept: "application/vnd.npm.install-v1+json" }
22429
+ }, 5e3);
22430
+ if (!res.ok) return null;
22431
+ const body = await res.json();
22432
+ const v = body["dist-tags"]?.latest;
22433
+ return typeof v === "string" && v ? v : null;
22434
+ } catch {
22435
+ return null;
22436
+ }
22411
22437
  }
22412
22438
  async function refreshUpdateCheck(force = false) {
22413
22439
  const prev = readCheck();
@@ -22417,9 +22443,9 @@ async function refreshUpdateCheck(force = false) {
22417
22443
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
22418
22444
  for (const cred of loadAllCredentials2()) {
22419
22445
  if (!cred.serverUrl) continue;
22420
- const base2 = cred.serverUrl.replace(/\/+$/, "");
22446
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
22421
22447
  try {
22422
- const res = await fetchWithTimeout(`${base2}/api/capabilities`, {
22448
+ const res = await fetchWithTimeout(`${base3}/api/capabilities`, {
22423
22449
  headers: cred.token ? { authorization: `Bearer ${cred.token}` } : {}
22424
22450
  }, 5e3);
22425
22451
  if (!res.ok) continue;
@@ -22432,8 +22458,14 @@ async function refreshUpdateCheck(force = false) {
22432
22458
  } catch {
22433
22459
  return prev;
22434
22460
  }
22435
- if (newest === "0.0.0") return prev;
22436
- const next = { serverVersion: newest, checkedAt: Date.now() };
22461
+ const npmVersion = await fetchNpmLatest();
22462
+ if (newest === "0.0.0" && !npmVersion) return prev;
22463
+ const next = {
22464
+ // A server that answered nothing must not erase what we last knew from one.
22465
+ serverVersion: newest !== "0.0.0" ? newest : prev?.serverVersion ?? "0.0.0",
22466
+ checkedAt: Date.now()
22467
+ };
22468
+ if (npmVersion) next.npmVersion = npmVersion;
22437
22469
  writeCheck(next);
22438
22470
  return next;
22439
22471
  }
@@ -22453,7 +22485,7 @@ function printUpdateNotice() {
22453
22485
  } catch {
22454
22486
  }
22455
22487
  }
22456
- var CHECK_TTL_MS, checkPath, printed;
22488
+ var CHECK_TTL_MS, NPM_LATEST_URL, checkPath, printed;
22457
22489
  var init_update_notice = __esm({
22458
22490
  "src/update-notice.ts"() {
22459
22491
  "use strict";
@@ -22461,6 +22493,7 @@ var init_update_notice = __esm({
22461
22493
  init_auto_update();
22462
22494
  init_http();
22463
22495
  CHECK_TTL_MS = 6 * 3600 * 1e3;
22496
+ NPM_LATEST_URL = "https://registry.npmjs.org/chat-recall";
22464
22497
  checkPath = () => join45(getDataDir(), "update-check.json");
22465
22498
  printed = false;
22466
22499
  }
@@ -23014,7 +23047,7 @@ __export(install_skills_exports, {
23014
23047
  skillsSourceDir: () => skillsSourceDir,
23015
23048
  uninstallSkills: () => uninstallSkills
23016
23049
  });
23017
- import { readdirSync as readdirSync34, existsSync as existsSync46, mkdirSync as mkdirSync12, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync41, statSync as statSync42, realpathSync as realpathSync4 } from "fs";
23050
+ import { readdirSync as readdirSync35, existsSync as existsSync46, mkdirSync as mkdirSync12, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync41, statSync as statSync42, realpathSync as realpathSync4 } from "fs";
23018
23051
  import { createHash as createHash15 } from "crypto";
23019
23052
  import { join as join49, basename as basename23 } from "path";
23020
23053
  function skillsSourceDir() {
@@ -23040,7 +23073,7 @@ function skillsFingerprint() {
23040
23073
  }
23041
23074
  function filesUnder(dir, prefix = "") {
23042
23075
  const out = [];
23043
- for (const e of readdirSync34(dir, { withFileTypes: true })) {
23076
+ for (const e of readdirSync35(dir, { withFileTypes: true })) {
23044
23077
  if (e.name === MARKER) continue;
23045
23078
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
23046
23079
  if (e.isDirectory()) out.push(...filesUnder(join49(dir, e.name), rel));
@@ -23054,7 +23087,7 @@ function skillStamp() {
23054
23087
  function bundledSkillNames() {
23055
23088
  const src = skillsSourceDir();
23056
23089
  if (!existsSync46(src)) return [];
23057
- return readdirSync34(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join49(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
23090
+ return readdirSync35(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join49(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
23058
23091
  }
23059
23092
  function realPathOrSelf(p) {
23060
23093
  try {
@@ -23146,7 +23179,7 @@ function uninstallSkills() {
23146
23179
  perTarget.push({ id: t.id, label: t.label, removed });
23147
23180
  continue;
23148
23181
  }
23149
- for (const entry of readdirSync34(t.dir, { withFileTypes: true })) {
23182
+ for (const entry of readdirSync35(t.dir, { withFileTypes: true })) {
23150
23183
  if (!entry.isDirectory()) continue;
23151
23184
  const dir = join49(t.dir, entry.name);
23152
23185
  if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
@@ -23849,9 +23882,9 @@ function portableCommand(cmd) {
23849
23882
  }
23850
23883
  if (isExecutableFile(cmd))
23851
23884
  return { command: cmd, rewritten: false };
23852
- const base2 = basename26(cmd);
23853
- if (onPath(base2))
23854
- return { command: base2, rewritten: true };
23885
+ const base3 = basename26(cmd);
23886
+ if (onPath(base3))
23887
+ return { command: base3, rewritten: true };
23855
23888
  return { missing: cmd };
23856
23889
  }
23857
23890
  function entryFromSpec(spec) {
@@ -24917,17 +24950,17 @@ async function collectCode(opts) {
24917
24950
  return r;
24918
24951
  };
24919
24952
  log6(`indexing ${ws} + analyses \u2026`);
24920
- const base2 = await runMcp(bin, ws, [
24953
+ const base3 = await runMcp(bin, ws, [
24921
24954
  call(1, "index_workspace", { path: ws }),
24922
24955
  call(2, "status"),
24923
24956
  call(3, "get_tree"),
24924
24957
  ...ANALYSES.map((a, i) => call(10 + i, "analyze", { analysis: a }))
24925
24958
  ]);
24926
- const st = parseJson(base2, 2, {});
24927
- const tree = parseJson(base2, 3, []);
24959
+ const st = parseJson(base3, 2, {});
24960
+ const tree = parseJson(base3, 3, []);
24928
24961
  const A = {};
24929
24962
  ANALYSES.forEach((a, i) => {
24930
- A[a] = parseJson(base2, 10 + i, {});
24963
+ A[a] = parseJson(base3, 10 + i, {});
24931
24964
  });
24932
24965
  const { health, security: sec, duplication: dup, clones, literal_scan: lit, coupling: coup, cycles: cyc, dead_code: dead, unwrap_audit: unwrap, test_coverage: coverage, architecture: arch, crossref, type_drift: typeDrift, db_schema: dbSchema, migration_parity: migParity, manifest_compliance: manifest } = A;
24933
24966
  if (tree.length === 0) {
@@ -25827,14 +25860,14 @@ function writeLedger(l) {
25827
25860
  } catch {
25828
25861
  }
25829
25862
  }
25830
- async function pushProjectTaskStatuses(base2, authHeaders, opts = {}) {
25863
+ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
25831
25864
  const out = { files: 0, pushedTasks: 0 };
25832
25865
  const ledger = readLedger();
25833
25866
  let ledgerDirty = false;
25834
25867
  for (const fk of FILE_KINDS) {
25835
25868
  let projects;
25836
25869
  try {
25837
- const res = await fetchWithTimeout(`${base2}${fk.trackedPath}`, { headers: authHeaders });
25870
+ const res = await fetchWithTimeout(`${base3}${fk.trackedPath}`, { headers: authHeaders });
25838
25871
  if (!res.ok) continue;
25839
25872
  projects = (await res.json()).projects || [];
25840
25873
  } catch {
@@ -25862,7 +25895,7 @@ async function pushProjectTaskStatuses(base2, authHeaders, opts = {}) {
25862
25895
  continue;
25863
25896
  }
25864
25897
  try {
25865
- const res = await fetchWithTimeout(`${base2}${fk.statusPath}`, {
25898
+ const res = await fetchWithTimeout(`${base3}${fk.statusPath}`, {
25866
25899
  method: "POST",
25867
25900
  headers: { "content-type": "application/json", ...authHeaders },
25868
25901
  body: JSON.stringify({ project, items: changed.map((t) => ({ id: t.id, status: t.intent })) })
@@ -25968,16 +26001,16 @@ ${block}`);
25968
26001
  return { status: "error", result: JSON.stringify({ error: e instanceof Error ? e.message : "failed" }) };
25969
26002
  }
25970
26003
  }
25971
- async function runPull(base2, opts) {
26004
+ async function runPull(base3, opts) {
25972
26005
  const { executePull: executePull2 } = await Promise.resolve().then(() => (init_toolkit_pull(), toolkit_pull_exports));
25973
26006
  const { hostname: hostname3 } = await import("node:os");
25974
- const creds = loadAllCredentials().find((c) => c.serverUrl.replace(/\/+$/, "") === base2.replace(/\/+$/, ""));
26007
+ const creds = loadAllCredentials().find((c) => c.serverUrl.replace(/\/+$/, "") === base3.replace(/\/+$/, ""));
25975
26008
  const headers = creds?.token ? { authorization: `Bearer ${creds.token}` } : {};
25976
26009
  const wanted = opts.types?.length ? opts.types : ["mcp", "skill", "command", "agent", "instructions"];
25977
26010
  const rows = [];
25978
26011
  for (const type of wanted) {
25979
26012
  try {
25980
- const res = await fetchWithTimeout(`${base2}/api/toolkit/browse/${type}?limit=1000`, { headers });
26013
+ const res = await fetchWithTimeout(`${base3}/api/toolkit/browse/${type}?limit=1000`, { headers });
25981
26014
  if (!res.ok) continue;
25982
26015
  const body = await res.json();
25983
26016
  for (const it of body.items || []) rows.push(it);
@@ -26046,20 +26079,20 @@ async function runIntent(intent, ctx) {
26046
26079
  async function drainSyncIntents(opts = {}) {
26047
26080
  const out = { processed: 0, done: 0, errored: 0 };
26048
26081
  for (const cred of loadAllCredentials()) {
26049
- const base2 = cred.serverUrl.replace(/\/+$/, "");
26082
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
26050
26083
  const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
26051
26084
  let pending;
26052
26085
  try {
26053
- const res = await fetchWithTimeout(`${base2}/api/sync-intents/pending`, { headers: authHeaders });
26086
+ const res = await fetchWithTimeout(`${base3}/api/sync-intents/pending`, { headers: authHeaders });
26054
26087
  if (!res.ok) continue;
26055
26088
  const data = await res.json();
26056
26089
  pending = data.intents || [];
26057
26090
  if (data.cli && data.cli.version) {
26058
26091
  const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
26059
- const plan = planAutoUpdate2(base2, { cli: data.cli }, ownVersion2, process.env.CHAT_RECALL_AUTO_UPDATE);
26092
+ const plan = planAutoUpdate2(base3, { cli: data.cli }, ownVersion2, process.env.CHAT_RECALL_AUTO_UPDATE);
26060
26093
  sweepStaleStaging2();
26061
26094
  if (plan.update) {
26062
- void runAutoUpdate2(base2, authHeaders, ownVersion2).catch(() => {
26095
+ void runAutoUpdate2(base3, authHeaders, ownVersion2).catch(() => {
26063
26096
  });
26064
26097
  }
26065
26098
  }
@@ -26067,7 +26100,7 @@ async function drainSyncIntents(opts = {}) {
26067
26100
  continue;
26068
26101
  }
26069
26102
  for (const intent of pending) {
26070
- const { status, result } = await runIntent(intent, { base: base2 });
26103
+ const { status, result } = await runIntent(intent, { base: base3 });
26071
26104
  out.processed++;
26072
26105
  if (status === "done") out.done++;
26073
26106
  else out.errored++;
@@ -26076,7 +26109,7 @@ async function drainSyncIntents(opts = {}) {
26076
26109
  console.error(`[sync-intent] ${label}: ${status}`);
26077
26110
  }
26078
26111
  try {
26079
- await fetchWithTimeout(`${base2}/api/sync-intents/${intent.id}/ack`, {
26112
+ await fetchWithTimeout(`${base3}/api/sync-intents/${intent.id}/ack`, {
26080
26113
  method: "POST",
26081
26114
  headers: { "content-type": "application/json", ...authHeaders },
26082
26115
  body: JSON.stringify({ status, result })
@@ -26085,7 +26118,7 @@ async function drainSyncIntents(opts = {}) {
26085
26118
  }
26086
26119
  }
26087
26120
  try {
26088
- await pushProjectTaskStatuses(base2, authHeaders, { verbose: opts.verbose });
26121
+ await pushProjectTaskStatuses(base3, authHeaders, { verbose: opts.verbose });
26089
26122
  } catch {
26090
26123
  }
26091
26124
  }
@@ -26111,7 +26144,7 @@ var init_intent_drain = __esm({
26111
26144
  ownVersion2 = JSON.parse(readFileSync46(join55(dir, "../package.json"), "utf-8")).version || ownVersion2;
26112
26145
  } catch {
26113
26146
  try {
26114
- if (true) ownVersion2 = "0.5.31";
26147
+ if (true) ownVersion2 = "0.6.0";
26115
26148
  } catch {
26116
26149
  }
26117
26150
  }
@@ -26533,7 +26566,7 @@ var init_vault_crypto = __esm({
26533
26566
  });
26534
26567
 
26535
26568
  // ../engine/dist/core/vault-source.js
26536
- import { existsSync as existsSync53, readdirSync as readdirSync35, statSync as statSync46 } from "fs";
26569
+ import { existsSync as existsSync53, readdirSync as readdirSync36, statSync as statSync46 } from "fs";
26537
26570
  import { extname, join as join57 } from "path";
26538
26571
  function* walkVaultSources() {
26539
26572
  const t = loadSettings().team.vault;
@@ -26565,18 +26598,18 @@ function* walkClaude() {
26565
26598
  return;
26566
26599
  let projectDirs = [];
26567
26600
  try {
26568
- projectDirs = readdirSync35(root);
26601
+ projectDirs = readdirSync36(root);
26569
26602
  } catch {
26570
26603
  return;
26571
26604
  }
26572
26605
  for (const proj of projectDirs) {
26573
26606
  if (proj.startsWith("."))
26574
26607
  continue;
26575
- const projectPath = decodeProjectDirName(proj);
26608
+ const projectPath = resolveProjectDirName(proj);
26576
26609
  const projDir = join57(root, proj);
26577
26610
  let entries = [];
26578
26611
  try {
26579
- entries = readdirSync35(projDir);
26612
+ entries = readdirSync36(projDir);
26580
26613
  } catch {
26581
26614
  continue;
26582
26615
  }
@@ -26598,7 +26631,7 @@ function* walkGemini() {
26598
26631
  return;
26599
26632
  let hashDirs = [];
26600
26633
  try {
26601
- hashDirs = readdirSync35(tmp);
26634
+ hashDirs = readdirSync36(tmp);
26602
26635
  } catch {
26603
26636
  return;
26604
26637
  }
@@ -26608,7 +26641,7 @@ function* walkGemini() {
26608
26641
  continue;
26609
26642
  let entries = [];
26610
26643
  try {
26611
- entries = readdirSync35(chatsDir);
26644
+ entries = readdirSync36(chatsDir);
26612
26645
  } catch {
26613
26646
  continue;
26614
26647
  }
@@ -26620,8 +26653,8 @@ function* walkGemini() {
26620
26653
  const s = safeStat(path);
26621
26654
  if (!s)
26622
26655
  continue;
26623
- const base2 = f.replace(/^session-/, "").replace(/\.(jsonl?)$/, "");
26624
- yield { sessionId: `gemini_${base2}`, tool: "gemini", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
26656
+ const base3 = f.replace(/^session-/, "").replace(/\.(jsonl?)$/, "");
26657
+ yield { sessionId: `gemini_${base3}`, tool: "gemini", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
26625
26658
  }
26626
26659
  }
26627
26660
  }
@@ -26654,7 +26687,7 @@ function* walkCodex() {
26654
26687
  const dir = stack.pop();
26655
26688
  let entries = [];
26656
26689
  try {
26657
- entries = readdirSync35(dir, { withFileTypes: true });
26690
+ entries = readdirSync36(dir, { withFileTypes: true });
26658
26691
  } catch {
26659
26692
  continue;
26660
26693
  }
@@ -26669,8 +26702,8 @@ function* walkCodex() {
26669
26702
  const s = safeStat(path);
26670
26703
  if (!s)
26671
26704
  continue;
26672
- const base2 = e.name.replace(/\.jsonl$/, "");
26673
- yield { sessionId: `codex_${base2}`, tool: "codex", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
26705
+ const base3 = e.name.replace(/\.jsonl$/, "");
26706
+ yield { sessionId: `codex_${base3}`, tool: "codex", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
26674
26707
  }
26675
26708
  }
26676
26709
  }
@@ -26680,7 +26713,7 @@ function* walkAgy() {
26680
26713
  return;
26681
26714
  let sessionDirs = [];
26682
26715
  try {
26683
- sessionDirs = readdirSync35(brainDir);
26716
+ sessionDirs = readdirSync36(brainDir);
26684
26717
  } catch {
26685
26718
  return;
26686
26719
  }
@@ -26813,11 +26846,11 @@ function vaultEnable(passphrase, opts = {}) {
26813
26846
  }
26814
26847
  async function vaultEnableWithRemote(passphrase, remote, opts = {}) {
26815
26848
  const doFetch = opts.fetchImpl ?? fetch;
26816
- const base2 = remote.baseUrl.replace(/\/+$/, "");
26849
+ const base3 = remote.baseUrl.replace(/\/+$/, "");
26817
26850
  const headers = { authorization: `Bearer ${remote.token}`, "content-type": "application/json" };
26818
26851
  let published = { saltHex: null, keyId: null };
26819
26852
  try {
26820
- const r = await doFetch(`${base2}/api/vault/salt`, { headers });
26853
+ const r = await doFetch(`${base3}/api/vault/salt`, { headers });
26821
26854
  if (r.ok)
26822
26855
  published = await r.json();
26823
26856
  } catch {
@@ -26831,7 +26864,7 @@ async function vaultEnableWithRemote(passphrase, remote, opts = {}) {
26831
26864
  }
26832
26865
  const result = vaultEnable(passphrase);
26833
26866
  try {
26834
- const r = await doFetch(`${base2}/api/vault/salt`, {
26867
+ const r = await doFetch(`${base3}/api/vault/salt`, {
26835
26868
  method: "POST",
26836
26869
  headers,
26837
26870
  body: JSON.stringify({ saltHex: result.saltHex, keyId: result.keyId })
@@ -26924,8 +26957,8 @@ async function vaultSync(passphrase) {
26924
26957
  return report;
26925
26958
  }
26926
26959
  function importedPathFor(tool, sessionId) {
26927
- const base2 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
26928
- return join58(IMPORTED_ROOT(), tool, `${base2}.jsonl`);
26960
+ const base3 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
26961
+ return join58(IMPORTED_ROOT(), tool, `${base3}.jsonl`);
26929
26962
  }
26930
26963
  async function vaultRestore(passphrase, opts = {}) {
26931
26964
  const ctx = context2();
@@ -27468,8 +27501,8 @@ async function deviceLogin(opts, onPrompt) {
27468
27501
  throw new Error("device-auth timed out \u2014 the code expired before approval.");
27469
27502
  }
27470
27503
  async function betterAuthDeviceLogin(serverUrl, onPrompt) {
27471
- const base2 = serverUrl.replace(/\/+$/, "");
27472
- const startRes = await fetch(`${base2}/api/auth/device/code`, {
27504
+ const base3 = serverUrl.replace(/\/+$/, "");
27505
+ const startRes = await fetch(`${base3}/api/auth/device/code`, {
27473
27506
  method: "POST",
27474
27507
  headers: { "content-type": "application/json" },
27475
27508
  body: JSON.stringify({ client_id: BETTER_AUTH_CLI_CLIENT_ID })
@@ -27477,7 +27510,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
27477
27510
  if (!startRes.ok) {
27478
27511
  throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => "")}`);
27479
27512
  }
27480
- const start = await readDeviceCode(startRes, base2);
27513
+ const start = await readDeviceCode(startRes, base3);
27481
27514
  onPrompt({
27482
27515
  url: start.verification_uri_complete || start.verification_uri,
27483
27516
  userCode: start.user_code,
@@ -27487,7 +27520,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
27487
27520
  let interval = (start.interval ?? 5) * 1e3;
27488
27521
  while (Date.now() < deadline) {
27489
27522
  await sleep(interval);
27490
- const res = await fetch(`${base2}/api/auth/device/token`, {
27523
+ const res = await fetch(`${base3}/api/auth/device/token`, {
27491
27524
  method: "POST",
27492
27525
  headers: { "content-type": "application/json" },
27493
27526
  body: JSON.stringify({
@@ -27572,6 +27605,140 @@ var init_install_report = __esm({
27572
27605
  }
27573
27606
  });
27574
27607
 
27608
+ // src/bulk-delete.ts
27609
+ var bulk_delete_exports = {};
27610
+ __export(bulk_delete_exports, {
27611
+ BATCH: () => BATCH,
27612
+ bulkDelete: () => bulkDelete,
27613
+ listTombstones: () => listTombstones,
27614
+ needsConfirmation: () => needsConfirmation,
27615
+ restoreSessions: () => restoreSessions,
27616
+ selectSessions: () => selectSessions
27617
+ });
27618
+ async function selectSessions(target, opts, fetchImpl = fetch) {
27619
+ const found = [];
27620
+ const want = opts.match?.trim();
27621
+ for (let offset = 0; ; offset += PAGE) {
27622
+ const qs = new URLSearchParams({ limit: String(PAGE), offset: String(offset) });
27623
+ if (opts.project) qs.set("project", opts.project);
27624
+ if (opts.tool) qs.set("tool", opts.tool);
27625
+ const res = await fetchImpl(`${base2(target.serverUrl)}/api/conversations/recent?${qs}`, {
27626
+ headers: { authorization: `Bearer ${target.token}` }
27627
+ });
27628
+ if (!res.ok) throw new Error(`list failed: HTTP ${res.status}`);
27629
+ const body = await res.json();
27630
+ const page = body.sessions ?? [];
27631
+ if (page.length === 0) break;
27632
+ for (const row of page) {
27633
+ if (!row.sessionId) continue;
27634
+ if (want !== void 0 && (row.firstPrompt ?? "").trim() !== want) continue;
27635
+ found.push(row);
27636
+ if (opts.limit && found.length >= opts.limit) return found;
27637
+ }
27638
+ if (page.length < PAGE) break;
27639
+ }
27640
+ return found;
27641
+ }
27642
+ async function bulkDelete(ids, opts = {}) {
27643
+ const unique = [...new Set(ids.filter(Boolean))];
27644
+ const targets = opts.targets ?? loadAllCredentials();
27645
+ const fetchImpl = opts.fetchImpl ?? fetch;
27646
+ const out = { requested: unique.length, perTarget: {}, deleted: 0 };
27647
+ if (unique.length === 0 || targets.length === 0) return out;
27648
+ const succeeded = /* @__PURE__ */ new Set();
27649
+ for (const t of targets) {
27650
+ let deleted = 0;
27651
+ try {
27652
+ for (let i = 0; i < unique.length; i += BATCH) {
27653
+ const slice = unique.slice(i, i + BATCH);
27654
+ const res = await fetchImpl(`${base2(t.serverUrl)}/api/sync`, {
27655
+ method: "POST",
27656
+ headers: { "content-type": "application/json", authorization: `Bearer ${t.token}` },
27657
+ body: JSON.stringify({
27658
+ tombstones: slice.map((session_id) => ({ session_id, deleted_at: Date.now() }))
27659
+ })
27660
+ });
27661
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
27662
+ deleted += slice.length;
27663
+ for (const id of slice) succeeded.add(id);
27664
+ opts.onProgress?.(deleted, unique.length);
27665
+ }
27666
+ out.perTarget[t.serverUrl] = { deleted };
27667
+ } catch (e) {
27668
+ out.perTarget[t.serverUrl] = { deleted, error: e instanceof Error ? e.message : "failed" };
27669
+ }
27670
+ }
27671
+ out.deleted = succeeded.size;
27672
+ for (const t of targets) {
27673
+ if (!out.perTarget[t.serverUrl] || out.perTarget[t.serverUrl].error) continue;
27674
+ try {
27675
+ const data = getLedgerData(t.serverUrl);
27676
+ let touched = false;
27677
+ for (const id of succeeded) {
27678
+ if (id in data) {
27679
+ delete data[id];
27680
+ touched = true;
27681
+ }
27682
+ }
27683
+ if (touched) persistLedgerData(t.serverUrl, data);
27684
+ } catch {
27685
+ }
27686
+ }
27687
+ return out;
27688
+ }
27689
+ async function listTombstones(target, limit = 500, fetchImpl = fetch) {
27690
+ const res = await fetchImpl(`${base2(target.serverUrl)}/api/data/tombstones?limit=${limit}`, {
27691
+ headers: { authorization: `Bearer ${target.token}` }
27692
+ });
27693
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
27694
+ return await res.json();
27695
+ }
27696
+ async function restoreSessions(ids, opts = {}) {
27697
+ const unique = [...new Set(ids.filter(Boolean))];
27698
+ const targets = opts.targets ?? loadAllCredentials();
27699
+ const fetchImpl = opts.fetchImpl ?? fetch;
27700
+ const out = { requested: unique.length, restored: 0, perTarget: {} };
27701
+ if (unique.length === 0 || targets.length === 0) return out;
27702
+ let best = 0;
27703
+ for (const t of targets) {
27704
+ try {
27705
+ let restored = 0, notDeleted = 0;
27706
+ for (let i = 0; i < unique.length; i += BATCH) {
27707
+ const res = await fetchImpl(`${base2(t.serverUrl)}/api/data/restore`, {
27708
+ method: "POST",
27709
+ headers: { "content-type": "application/json", authorization: `Bearer ${t.token}` },
27710
+ body: JSON.stringify({ session_ids: unique.slice(i, i + BATCH) })
27711
+ });
27712
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
27713
+ const body = await res.json();
27714
+ restored += body.restored ?? 0;
27715
+ notDeleted += (body.notDeleted ?? []).length;
27716
+ }
27717
+ out.perTarget[t.serverUrl] = { restored, notDeleted };
27718
+ best = Math.max(best, restored);
27719
+ } catch (e) {
27720
+ out.perTarget[t.serverUrl] = { restored: 0, notDeleted: 0, error: e instanceof Error ? e.message : "failed" };
27721
+ }
27722
+ }
27723
+ out.restored = best;
27724
+ return out;
27725
+ }
27726
+ function needsConfirmation(opts) {
27727
+ const enumeratedByCaller = opts.argvIds > 0 && !opts.project && !opts.stdin;
27728
+ return !enumeratedByCaller;
27729
+ }
27730
+ var PAGE, BATCH, base2;
27731
+ var init_bulk_delete = __esm({
27732
+ "src/bulk-delete.ts"() {
27733
+ "use strict";
27734
+ init_sync_client();
27735
+ init_sync_ledger();
27736
+ PAGE = 200;
27737
+ BATCH = 250;
27738
+ base2 = (url) => url.replace(/\/+$/, "");
27739
+ }
27740
+ });
27741
+
27575
27742
  // ../engine/dist/core/resume-command.js
27576
27743
  function toolOfSessionId(sessionId) {
27577
27744
  if (sessionId.startsWith("codex_"))
@@ -27782,13 +27949,13 @@ function requireTarget() {
27782
27949
  }
27783
27950
  return t;
27784
27951
  }
27785
- async function readJsonBody(res, base2, what) {
27952
+ async function readJsonBody(res, base3, what) {
27786
27953
  const text = await res.text().catch(() => "");
27787
27954
  try {
27788
27955
  return JSON.parse(text);
27789
27956
  } catch {
27790
27957
  throw new Error(
27791
- `${base2} answered ${res.status} with something that is not JSON (${JSON.stringify(text.trim().slice(0, 60))}) for ${what} \u2014 a proxy, a captive portal or a host that no longer serves the chat-recall API`
27958
+ `${base3} answered ${res.status} with something that is not JSON (${JSON.stringify(text.trim().slice(0, 60))}) for ${what} \u2014 a proxy, a captive portal or a host that no longer serves the chat-recall API`
27792
27959
  );
27793
27960
  }
27794
27961
  }
@@ -28010,7 +28177,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
28010
28177
  const projectRoot = join60(import.meta.dirname, "..");
28011
28178
  let mcpBinOnPath = false;
28012
28179
  mcpBinOnPath = isOnPath("chat-recall-mcp");
28013
- const launch = mcpBinOnPath ? { command: "chat-recall-mcp" } : { command: "node", args: [join60(projectRoot, "dist", "mcp.js")] };
28180
+ const launch = mcpBinOnPath ? { command: "chat-recall-mcp" } : { command: "node", args: [join60(projectRoot, "dist", "mcp-relay.js")] };
28014
28181
  const MCP_ENV = { NODE_OPTIONS: "--max-old-space-size=1024" };
28015
28182
  const { registerMcpEverywhere: registerMcpEverywhere2 } = await Promise.resolve().then(() => (init_mcp_clients(), mcp_clients_exports));
28016
28183
  const MCP_CLIENT_FOR_BIN = {
@@ -28573,9 +28740,9 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
28573
28740
  return;
28574
28741
  }
28575
28742
  const target = firstTarget();
28576
- const base2 = target?.base || (process.env.CHAT_RECALL_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
28743
+ const base3 = target?.base || (process.env.CHAT_RECALL_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
28577
28744
  try {
28578
- const res = await fetch(`${base2}/api/contact/feedback`, {
28745
+ const res = await fetch(`${base3}/api/contact/feedback`, {
28579
28746
  method: "POST",
28580
28747
  headers: {
28581
28748
  "content-type": "application/json",
@@ -28602,7 +28769,7 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
28602
28769
  console.log(chalk2.dim(" Add --email <address> next time if you want a reply."));
28603
28770
  }
28604
28771
  } catch (err) {
28605
- console.error(chalk2.red(`Could not reach ${base2}.`));
28772
+ console.error(chalk2.red(`Could not reach ${base3}.`));
28606
28773
  console.error(chalk2.dim(" Your message, so it is not lost:"));
28607
28774
  console.error(chalk2.dim(` ${message}`));
28608
28775
  process.exitCode = 1;
@@ -29170,13 +29337,13 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
29170
29337
  }
29171
29338
  const { fetchWithTimeout: fetchWithTimeout2 } = await Promise.resolve().then(() => (init_http(), http_exports));
29172
29339
  for (const t of targets) {
29173
- const base2 = t.serverUrl.replace(/\/+$/, "");
29340
+ const base3 = t.serverUrl.replace(/\/+$/, "");
29174
29341
  const headers = {};
29175
29342
  if (t.token) headers.authorization = `Bearer ${t.token}`;
29176
29343
  let deleted = 0, missing = 0, failed = 0;
29177
29344
  for (const id of ids) {
29178
29345
  try {
29179
- const res = await fetchWithTimeout2(`${base2}/api/conversations/${encodeURIComponent(id)}`, { method: "DELETE", headers }, 3e4);
29346
+ const res = await fetchWithTimeout2(`${base3}/api/conversations/${encodeURIComponent(id)}`, { method: "DELETE", headers }, 3e4);
29180
29347
  if (res.ok) deleted++;
29181
29348
  else if (res.status === 404) missing++;
29182
29349
  else failed++;
@@ -29184,7 +29351,7 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
29184
29351
  failed++;
29185
29352
  }
29186
29353
  }
29187
- const line = ` ${base2}: deleted ${deleted}` + (missing ? `, ${missing} were not there` : "") + (failed ? chalk2.red(`, ${failed} FAILED`) : "");
29354
+ const line = ` ${base3}: deleted ${deleted}` + (missing ? `, ${missing} were not there` : "") + (failed ? chalk2.red(`, ${failed} FAILED`) : "");
29188
29355
  console.log(failed ? chalk2.yellow(line) : chalk2.green(line));
29189
29356
  if (failed) process.exitCode = 1;
29190
29357
  }
@@ -30032,10 +30199,10 @@ program.command("service <action>").description("Manage the per-user background
30032
30199
  });
30033
30200
  async function runLogin(serverUrl, opts) {
30034
30201
  const { saveCredentials: saveCredentials2, loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30035
- const base2 = serverUrl.replace(/\/+$/, "");
30202
+ const base3 = serverUrl.replace(/\/+$/, "");
30036
30203
  const verifyToken = async (token) => {
30037
30204
  try {
30038
- const res = await fetch(`${base2}/api/status`, {
30205
+ const res = await fetch(`${base3}/api/status`, {
30039
30206
  headers: token ? { authorization: `Bearer ${token}` } : {},
30040
30207
  signal: AbortSignal.timeout(1e4)
30041
30208
  });
@@ -30051,36 +30218,36 @@ async function runLogin(serverUrl, opts) {
30051
30218
  return failLogin(opts, 'the token contains non-ASCII characters \u2014 this looks like a masked/truncated copy (e.g. "ct_12345678\u2026")');
30052
30219
  }
30053
30220
  const { probeServer: identifyServer, probeOk: identified, probeAdvice: identifyAdvice } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
30054
- const who = await identifyServer(base2, { timeoutMs: 1e4 });
30055
- if (!identified(who)) return failLogin(opts, identifyAdvice(who, base2));
30221
+ const who = await identifyServer(base3, { timeoutMs: 1e4 });
30222
+ if (!identified(who)) return failLogin(opts, identifyAdvice(who, base3));
30056
30223
  const check = await verifyToken(token);
30057
30224
  if (!check.ok) {
30058
30225
  if (check.status === 401 || check.status === 403) {
30059
30226
  console.error(chalk2.dim("Tokens are shown once \u2014 mint a fresh one (Account \u2192 Connect your machine) and copy the whole line."));
30060
- return failLogin(opts, `${base2} rejected the token (HTTP ${check.status})`);
30227
+ return failLogin(opts, `${base3} rejected the token (HTTP ${check.status})`);
30061
30228
  }
30062
30229
  if (check.status !== void 0 && check.status >= 500) {
30063
- return failLogin(opts, `${base2} answered HTTP ${check.status} \u2014 the server is up but failing. The token was never checked. Nothing to fix locally.`);
30230
+ return failLogin(opts, `${base3} answered HTTP ${check.status} \u2014 the server is up but failing. The token was never checked. Nothing to fix locally.`);
30064
30231
  }
30065
30232
  if (check.status === 404 || check.status === 410) {
30066
- return failLogin(opts, `${base2} does not serve the chat-recall API (HTTP ${check.status}). If the server moved, log in to its new URL.`);
30233
+ return failLogin(opts, `${base3} does not serve the chat-recall API (HTTP ${check.status}). If the server moved, log in to its new URL.`);
30067
30234
  }
30068
- return failLogin(opts, `could not verify the token against ${base2}: ${check.error || `HTTP ${check.status}`}`);
30235
+ return failLogin(opts, `could not verify the token against ${base3}: ${check.error || `HTTP ${check.status}`}`);
30069
30236
  }
30070
30237
  saveCredentials2({ serverUrl, token });
30071
30238
  console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` server: ${serverUrl} (token verified)`));
30072
30239
  return;
30073
30240
  }
30074
- const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") === base2);
30241
+ const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") === base3);
30075
30242
  if (existing && (await verifyToken(existing.token)).ok) {
30076
30243
  console.log(chalk2.green("\u2713 Already connected.") + chalk2.dim(` server: ${serverUrl} (existing token verified)`));
30077
30244
  return;
30078
30245
  }
30079
30246
  try {
30080
30247
  const { probeServer: probeServer2, probeOk: probeOk2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
30081
- const identified = await probeServer2(base2, { timeoutMs: 8e3 });
30248
+ const identified = await probeServer2(base3, { timeoutMs: 8e3 });
30082
30249
  if (probeOk2(identified)) {
30083
- const probe = await fetch(`${base2}/api/status`, { signal: AbortSignal.timeout(8e3) });
30250
+ const probe = await fetch(`${base3}/api/status`, { signal: AbortSignal.timeout(8e3) });
30084
30251
  if (probe.status === 200) {
30085
30252
  saveCredentials2({ serverUrl, token: "" });
30086
30253
  console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` ${serverUrl} (local server \u2014 no auth, no token needed)`));
@@ -30101,16 +30268,16 @@ async function runLogin(serverUrl, opts) {
30101
30268
  if (!issuer) {
30102
30269
  const { probeServer: probeServer2, probeOk: probeOk2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
30103
30270
  for (let attempt = 1; attempt <= 3 && !authProvider; attempt++) {
30104
- const probe = await probeServer2(base2, { timeoutMs: 15e3 });
30271
+ const probe = await probeServer2(base3, { timeoutMs: 15e3 });
30105
30272
  if (probeOk2(probe)) {
30106
30273
  const caps = probe.raw;
30107
30274
  authProvider = caps?.authProvider;
30108
30275
  if (caps?.oidcIssuer) issuer = caps.oidcIssuer;
30109
30276
  capsError = null;
30110
30277
  } else if (probe.kind === "moved" || probe.kind === "not_api") {
30111
- return failLogin(opts, probeAdvice2(probe, base2));
30278
+ return failLogin(opts, probeAdvice2(probe, base3));
30112
30279
  } else {
30113
- capsError = probeAdvice2(probe, base2);
30280
+ capsError = probeAdvice2(probe, base3);
30114
30281
  }
30115
30282
  if (!authProvider && attempt < 3) await new Promise((r) => setTimeout(r, attempt * 1e3));
30116
30283
  }
@@ -30119,7 +30286,7 @@ async function runLogin(serverUrl, opts) {
30119
30286
  authProvider = "better-auth";
30120
30287
  if (capsError) {
30121
30288
  console.log(` ${chalk2.yellow("Could not read the server's capabilities")} (${capsError}) \u2014 assuming the standard sign-in flow.`);
30122
- void Promise.resolve().then(() => (init_install_report(), install_report_exports)).then(({ reportInstallFailure: reportInstallFailure2 }) => reportInstallFailure2(base2, {
30289
+ void Promise.resolve().then(() => (init_install_report(), install_report_exports)).then(({ reportInstallFailure: reportInstallFailure2 }) => reportInstallFailure2(base3, {
30123
30290
  step: "cli_login_probe",
30124
30291
  reason: capsError?.slice(0, 40) ?? null,
30125
30292
  message: capsError
@@ -30141,20 +30308,20 @@ async function runLogin(serverUrl, opts) {
30141
30308
  console.log(chalk2.dim(` If the page asks for a code: ${chalk2.bold(p.userCode)}`));
30142
30309
  console.log(chalk2.dim("Then come back here \u2014 this finishes on its own. Waiting\u2026"));
30143
30310
  };
30144
- const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base2, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
30311
+ const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base3, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
30145
30312
  const authHdr = { authorization: `Bearer ${tokens.accessToken}`, "content-type": "application/json" };
30146
- const meRes = await fetch(`${base2}/api/me`, { headers: authHdr });
30147
- if (!meRes.ok) throw new Error(`${base2}/api/me: HTTP ${meRes.status} ${await meRes.text().catch(() => "")}`);
30148
- const me = await readJsonBody(meRes, base2, "GET /api/me");
30313
+ const meRes = await fetch(`${base3}/api/me`, { headers: authHdr });
30314
+ if (!meRes.ok) throw new Error(`${base3}/api/me: HTTP ${meRes.status} ${await meRes.text().catch(() => "")}`);
30315
+ const me = await readJsonBody(meRes, base3, "GET /api/me");
30149
30316
  const teams = me.teams || [];
30150
30317
  let slug = opts.team;
30151
30318
  if (!slug) {
30152
30319
  if (teams.length === 1) slug = teams[0].team_slug;
30153
30320
  else if (teams.length === 0) {
30154
30321
  const name = me.user?.email?.split("@")[0]?.replace(/[^a-z0-9]/gi, "") || "workspace";
30155
- const created = await fetch(`${base2}/api/teams`, { method: "POST", headers: authHdr, body: JSON.stringify({ name }) });
30322
+ const created = await fetch(`${base3}/api/teams`, { method: "POST", headers: authHdr, body: JSON.stringify({ name }) });
30156
30323
  if (!created.ok) throw new Error(`workspace create failed: HTTP ${created.status} ${await created.text().catch(() => "")}`);
30157
- slug = (await readJsonBody(created, base2, "POST /api/teams")).slug;
30324
+ slug = (await readJsonBody(created, base3, "POST /api/teams")).slug;
30158
30325
  console.log(chalk2.dim(`Created workspace "${name}" (${slug}) \u2014 you're its owner.`));
30159
30326
  } else {
30160
30327
  teams.forEach((t) => console.error(` ${t.team_slug} ${chalk2.dim(t.name)}`));
@@ -30162,13 +30329,13 @@ async function runLogin(serverUrl, opts) {
30162
30329
  }
30163
30330
  }
30164
30331
  const deviceId = opts.deviceId || (await import("node:os")).hostname();
30165
- const mint = await fetch(`${base2}/api/teams/${encodeURIComponent(slug)}/tokens`, {
30332
+ const mint = await fetch(`${base3}/api/teams/${encodeURIComponent(slug)}/tokens`, {
30166
30333
  method: "POST",
30167
30334
  headers: authHdr,
30168
30335
  body: JSON.stringify({ device_id: deviceId })
30169
30336
  });
30170
30337
  if (!mint.ok) throw new Error(`token mint failed: HTTP ${mint.status} ${await mint.text().catch(() => "")}`);
30171
- const { token } = await readJsonBody(mint, base2, "POST /api/teams/:slug/tokens");
30338
+ const { token } = await readJsonBody(mint, base3, "POST /api/teams/:slug/tokens");
30172
30339
  saveCredentials2({ serverUrl, token });
30173
30340
  console.log(chalk2.green(`\u2713 Logged in to ${chalk2.bold(slug)}`) + chalk2.dim(` (device: ${deviceId}) server: ${serverUrl}`));
30174
30341
  if (!opts.fromInit) {
@@ -30420,30 +30587,142 @@ program.command("logout <server-url>").description("Remove one sync target (othe
30420
30587
  process.exit(1);
30421
30588
  }
30422
30589
  });
30423
- program.command("delete <session-id>").description("Delete a session from chat-recall everywhere: purges it on every logged-in server and tombstones it so it can't resurrect on the next sync. Does NOT touch the AI tool's own transcript file.").action(async (sessionId) => {
30590
+ program.command("delete [session-ids...]").description("Delete sessions from chat-recall everywhere: purges them on every logged-in server and tombstones them so they can't resurrect on the next sync. Does NOT touch the AI tool's own transcript files.").option("--project <id>", "Select every session in a project (e.g. git:github.com/me/repo)").option("--match <text>", "With --project: only sessions whose first prompt is EXACTLY this").option("--tool <name>", "With --project: only sessions from this tool (claude, codex, \u2026)").option("--stdin", "Read session ids from stdin, one per line").option("-n, --dry-run", "List what would be deleted and exit").option("-y, --yes", "Skip the confirmation prompt").action(async (sessionIds, opts) => {
30424
30591
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30592
+ const { selectSessions: selectSessions2, bulkDelete: bulkDelete2, needsConfirmation: needsConfirmation2 } = await Promise.resolve().then(() => (init_bulk_delete(), bulk_delete_exports));
30425
30593
  const targets = loadAllCredentials2();
30426
30594
  if (targets.length === 0) {
30427
30595
  console.error(chalk2.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30428
30596
  process.exit(1);
30429
30597
  }
30430
- let ok = 0;
30431
- for (const t of targets) {
30598
+ if ((opts.match || opts.tool) && !opts.project) {
30599
+ console.error(chalk2.red("--match and --tool select WITHIN a project; pass --project too."));
30600
+ process.exit(1);
30601
+ }
30602
+ let ids = [...sessionIds];
30603
+ let preview = [];
30604
+ if (opts.stdin) {
30605
+ const stdin = await new Promise((resolve3) => {
30606
+ let buf = "";
30607
+ process.stdin.setEncoding("utf-8");
30608
+ process.stdin.on("data", (d) => {
30609
+ buf += d;
30610
+ });
30611
+ process.stdin.on("end", () => resolve3(buf));
30612
+ });
30613
+ ids.push(...stdin.split("\n").map((l) => l.trim()).filter(Boolean));
30614
+ }
30615
+ if (opts.project) {
30432
30616
  try {
30433
- const res = await fetch(`${t.serverUrl.replace(/\/+$/, "")}/api/conversations/${encodeURIComponent(sessionId)}`, {
30434
- method: "DELETE",
30435
- headers: { authorization: `Bearer ${t.token}` }
30617
+ preview = await selectSessions2(targets[0], { project: opts.project, match: opts.match, tool: opts.tool });
30618
+ } catch (e) {
30619
+ console.error(chalk2.red(`Could not list sessions: ${e instanceof Error ? e.message : "failed"}`));
30620
+ process.exit(1);
30621
+ }
30622
+ ids.push(...preview.map((r) => r.sessionId));
30623
+ }
30624
+ ids = [...new Set(ids.filter(Boolean))];
30625
+ if (ids.length === 0) {
30626
+ console.log(chalk2.yellow("Nothing selected \u2014 no session ids given and no selector matched."));
30627
+ return;
30628
+ }
30629
+ const shown = preview.length > 0 ? preview.slice(0, 10) : ids.slice(0, 10).map((id) => ({ sessionId: id, firstPrompt: void 0 }));
30630
+ for (const r of shown) {
30631
+ const label = r.firstPrompt ? chalk2.dim(` \u2014 ${r.firstPrompt.replace(/\s+/g, " ").slice(0, 60)}`) : "";
30632
+ console.log(` ${r.sessionId}${label}`);
30633
+ }
30634
+ if (ids.length > shown.length) console.log(chalk2.dim(` \u2026 and ${ids.length - shown.length} more`));
30635
+ if (opts.dryRun) {
30636
+ console.log(chalk2.cyan(`
30637
+ Dry run \u2014 ${ids.length} session(s) would be deleted on ${targets.length} server(s). Nothing was changed.`));
30638
+ return;
30639
+ }
30640
+ if (!opts.yes && needsConfirmation2({ argvIds: sessionIds.length, project: opts.project, stdin: opts.stdin })) {
30641
+ if (!process.stdin.isTTY) {
30642
+ console.error(chalk2.red(`
30643
+ Refusing to delete ${ids.length} session(s) without confirmation \u2014 pass --yes (or -n to preview).`));
30644
+ process.exit(1);
30645
+ }
30646
+ const readline = await import("node:readline/promises");
30647
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
30648
+ const answer = await rl.question(chalk2.yellow(`
30649
+ Permanently delete ${ids.length} session(s) on ${targets.length} server(s)? This cannot be undone. [y/N] `));
30650
+ rl.close();
30651
+ if (answer.trim().toLowerCase() !== "y") {
30652
+ console.log("Aborted \u2014 nothing was deleted.");
30653
+ return;
30654
+ }
30655
+ }
30656
+ const res = await bulkDelete2(ids, { targets });
30657
+ for (const [server, r] of Object.entries(res.perTarget)) {
30658
+ if (r.error) console.error(chalk2.red(`\u2717 ${server}: ${r.deleted}/${ids.length} deleted, then failed \u2014 ${r.error}`));
30659
+ else console.log(chalk2.green(`\u2713 ${server}: ${r.deleted} deleted`));
30660
+ }
30661
+ if (res.deleted === 0) {
30662
+ console.error(chalk2.red("Nothing was deleted."));
30663
+ process.exit(1);
30664
+ }
30665
+ console.log(chalk2.dim(`Tombstoned \u2014 re-sync cannot resurrect them. Local transcript files were not touched.`));
30666
+ if (res.deleted < ids.length) {
30667
+ console.error(chalk2.yellow(`Note: ${ids.length - res.deleted} session(s) did not delete on any server.`));
30668
+ }
30669
+ });
30670
+ program.command("restore [session-ids...]").description("Undo a delete: lifts the tombstone so those sessions can be synced again. Restores no content by itself \u2014 re-run `chat-recall index --force` on a machine that still has the transcript. With no ids, lists what has been deleted.").option("--stdin", "Read session ids from stdin, one per line").option("--list", "Just show what has been deleted, newest first").option("--limit <n>", "How many deletions to list (default 50)").action(async (sessionIds, opts) => {
30671
+ const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30672
+ const { listTombstones: listTombstones2, restoreSessions: restoreSessions2 } = await Promise.resolve().then(() => (init_bulk_delete(), bulk_delete_exports));
30673
+ const targets = loadAllCredentials2();
30674
+ if (targets.length === 0) {
30675
+ console.error(chalk2.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30676
+ process.exit(1);
30677
+ }
30678
+ const ids = [...sessionIds];
30679
+ if (opts.stdin) {
30680
+ const buf = await new Promise((resolve3) => {
30681
+ let b = "";
30682
+ process.stdin.setEncoding("utf-8");
30683
+ process.stdin.on("data", (d) => {
30684
+ b += d;
30436
30685
  });
30437
- if (res.ok) {
30438
- ok++;
30439
- console.log(chalk2.green(`\u2713 Deleted on ${t.serverUrl}`));
30440
- } else console.error(chalk2.red(`\u2717 ${t.serverUrl}: HTTP ${res.status}`));
30686
+ process.stdin.on("end", () => resolve3(b));
30687
+ });
30688
+ ids.push(...buf.split("\n").map((l) => l.trim()).filter(Boolean));
30689
+ }
30690
+ if (opts.list || ids.length === 0) {
30691
+ const limit = Math.max(1, parseInt(opts.limit ?? "") || 50);
30692
+ try {
30693
+ const { total, tombstones } = await listTombstones2(targets[0], limit);
30694
+ if (total === 0) {
30695
+ console.log(chalk2.dim("Nothing has been deleted."));
30696
+ return;
30697
+ }
30698
+ console.log(chalk2.bold(`${total} deleted session(s) \u2014 newest first:
30699
+ `));
30700
+ for (const t of tombstones) {
30701
+ console.log(` ${t.session_id} ${chalk2.dim(new Date(t.deleted_at).toISOString().replace("T", " ").slice(0, 19))}`);
30702
+ }
30703
+ if (total > tombstones.length) console.log(chalk2.dim(`
30704
+ \u2026 and ${total - tombstones.length} more (--limit to see them)`));
30705
+ console.log(chalk2.dim("\nRestore with: chat-recall restore <id...>"));
30441
30706
  } catch (e) {
30442
- console.error(chalk2.red(`\u2717 ${t.serverUrl}: ${e instanceof Error ? e.message : "failed"}`));
30707
+ console.error(chalk2.red(`Could not list deletions: ${e instanceof Error ? e.message : "failed"}`));
30708
+ process.exit(1);
30443
30709
  }
30710
+ return;
30444
30711
  }
30445
- if (ok === 0) process.exit(1);
30446
- console.log(chalk2.dim(`Tombstoned on ${ok}/${targets.length} server(s) \u2014 re-sync cannot resurrect it.`));
30712
+ const res = await restoreSessions2(ids, { targets });
30713
+ for (const [server, r] of Object.entries(res.perTarget)) {
30714
+ if (r.error) console.error(chalk2.red(`\u2717 ${server}: ${r.error}`));
30715
+ else {
30716
+ console.log(chalk2.green(`\u2713 ${server}: ${r.restored} tombstone(s) lifted`));
30717
+ if (r.notDeleted > 0) console.log(chalk2.yellow(` ${r.notDeleted} id(s) were not deleted here \u2014 nothing to lift`));
30718
+ }
30719
+ }
30720
+ if (res.restored === 0) {
30721
+ console.error(chalk2.red("No tombstones were lifted."));
30722
+ process.exit(1);
30723
+ }
30724
+ console.log(chalk2.dim("\nThe content is still gone from the server. Re-upload it from a machine that"));
30725
+ console.log(chalk2.dim("still holds the transcript: chat-recall index --force"));
30447
30726
  });
30448
30727
  program.command("rename <session-id> <name>").description('Give a session a memorable name on every logged-in server (mirrors Claude Code\'s /rename \u2014 shows in `recall recent` and the web UI in place of the auto summary). Pass an empty name "" to clear it and revert to the auto title.').action(async (sessionId, name) => {
30449
30728
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
@@ -30522,9 +30801,9 @@ program.command("reconcile").description("Reconcile DERIVED FIELDS (e.g. native
30522
30801
  process.exit(1);
30523
30802
  }
30524
30803
  });
30525
- program.command("update").description("Update this CLI now from the server you sync to (same-origin, checksum-pinned) and report exactly why if it cannot").action(async () => {
30526
- const { runAutoUpdate: runAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
30527
- const { refreshUpdateCheck: refreshUpdateCheck2 } = await Promise.resolve().then(() => (init_update_notice(), update_notice_exports));
30804
+ program.command("update").description("Update this CLI now from the server you sync to (same-origin, checksum-pinned) and report exactly why if it cannot. Use --from-npm when the server has not rolled the newest release yet.").option("--from-npm", "Install the newest published release straight from the npm registry instead of the server tarball").action(async (opts) => {
30805
+ const { runAutoUpdate: runAutoUpdate2, compareVersions: compareVersions2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
30806
+ const { refreshUpdateCheck: refreshUpdateCheck2, newestKnown: newestKnown2 } = await Promise.resolve().then(() => (init_update_notice(), update_notice_exports));
30528
30807
  const { cliVersion: cliVersion3 } = await Promise.resolve().then(() => (init_http(), http_exports));
30529
30808
  const targets = loadAllCredentials();
30530
30809
  if (!targets.length) {
@@ -30533,22 +30812,54 @@ program.command("update").description("Update this CLI now from the server you s
30533
30812
  }
30534
30813
  let installFailed = false;
30535
30814
  for (const cred of targets) {
30536
- const base2 = cred.serverUrl.replace(/\/+$/, "");
30815
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
30537
30816
  const headers = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
30538
- const r = await runAutoUpdate2(base2, headers, cliVersion3());
30817
+ const r = await runAutoUpdate2(base3, headers, cliVersion3());
30539
30818
  if (r.updated) {
30540
- console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${base2})`));
30819
+ console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${base3})`));
30541
30820
  continue;
30542
30821
  }
30543
30822
  const benign = /already current|no CLI release|disabled/.test(r.reason);
30544
30823
  if (/install failed|download failed|checksum/.test(r.reason)) installFailed = true;
30545
- console.log((benign ? chalk2.dim("\u2022 ") : chalk2.red("\u2717 ")) + r.reason + chalk2.dim(` (${base2})`));
30824
+ console.log((benign ? chalk2.dim("\u2022 ") : chalk2.red("\u2717 ")) + r.reason + chalk2.dim(` (${base3})`));
30546
30825
  }
30547
- await refreshUpdateCheck2(true);
30826
+ const check = await refreshUpdateCheck2(true);
30548
30827
  if (installFailed) {
30549
30828
  console.log(chalk2.dim("\nThe update was available but did not install \u2014 usually npm cannot write the global prefix. Retry with:"));
30550
30829
  console.log(chalk2.dim(` npm install -g chat-recall (or re-run the installer: curl -fsSL ${targets[0].serverUrl.replace(/\/+$/, "")}/install | sh)`));
30551
30830
  }
30831
+ if (check) {
30832
+ const own = cliVersion3();
30833
+ const { version: version2, source } = newestKnown2(check);
30834
+ const behind = source === "npm" && compareVersions2(version2, own) > 0;
30835
+ if (behind && !opts.fromNpm) {
30836
+ console.log(chalk2.yellow(`
30837
+ npm has ${version2}; the server you sync to still serves ${check.serverVersion}.`));
30838
+ console.log(chalk2.dim("The server tarball cannot deliver it until that deployment rolls. Get it now with:"));
30839
+ console.log(chalk2.dim(" chat-recall update --from-npm"));
30840
+ } else if (behind && opts.fromNpm) {
30841
+ console.log(chalk2.dim(`
30842
+ Installing ${version2} from npm\u2026`));
30843
+ try {
30844
+ const { execFileSync: execFileSync4 } = await import("node:child_process");
30845
+ execFileSync4("npm", ["install", "-g", `chat-recall@${version2}`], { stdio: "inherit" });
30846
+ const { installedVersion: installedVersion2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
30847
+ const landed = installedVersion2();
30848
+ if (landed && compareVersions2(landed, version2) >= 0) {
30849
+ console.log(chalk2.green(`\u2713 updated to ${landed} from npm`));
30850
+ } else {
30851
+ console.error(chalk2.red(`\u2717 npm reported success but ${landed ?? "an unknown version"} is still on disk \u2014 the package that runs is not the one npm wrote.`));
30852
+ process.exit(1);
30853
+ }
30854
+ } catch (e) {
30855
+ console.error(chalk2.red(`\u2717 npm install failed: ${e instanceof Error ? e.message : e}`));
30856
+ process.exit(1);
30857
+ }
30858
+ } else if (opts.fromNpm) {
30859
+ console.log(chalk2.dim(`
30860
+ npm has nothing newer than ${own} \u2014 nothing to do.`));
30861
+ }
30862
+ }
30552
30863
  });
30553
30864
  program.hook("preAction", () => {
30554
30865
  printUpdateNotice();