chat-recall 0.5.31 → 0.5.32

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.5.32";
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.5.32" : "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.5.32";
22351
22356
  } catch {
22352
22357
  }
22353
22358
  }
@@ -22417,9 +22422,9 @@ async function refreshUpdateCheck(force = false) {
22417
22422
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
22418
22423
  for (const cred of loadAllCredentials2()) {
22419
22424
  if (!cred.serverUrl) continue;
22420
- const base2 = cred.serverUrl.replace(/\/+$/, "");
22425
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
22421
22426
  try {
22422
- const res = await fetchWithTimeout(`${base2}/api/capabilities`, {
22427
+ const res = await fetchWithTimeout(`${base3}/api/capabilities`, {
22423
22428
  headers: cred.token ? { authorization: `Bearer ${cred.token}` } : {}
22424
22429
  }, 5e3);
22425
22430
  if (!res.ok) continue;
@@ -23014,7 +23019,7 @@ __export(install_skills_exports, {
23014
23019
  skillsSourceDir: () => skillsSourceDir,
23015
23020
  uninstallSkills: () => uninstallSkills
23016
23021
  });
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";
23022
+ 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
23023
  import { createHash as createHash15 } from "crypto";
23019
23024
  import { join as join49, basename as basename23 } from "path";
23020
23025
  function skillsSourceDir() {
@@ -23040,7 +23045,7 @@ function skillsFingerprint() {
23040
23045
  }
23041
23046
  function filesUnder(dir, prefix = "") {
23042
23047
  const out = [];
23043
- for (const e of readdirSync34(dir, { withFileTypes: true })) {
23048
+ for (const e of readdirSync35(dir, { withFileTypes: true })) {
23044
23049
  if (e.name === MARKER) continue;
23045
23050
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
23046
23051
  if (e.isDirectory()) out.push(...filesUnder(join49(dir, e.name), rel));
@@ -23054,7 +23059,7 @@ function skillStamp() {
23054
23059
  function bundledSkillNames() {
23055
23060
  const src = skillsSourceDir();
23056
23061
  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();
23062
+ return readdirSync35(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join49(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
23058
23063
  }
23059
23064
  function realPathOrSelf(p) {
23060
23065
  try {
@@ -23146,7 +23151,7 @@ function uninstallSkills() {
23146
23151
  perTarget.push({ id: t.id, label: t.label, removed });
23147
23152
  continue;
23148
23153
  }
23149
- for (const entry of readdirSync34(t.dir, { withFileTypes: true })) {
23154
+ for (const entry of readdirSync35(t.dir, { withFileTypes: true })) {
23150
23155
  if (!entry.isDirectory()) continue;
23151
23156
  const dir = join49(t.dir, entry.name);
23152
23157
  if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
@@ -23849,9 +23854,9 @@ function portableCommand(cmd) {
23849
23854
  }
23850
23855
  if (isExecutableFile(cmd))
23851
23856
  return { command: cmd, rewritten: false };
23852
- const base2 = basename26(cmd);
23853
- if (onPath(base2))
23854
- return { command: base2, rewritten: true };
23857
+ const base3 = basename26(cmd);
23858
+ if (onPath(base3))
23859
+ return { command: base3, rewritten: true };
23855
23860
  return { missing: cmd };
23856
23861
  }
23857
23862
  function entryFromSpec(spec) {
@@ -24917,17 +24922,17 @@ async function collectCode(opts) {
24917
24922
  return r;
24918
24923
  };
24919
24924
  log6(`indexing ${ws} + analyses \u2026`);
24920
- const base2 = await runMcp(bin, ws, [
24925
+ const base3 = await runMcp(bin, ws, [
24921
24926
  call(1, "index_workspace", { path: ws }),
24922
24927
  call(2, "status"),
24923
24928
  call(3, "get_tree"),
24924
24929
  ...ANALYSES.map((a, i) => call(10 + i, "analyze", { analysis: a }))
24925
24930
  ]);
24926
- const st = parseJson(base2, 2, {});
24927
- const tree = parseJson(base2, 3, []);
24931
+ const st = parseJson(base3, 2, {});
24932
+ const tree = parseJson(base3, 3, []);
24928
24933
  const A = {};
24929
24934
  ANALYSES.forEach((a, i) => {
24930
- A[a] = parseJson(base2, 10 + i, {});
24935
+ A[a] = parseJson(base3, 10 + i, {});
24931
24936
  });
24932
24937
  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
24938
  if (tree.length === 0) {
@@ -25827,14 +25832,14 @@ function writeLedger(l) {
25827
25832
  } catch {
25828
25833
  }
25829
25834
  }
25830
- async function pushProjectTaskStatuses(base2, authHeaders, opts = {}) {
25835
+ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
25831
25836
  const out = { files: 0, pushedTasks: 0 };
25832
25837
  const ledger = readLedger();
25833
25838
  let ledgerDirty = false;
25834
25839
  for (const fk of FILE_KINDS) {
25835
25840
  let projects;
25836
25841
  try {
25837
- const res = await fetchWithTimeout(`${base2}${fk.trackedPath}`, { headers: authHeaders });
25842
+ const res = await fetchWithTimeout(`${base3}${fk.trackedPath}`, { headers: authHeaders });
25838
25843
  if (!res.ok) continue;
25839
25844
  projects = (await res.json()).projects || [];
25840
25845
  } catch {
@@ -25862,7 +25867,7 @@ async function pushProjectTaskStatuses(base2, authHeaders, opts = {}) {
25862
25867
  continue;
25863
25868
  }
25864
25869
  try {
25865
- const res = await fetchWithTimeout(`${base2}${fk.statusPath}`, {
25870
+ const res = await fetchWithTimeout(`${base3}${fk.statusPath}`, {
25866
25871
  method: "POST",
25867
25872
  headers: { "content-type": "application/json", ...authHeaders },
25868
25873
  body: JSON.stringify({ project, items: changed.map((t) => ({ id: t.id, status: t.intent })) })
@@ -25968,16 +25973,16 @@ ${block}`);
25968
25973
  return { status: "error", result: JSON.stringify({ error: e instanceof Error ? e.message : "failed" }) };
25969
25974
  }
25970
25975
  }
25971
- async function runPull(base2, opts) {
25976
+ async function runPull(base3, opts) {
25972
25977
  const { executePull: executePull2 } = await Promise.resolve().then(() => (init_toolkit_pull(), toolkit_pull_exports));
25973
25978
  const { hostname: hostname3 } = await import("node:os");
25974
- const creds = loadAllCredentials().find((c) => c.serverUrl.replace(/\/+$/, "") === base2.replace(/\/+$/, ""));
25979
+ const creds = loadAllCredentials().find((c) => c.serverUrl.replace(/\/+$/, "") === base3.replace(/\/+$/, ""));
25975
25980
  const headers = creds?.token ? { authorization: `Bearer ${creds.token}` } : {};
25976
25981
  const wanted = opts.types?.length ? opts.types : ["mcp", "skill", "command", "agent", "instructions"];
25977
25982
  const rows = [];
25978
25983
  for (const type of wanted) {
25979
25984
  try {
25980
- const res = await fetchWithTimeout(`${base2}/api/toolkit/browse/${type}?limit=1000`, { headers });
25985
+ const res = await fetchWithTimeout(`${base3}/api/toolkit/browse/${type}?limit=1000`, { headers });
25981
25986
  if (!res.ok) continue;
25982
25987
  const body = await res.json();
25983
25988
  for (const it of body.items || []) rows.push(it);
@@ -26046,20 +26051,20 @@ async function runIntent(intent, ctx) {
26046
26051
  async function drainSyncIntents(opts = {}) {
26047
26052
  const out = { processed: 0, done: 0, errored: 0 };
26048
26053
  for (const cred of loadAllCredentials()) {
26049
- const base2 = cred.serverUrl.replace(/\/+$/, "");
26054
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
26050
26055
  const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
26051
26056
  let pending;
26052
26057
  try {
26053
- const res = await fetchWithTimeout(`${base2}/api/sync-intents/pending`, { headers: authHeaders });
26058
+ const res = await fetchWithTimeout(`${base3}/api/sync-intents/pending`, { headers: authHeaders });
26054
26059
  if (!res.ok) continue;
26055
26060
  const data = await res.json();
26056
26061
  pending = data.intents || [];
26057
26062
  if (data.cli && data.cli.version) {
26058
26063
  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);
26064
+ const plan = planAutoUpdate2(base3, { cli: data.cli }, ownVersion2, process.env.CHAT_RECALL_AUTO_UPDATE);
26060
26065
  sweepStaleStaging2();
26061
26066
  if (plan.update) {
26062
- void runAutoUpdate2(base2, authHeaders, ownVersion2).catch(() => {
26067
+ void runAutoUpdate2(base3, authHeaders, ownVersion2).catch(() => {
26063
26068
  });
26064
26069
  }
26065
26070
  }
@@ -26067,7 +26072,7 @@ async function drainSyncIntents(opts = {}) {
26067
26072
  continue;
26068
26073
  }
26069
26074
  for (const intent of pending) {
26070
- const { status, result } = await runIntent(intent, { base: base2 });
26075
+ const { status, result } = await runIntent(intent, { base: base3 });
26071
26076
  out.processed++;
26072
26077
  if (status === "done") out.done++;
26073
26078
  else out.errored++;
@@ -26076,7 +26081,7 @@ async function drainSyncIntents(opts = {}) {
26076
26081
  console.error(`[sync-intent] ${label}: ${status}`);
26077
26082
  }
26078
26083
  try {
26079
- await fetchWithTimeout(`${base2}/api/sync-intents/${intent.id}/ack`, {
26084
+ await fetchWithTimeout(`${base3}/api/sync-intents/${intent.id}/ack`, {
26080
26085
  method: "POST",
26081
26086
  headers: { "content-type": "application/json", ...authHeaders },
26082
26087
  body: JSON.stringify({ status, result })
@@ -26085,7 +26090,7 @@ async function drainSyncIntents(opts = {}) {
26085
26090
  }
26086
26091
  }
26087
26092
  try {
26088
- await pushProjectTaskStatuses(base2, authHeaders, { verbose: opts.verbose });
26093
+ await pushProjectTaskStatuses(base3, authHeaders, { verbose: opts.verbose });
26089
26094
  } catch {
26090
26095
  }
26091
26096
  }
@@ -26111,7 +26116,7 @@ var init_intent_drain = __esm({
26111
26116
  ownVersion2 = JSON.parse(readFileSync46(join55(dir, "../package.json"), "utf-8")).version || ownVersion2;
26112
26117
  } catch {
26113
26118
  try {
26114
- if (true) ownVersion2 = "0.5.31";
26119
+ if (true) ownVersion2 = "0.5.32";
26115
26120
  } catch {
26116
26121
  }
26117
26122
  }
@@ -26533,7 +26538,7 @@ var init_vault_crypto = __esm({
26533
26538
  });
26534
26539
 
26535
26540
  // ../engine/dist/core/vault-source.js
26536
- import { existsSync as existsSync53, readdirSync as readdirSync35, statSync as statSync46 } from "fs";
26541
+ import { existsSync as existsSync53, readdirSync as readdirSync36, statSync as statSync46 } from "fs";
26537
26542
  import { extname, join as join57 } from "path";
26538
26543
  function* walkVaultSources() {
26539
26544
  const t = loadSettings().team.vault;
@@ -26565,18 +26570,18 @@ function* walkClaude() {
26565
26570
  return;
26566
26571
  let projectDirs = [];
26567
26572
  try {
26568
- projectDirs = readdirSync35(root);
26573
+ projectDirs = readdirSync36(root);
26569
26574
  } catch {
26570
26575
  return;
26571
26576
  }
26572
26577
  for (const proj of projectDirs) {
26573
26578
  if (proj.startsWith("."))
26574
26579
  continue;
26575
- const projectPath = decodeProjectDirName(proj);
26580
+ const projectPath = resolveProjectDirName(proj);
26576
26581
  const projDir = join57(root, proj);
26577
26582
  let entries = [];
26578
26583
  try {
26579
- entries = readdirSync35(projDir);
26584
+ entries = readdirSync36(projDir);
26580
26585
  } catch {
26581
26586
  continue;
26582
26587
  }
@@ -26598,7 +26603,7 @@ function* walkGemini() {
26598
26603
  return;
26599
26604
  let hashDirs = [];
26600
26605
  try {
26601
- hashDirs = readdirSync35(tmp);
26606
+ hashDirs = readdirSync36(tmp);
26602
26607
  } catch {
26603
26608
  return;
26604
26609
  }
@@ -26608,7 +26613,7 @@ function* walkGemini() {
26608
26613
  continue;
26609
26614
  let entries = [];
26610
26615
  try {
26611
- entries = readdirSync35(chatsDir);
26616
+ entries = readdirSync36(chatsDir);
26612
26617
  } catch {
26613
26618
  continue;
26614
26619
  }
@@ -26620,8 +26625,8 @@ function* walkGemini() {
26620
26625
  const s = safeStat(path);
26621
26626
  if (!s)
26622
26627
  continue;
26623
- const base2 = f.replace(/^session-/, "").replace(/\.(jsonl?)$/, "");
26624
- yield { sessionId: `gemini_${base2}`, tool: "gemini", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
26628
+ const base3 = f.replace(/^session-/, "").replace(/\.(jsonl?)$/, "");
26629
+ yield { sessionId: `gemini_${base3}`, tool: "gemini", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
26625
26630
  }
26626
26631
  }
26627
26632
  }
@@ -26654,7 +26659,7 @@ function* walkCodex() {
26654
26659
  const dir = stack.pop();
26655
26660
  let entries = [];
26656
26661
  try {
26657
- entries = readdirSync35(dir, { withFileTypes: true });
26662
+ entries = readdirSync36(dir, { withFileTypes: true });
26658
26663
  } catch {
26659
26664
  continue;
26660
26665
  }
@@ -26669,8 +26674,8 @@ function* walkCodex() {
26669
26674
  const s = safeStat(path);
26670
26675
  if (!s)
26671
26676
  continue;
26672
- const base2 = e.name.replace(/\.jsonl$/, "");
26673
- yield { sessionId: `codex_${base2}`, tool: "codex", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
26677
+ const base3 = e.name.replace(/\.jsonl$/, "");
26678
+ yield { sessionId: `codex_${base3}`, tool: "codex", path, mtimeMs: s.mtimeMs, sizeBytes: s.size };
26674
26679
  }
26675
26680
  }
26676
26681
  }
@@ -26680,7 +26685,7 @@ function* walkAgy() {
26680
26685
  return;
26681
26686
  let sessionDirs = [];
26682
26687
  try {
26683
- sessionDirs = readdirSync35(brainDir);
26688
+ sessionDirs = readdirSync36(brainDir);
26684
26689
  } catch {
26685
26690
  return;
26686
26691
  }
@@ -26813,11 +26818,11 @@ function vaultEnable(passphrase, opts = {}) {
26813
26818
  }
26814
26819
  async function vaultEnableWithRemote(passphrase, remote, opts = {}) {
26815
26820
  const doFetch = opts.fetchImpl ?? fetch;
26816
- const base2 = remote.baseUrl.replace(/\/+$/, "");
26821
+ const base3 = remote.baseUrl.replace(/\/+$/, "");
26817
26822
  const headers = { authorization: `Bearer ${remote.token}`, "content-type": "application/json" };
26818
26823
  let published = { saltHex: null, keyId: null };
26819
26824
  try {
26820
- const r = await doFetch(`${base2}/api/vault/salt`, { headers });
26825
+ const r = await doFetch(`${base3}/api/vault/salt`, { headers });
26821
26826
  if (r.ok)
26822
26827
  published = await r.json();
26823
26828
  } catch {
@@ -26831,7 +26836,7 @@ async function vaultEnableWithRemote(passphrase, remote, opts = {}) {
26831
26836
  }
26832
26837
  const result = vaultEnable(passphrase);
26833
26838
  try {
26834
- const r = await doFetch(`${base2}/api/vault/salt`, {
26839
+ const r = await doFetch(`${base3}/api/vault/salt`, {
26835
26840
  method: "POST",
26836
26841
  headers,
26837
26842
  body: JSON.stringify({ saltHex: result.saltHex, keyId: result.keyId })
@@ -26924,8 +26929,8 @@ async function vaultSync(passphrase) {
26924
26929
  return report;
26925
26930
  }
26926
26931
  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`);
26932
+ const base3 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
26933
+ return join58(IMPORTED_ROOT(), tool, `${base3}.jsonl`);
26929
26934
  }
26930
26935
  async function vaultRestore(passphrase, opts = {}) {
26931
26936
  const ctx = context2();
@@ -27468,8 +27473,8 @@ async function deviceLogin(opts, onPrompt) {
27468
27473
  throw new Error("device-auth timed out \u2014 the code expired before approval.");
27469
27474
  }
27470
27475
  async function betterAuthDeviceLogin(serverUrl, onPrompt) {
27471
- const base2 = serverUrl.replace(/\/+$/, "");
27472
- const startRes = await fetch(`${base2}/api/auth/device/code`, {
27476
+ const base3 = serverUrl.replace(/\/+$/, "");
27477
+ const startRes = await fetch(`${base3}/api/auth/device/code`, {
27473
27478
  method: "POST",
27474
27479
  headers: { "content-type": "application/json" },
27475
27480
  body: JSON.stringify({ client_id: BETTER_AUTH_CLI_CLIENT_ID })
@@ -27477,7 +27482,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
27477
27482
  if (!startRes.ok) {
27478
27483
  throw new Error(`device-auth init failed: HTTP ${startRes.status} ${await startRes.text().catch(() => "")}`);
27479
27484
  }
27480
- const start = await readDeviceCode(startRes, base2);
27485
+ const start = await readDeviceCode(startRes, base3);
27481
27486
  onPrompt({
27482
27487
  url: start.verification_uri_complete || start.verification_uri,
27483
27488
  userCode: start.user_code,
@@ -27487,7 +27492,7 @@ async function betterAuthDeviceLogin(serverUrl, onPrompt) {
27487
27492
  let interval = (start.interval ?? 5) * 1e3;
27488
27493
  while (Date.now() < deadline) {
27489
27494
  await sleep(interval);
27490
- const res = await fetch(`${base2}/api/auth/device/token`, {
27495
+ const res = await fetch(`${base3}/api/auth/device/token`, {
27491
27496
  method: "POST",
27492
27497
  headers: { "content-type": "application/json" },
27493
27498
  body: JSON.stringify({
@@ -27572,6 +27577,140 @@ var init_install_report = __esm({
27572
27577
  }
27573
27578
  });
27574
27579
 
27580
+ // src/bulk-delete.ts
27581
+ var bulk_delete_exports = {};
27582
+ __export(bulk_delete_exports, {
27583
+ BATCH: () => BATCH,
27584
+ bulkDelete: () => bulkDelete,
27585
+ listTombstones: () => listTombstones,
27586
+ needsConfirmation: () => needsConfirmation,
27587
+ restoreSessions: () => restoreSessions,
27588
+ selectSessions: () => selectSessions
27589
+ });
27590
+ async function selectSessions(target, opts, fetchImpl = fetch) {
27591
+ const found = [];
27592
+ const want = opts.match?.trim();
27593
+ for (let offset = 0; ; offset += PAGE) {
27594
+ const qs = new URLSearchParams({ limit: String(PAGE), offset: String(offset) });
27595
+ if (opts.project) qs.set("project", opts.project);
27596
+ if (opts.tool) qs.set("tool", opts.tool);
27597
+ const res = await fetchImpl(`${base2(target.serverUrl)}/api/conversations/recent?${qs}`, {
27598
+ headers: { authorization: `Bearer ${target.token}` }
27599
+ });
27600
+ if (!res.ok) throw new Error(`list failed: HTTP ${res.status}`);
27601
+ const body = await res.json();
27602
+ const page = body.sessions ?? [];
27603
+ if (page.length === 0) break;
27604
+ for (const row of page) {
27605
+ if (!row.sessionId) continue;
27606
+ if (want !== void 0 && (row.firstPrompt ?? "").trim() !== want) continue;
27607
+ found.push(row);
27608
+ if (opts.limit && found.length >= opts.limit) return found;
27609
+ }
27610
+ if (page.length < PAGE) break;
27611
+ }
27612
+ return found;
27613
+ }
27614
+ async function bulkDelete(ids, opts = {}) {
27615
+ const unique = [...new Set(ids.filter(Boolean))];
27616
+ const targets = opts.targets ?? loadAllCredentials();
27617
+ const fetchImpl = opts.fetchImpl ?? fetch;
27618
+ const out = { requested: unique.length, perTarget: {}, deleted: 0 };
27619
+ if (unique.length === 0 || targets.length === 0) return out;
27620
+ const succeeded = /* @__PURE__ */ new Set();
27621
+ for (const t of targets) {
27622
+ let deleted = 0;
27623
+ try {
27624
+ for (let i = 0; i < unique.length; i += BATCH) {
27625
+ const slice = unique.slice(i, i + BATCH);
27626
+ const res = await fetchImpl(`${base2(t.serverUrl)}/api/sync`, {
27627
+ method: "POST",
27628
+ headers: { "content-type": "application/json", authorization: `Bearer ${t.token}` },
27629
+ body: JSON.stringify({
27630
+ tombstones: slice.map((session_id) => ({ session_id, deleted_at: Date.now() }))
27631
+ })
27632
+ });
27633
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
27634
+ deleted += slice.length;
27635
+ for (const id of slice) succeeded.add(id);
27636
+ opts.onProgress?.(deleted, unique.length);
27637
+ }
27638
+ out.perTarget[t.serverUrl] = { deleted };
27639
+ } catch (e) {
27640
+ out.perTarget[t.serverUrl] = { deleted, error: e instanceof Error ? e.message : "failed" };
27641
+ }
27642
+ }
27643
+ out.deleted = succeeded.size;
27644
+ for (const t of targets) {
27645
+ if (!out.perTarget[t.serverUrl] || out.perTarget[t.serverUrl].error) continue;
27646
+ try {
27647
+ const data = getLedgerData(t.serverUrl);
27648
+ let touched = false;
27649
+ for (const id of succeeded) {
27650
+ if (id in data) {
27651
+ delete data[id];
27652
+ touched = true;
27653
+ }
27654
+ }
27655
+ if (touched) persistLedgerData(t.serverUrl, data);
27656
+ } catch {
27657
+ }
27658
+ }
27659
+ return out;
27660
+ }
27661
+ async function listTombstones(target, limit = 500, fetchImpl = fetch) {
27662
+ const res = await fetchImpl(`${base2(target.serverUrl)}/api/data/tombstones?limit=${limit}`, {
27663
+ headers: { authorization: `Bearer ${target.token}` }
27664
+ });
27665
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
27666
+ return await res.json();
27667
+ }
27668
+ async function restoreSessions(ids, opts = {}) {
27669
+ const unique = [...new Set(ids.filter(Boolean))];
27670
+ const targets = opts.targets ?? loadAllCredentials();
27671
+ const fetchImpl = opts.fetchImpl ?? fetch;
27672
+ const out = { requested: unique.length, restored: 0, perTarget: {} };
27673
+ if (unique.length === 0 || targets.length === 0) return out;
27674
+ let best = 0;
27675
+ for (const t of targets) {
27676
+ try {
27677
+ let restored = 0, notDeleted = 0;
27678
+ for (let i = 0; i < unique.length; i += BATCH) {
27679
+ const res = await fetchImpl(`${base2(t.serverUrl)}/api/data/restore`, {
27680
+ method: "POST",
27681
+ headers: { "content-type": "application/json", authorization: `Bearer ${t.token}` },
27682
+ body: JSON.stringify({ session_ids: unique.slice(i, i + BATCH) })
27683
+ });
27684
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
27685
+ const body = await res.json();
27686
+ restored += body.restored ?? 0;
27687
+ notDeleted += (body.notDeleted ?? []).length;
27688
+ }
27689
+ out.perTarget[t.serverUrl] = { restored, notDeleted };
27690
+ best = Math.max(best, restored);
27691
+ } catch (e) {
27692
+ out.perTarget[t.serverUrl] = { restored: 0, notDeleted: 0, error: e instanceof Error ? e.message : "failed" };
27693
+ }
27694
+ }
27695
+ out.restored = best;
27696
+ return out;
27697
+ }
27698
+ function needsConfirmation(opts) {
27699
+ const enumeratedByCaller = opts.argvIds > 0 && !opts.project && !opts.stdin;
27700
+ return !enumeratedByCaller;
27701
+ }
27702
+ var PAGE, BATCH, base2;
27703
+ var init_bulk_delete = __esm({
27704
+ "src/bulk-delete.ts"() {
27705
+ "use strict";
27706
+ init_sync_client();
27707
+ init_sync_ledger();
27708
+ PAGE = 200;
27709
+ BATCH = 250;
27710
+ base2 = (url) => url.replace(/\/+$/, "");
27711
+ }
27712
+ });
27713
+
27575
27714
  // ../engine/dist/core/resume-command.js
27576
27715
  function toolOfSessionId(sessionId) {
27577
27716
  if (sessionId.startsWith("codex_"))
@@ -27782,13 +27921,13 @@ function requireTarget() {
27782
27921
  }
27783
27922
  return t;
27784
27923
  }
27785
- async function readJsonBody(res, base2, what) {
27924
+ async function readJsonBody(res, base3, what) {
27786
27925
  const text = await res.text().catch(() => "");
27787
27926
  try {
27788
27927
  return JSON.parse(text);
27789
27928
  } catch {
27790
27929
  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`
27930
+ `${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
27931
  );
27793
27932
  }
27794
27933
  }
@@ -28573,9 +28712,9 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
28573
28712
  return;
28574
28713
  }
28575
28714
  const target = firstTarget();
28576
- const base2 = target?.base || (process.env.CHAT_RECALL_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
28715
+ const base3 = target?.base || (process.env.CHAT_RECALL_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
28577
28716
  try {
28578
- const res = await fetch(`${base2}/api/contact/feedback`, {
28717
+ const res = await fetch(`${base3}/api/contact/feedback`, {
28579
28718
  method: "POST",
28580
28719
  headers: {
28581
28720
  "content-type": "application/json",
@@ -28602,7 +28741,7 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
28602
28741
  console.log(chalk2.dim(" Add --email <address> next time if you want a reply."));
28603
28742
  }
28604
28743
  } catch (err) {
28605
- console.error(chalk2.red(`Could not reach ${base2}.`));
28744
+ console.error(chalk2.red(`Could not reach ${base3}.`));
28606
28745
  console.error(chalk2.dim(" Your message, so it is not lost:"));
28607
28746
  console.error(chalk2.dim(` ${message}`));
28608
28747
  process.exitCode = 1;
@@ -29170,13 +29309,13 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
29170
29309
  }
29171
29310
  const { fetchWithTimeout: fetchWithTimeout2 } = await Promise.resolve().then(() => (init_http(), http_exports));
29172
29311
  for (const t of targets) {
29173
- const base2 = t.serverUrl.replace(/\/+$/, "");
29312
+ const base3 = t.serverUrl.replace(/\/+$/, "");
29174
29313
  const headers = {};
29175
29314
  if (t.token) headers.authorization = `Bearer ${t.token}`;
29176
29315
  let deleted = 0, missing = 0, failed = 0;
29177
29316
  for (const id of ids) {
29178
29317
  try {
29179
- const res = await fetchWithTimeout2(`${base2}/api/conversations/${encodeURIComponent(id)}`, { method: "DELETE", headers }, 3e4);
29318
+ const res = await fetchWithTimeout2(`${base3}/api/conversations/${encodeURIComponent(id)}`, { method: "DELETE", headers }, 3e4);
29180
29319
  if (res.ok) deleted++;
29181
29320
  else if (res.status === 404) missing++;
29182
29321
  else failed++;
@@ -29184,7 +29323,7 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
29184
29323
  failed++;
29185
29324
  }
29186
29325
  }
29187
- const line = ` ${base2}: deleted ${deleted}` + (missing ? `, ${missing} were not there` : "") + (failed ? chalk2.red(`, ${failed} FAILED`) : "");
29326
+ const line = ` ${base3}: deleted ${deleted}` + (missing ? `, ${missing} were not there` : "") + (failed ? chalk2.red(`, ${failed} FAILED`) : "");
29188
29327
  console.log(failed ? chalk2.yellow(line) : chalk2.green(line));
29189
29328
  if (failed) process.exitCode = 1;
29190
29329
  }
@@ -30032,10 +30171,10 @@ program.command("service <action>").description("Manage the per-user background
30032
30171
  });
30033
30172
  async function runLogin(serverUrl, opts) {
30034
30173
  const { saveCredentials: saveCredentials2, loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30035
- const base2 = serverUrl.replace(/\/+$/, "");
30174
+ const base3 = serverUrl.replace(/\/+$/, "");
30036
30175
  const verifyToken = async (token) => {
30037
30176
  try {
30038
- const res = await fetch(`${base2}/api/status`, {
30177
+ const res = await fetch(`${base3}/api/status`, {
30039
30178
  headers: token ? { authorization: `Bearer ${token}` } : {},
30040
30179
  signal: AbortSignal.timeout(1e4)
30041
30180
  });
@@ -30051,36 +30190,36 @@ async function runLogin(serverUrl, opts) {
30051
30190
  return failLogin(opts, 'the token contains non-ASCII characters \u2014 this looks like a masked/truncated copy (e.g. "ct_12345678\u2026")');
30052
30191
  }
30053
30192
  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));
30193
+ const who = await identifyServer(base3, { timeoutMs: 1e4 });
30194
+ if (!identified(who)) return failLogin(opts, identifyAdvice(who, base3));
30056
30195
  const check = await verifyToken(token);
30057
30196
  if (!check.ok) {
30058
30197
  if (check.status === 401 || check.status === 403) {
30059
30198
  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})`);
30199
+ return failLogin(opts, `${base3} rejected the token (HTTP ${check.status})`);
30061
30200
  }
30062
30201
  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.`);
30202
+ 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
30203
  }
30065
30204
  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.`);
30205
+ 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
30206
  }
30068
- return failLogin(opts, `could not verify the token against ${base2}: ${check.error || `HTTP ${check.status}`}`);
30207
+ return failLogin(opts, `could not verify the token against ${base3}: ${check.error || `HTTP ${check.status}`}`);
30069
30208
  }
30070
30209
  saveCredentials2({ serverUrl, token });
30071
30210
  console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` server: ${serverUrl} (token verified)`));
30072
30211
  return;
30073
30212
  }
30074
- const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") === base2);
30213
+ const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") === base3);
30075
30214
  if (existing && (await verifyToken(existing.token)).ok) {
30076
30215
  console.log(chalk2.green("\u2713 Already connected.") + chalk2.dim(` server: ${serverUrl} (existing token verified)`));
30077
30216
  return;
30078
30217
  }
30079
30218
  try {
30080
30219
  const { probeServer: probeServer2, probeOk: probeOk2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
30081
- const identified = await probeServer2(base2, { timeoutMs: 8e3 });
30220
+ const identified = await probeServer2(base3, { timeoutMs: 8e3 });
30082
30221
  if (probeOk2(identified)) {
30083
- const probe = await fetch(`${base2}/api/status`, { signal: AbortSignal.timeout(8e3) });
30222
+ const probe = await fetch(`${base3}/api/status`, { signal: AbortSignal.timeout(8e3) });
30084
30223
  if (probe.status === 200) {
30085
30224
  saveCredentials2({ serverUrl, token: "" });
30086
30225
  console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` ${serverUrl} (local server \u2014 no auth, no token needed)`));
@@ -30101,16 +30240,16 @@ async function runLogin(serverUrl, opts) {
30101
30240
  if (!issuer) {
30102
30241
  const { probeServer: probeServer2, probeOk: probeOk2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
30103
30242
  for (let attempt = 1; attempt <= 3 && !authProvider; attempt++) {
30104
- const probe = await probeServer2(base2, { timeoutMs: 15e3 });
30243
+ const probe = await probeServer2(base3, { timeoutMs: 15e3 });
30105
30244
  if (probeOk2(probe)) {
30106
30245
  const caps = probe.raw;
30107
30246
  authProvider = caps?.authProvider;
30108
30247
  if (caps?.oidcIssuer) issuer = caps.oidcIssuer;
30109
30248
  capsError = null;
30110
30249
  } else if (probe.kind === "moved" || probe.kind === "not_api") {
30111
- return failLogin(opts, probeAdvice2(probe, base2));
30250
+ return failLogin(opts, probeAdvice2(probe, base3));
30112
30251
  } else {
30113
- capsError = probeAdvice2(probe, base2);
30252
+ capsError = probeAdvice2(probe, base3);
30114
30253
  }
30115
30254
  if (!authProvider && attempt < 3) await new Promise((r) => setTimeout(r, attempt * 1e3));
30116
30255
  }
@@ -30119,7 +30258,7 @@ async function runLogin(serverUrl, opts) {
30119
30258
  authProvider = "better-auth";
30120
30259
  if (capsError) {
30121
30260
  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, {
30261
+ void Promise.resolve().then(() => (init_install_report(), install_report_exports)).then(({ reportInstallFailure: reportInstallFailure2 }) => reportInstallFailure2(base3, {
30123
30262
  step: "cli_login_probe",
30124
30263
  reason: capsError?.slice(0, 40) ?? null,
30125
30264
  message: capsError
@@ -30141,20 +30280,20 @@ async function runLogin(serverUrl, opts) {
30141
30280
  console.log(chalk2.dim(` If the page asks for a code: ${chalk2.bold(p.userCode)}`));
30142
30281
  console.log(chalk2.dim("Then come back here \u2014 this finishes on its own. Waiting\u2026"));
30143
30282
  };
30144
- const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base2, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
30283
+ const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base3, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
30145
30284
  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");
30285
+ const meRes = await fetch(`${base3}/api/me`, { headers: authHdr });
30286
+ if (!meRes.ok) throw new Error(`${base3}/api/me: HTTP ${meRes.status} ${await meRes.text().catch(() => "")}`);
30287
+ const me = await readJsonBody(meRes, base3, "GET /api/me");
30149
30288
  const teams = me.teams || [];
30150
30289
  let slug = opts.team;
30151
30290
  if (!slug) {
30152
30291
  if (teams.length === 1) slug = teams[0].team_slug;
30153
30292
  else if (teams.length === 0) {
30154
30293
  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 }) });
30294
+ const created = await fetch(`${base3}/api/teams`, { method: "POST", headers: authHdr, body: JSON.stringify({ name }) });
30156
30295
  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;
30296
+ slug = (await readJsonBody(created, base3, "POST /api/teams")).slug;
30158
30297
  console.log(chalk2.dim(`Created workspace "${name}" (${slug}) \u2014 you're its owner.`));
30159
30298
  } else {
30160
30299
  teams.forEach((t) => console.error(` ${t.team_slug} ${chalk2.dim(t.name)}`));
@@ -30162,13 +30301,13 @@ async function runLogin(serverUrl, opts) {
30162
30301
  }
30163
30302
  }
30164
30303
  const deviceId = opts.deviceId || (await import("node:os")).hostname();
30165
- const mint = await fetch(`${base2}/api/teams/${encodeURIComponent(slug)}/tokens`, {
30304
+ const mint = await fetch(`${base3}/api/teams/${encodeURIComponent(slug)}/tokens`, {
30166
30305
  method: "POST",
30167
30306
  headers: authHdr,
30168
30307
  body: JSON.stringify({ device_id: deviceId })
30169
30308
  });
30170
30309
  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");
30310
+ const { token } = await readJsonBody(mint, base3, "POST /api/teams/:slug/tokens");
30172
30311
  saveCredentials2({ serverUrl, token });
30173
30312
  console.log(chalk2.green(`\u2713 Logged in to ${chalk2.bold(slug)}`) + chalk2.dim(` (device: ${deviceId}) server: ${serverUrl}`));
30174
30313
  if (!opts.fromInit) {
@@ -30420,30 +30559,142 @@ program.command("logout <server-url>").description("Remove one sync target (othe
30420
30559
  process.exit(1);
30421
30560
  }
30422
30561
  });
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) => {
30562
+ 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
30563
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30564
+ const { selectSessions: selectSessions2, bulkDelete: bulkDelete2, needsConfirmation: needsConfirmation2 } = await Promise.resolve().then(() => (init_bulk_delete(), bulk_delete_exports));
30425
30565
  const targets = loadAllCredentials2();
30426
30566
  if (targets.length === 0) {
30427
30567
  console.error(chalk2.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30428
30568
  process.exit(1);
30429
30569
  }
30430
- let ok = 0;
30431
- for (const t of targets) {
30570
+ if ((opts.match || opts.tool) && !opts.project) {
30571
+ console.error(chalk2.red("--match and --tool select WITHIN a project; pass --project too."));
30572
+ process.exit(1);
30573
+ }
30574
+ let ids = [...sessionIds];
30575
+ let preview = [];
30576
+ if (opts.stdin) {
30577
+ const stdin = await new Promise((resolve3) => {
30578
+ let buf = "";
30579
+ process.stdin.setEncoding("utf-8");
30580
+ process.stdin.on("data", (d) => {
30581
+ buf += d;
30582
+ });
30583
+ process.stdin.on("end", () => resolve3(buf));
30584
+ });
30585
+ ids.push(...stdin.split("\n").map((l) => l.trim()).filter(Boolean));
30586
+ }
30587
+ if (opts.project) {
30432
30588
  try {
30433
- const res = await fetch(`${t.serverUrl.replace(/\/+$/, "")}/api/conversations/${encodeURIComponent(sessionId)}`, {
30434
- method: "DELETE",
30435
- headers: { authorization: `Bearer ${t.token}` }
30589
+ preview = await selectSessions2(targets[0], { project: opts.project, match: opts.match, tool: opts.tool });
30590
+ } catch (e) {
30591
+ console.error(chalk2.red(`Could not list sessions: ${e instanceof Error ? e.message : "failed"}`));
30592
+ process.exit(1);
30593
+ }
30594
+ ids.push(...preview.map((r) => r.sessionId));
30595
+ }
30596
+ ids = [...new Set(ids.filter(Boolean))];
30597
+ if (ids.length === 0) {
30598
+ console.log(chalk2.yellow("Nothing selected \u2014 no session ids given and no selector matched."));
30599
+ return;
30600
+ }
30601
+ const shown = preview.length > 0 ? preview.slice(0, 10) : ids.slice(0, 10).map((id) => ({ sessionId: id, firstPrompt: void 0 }));
30602
+ for (const r of shown) {
30603
+ const label = r.firstPrompt ? chalk2.dim(` \u2014 ${r.firstPrompt.replace(/\s+/g, " ").slice(0, 60)}`) : "";
30604
+ console.log(` ${r.sessionId}${label}`);
30605
+ }
30606
+ if (ids.length > shown.length) console.log(chalk2.dim(` \u2026 and ${ids.length - shown.length} more`));
30607
+ if (opts.dryRun) {
30608
+ console.log(chalk2.cyan(`
30609
+ Dry run \u2014 ${ids.length} session(s) would be deleted on ${targets.length} server(s). Nothing was changed.`));
30610
+ return;
30611
+ }
30612
+ if (!opts.yes && needsConfirmation2({ argvIds: sessionIds.length, project: opts.project, stdin: opts.stdin })) {
30613
+ if (!process.stdin.isTTY) {
30614
+ console.error(chalk2.red(`
30615
+ Refusing to delete ${ids.length} session(s) without confirmation \u2014 pass --yes (or -n to preview).`));
30616
+ process.exit(1);
30617
+ }
30618
+ const readline = await import("node:readline/promises");
30619
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
30620
+ const answer = await rl.question(chalk2.yellow(`
30621
+ Permanently delete ${ids.length} session(s) on ${targets.length} server(s)? This cannot be undone. [y/N] `));
30622
+ rl.close();
30623
+ if (answer.trim().toLowerCase() !== "y") {
30624
+ console.log("Aborted \u2014 nothing was deleted.");
30625
+ return;
30626
+ }
30627
+ }
30628
+ const res = await bulkDelete2(ids, { targets });
30629
+ for (const [server, r] of Object.entries(res.perTarget)) {
30630
+ if (r.error) console.error(chalk2.red(`\u2717 ${server}: ${r.deleted}/${ids.length} deleted, then failed \u2014 ${r.error}`));
30631
+ else console.log(chalk2.green(`\u2713 ${server}: ${r.deleted} deleted`));
30632
+ }
30633
+ if (res.deleted === 0) {
30634
+ console.error(chalk2.red("Nothing was deleted."));
30635
+ process.exit(1);
30636
+ }
30637
+ console.log(chalk2.dim(`Tombstoned \u2014 re-sync cannot resurrect them. Local transcript files were not touched.`));
30638
+ if (res.deleted < ids.length) {
30639
+ console.error(chalk2.yellow(`Note: ${ids.length - res.deleted} session(s) did not delete on any server.`));
30640
+ }
30641
+ });
30642
+ 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) => {
30643
+ const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30644
+ const { listTombstones: listTombstones2, restoreSessions: restoreSessions2 } = await Promise.resolve().then(() => (init_bulk_delete(), bulk_delete_exports));
30645
+ const targets = loadAllCredentials2();
30646
+ if (targets.length === 0) {
30647
+ console.error(chalk2.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30648
+ process.exit(1);
30649
+ }
30650
+ const ids = [...sessionIds];
30651
+ if (opts.stdin) {
30652
+ const buf = await new Promise((resolve3) => {
30653
+ let b = "";
30654
+ process.stdin.setEncoding("utf-8");
30655
+ process.stdin.on("data", (d) => {
30656
+ b += d;
30436
30657
  });
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}`));
30658
+ process.stdin.on("end", () => resolve3(b));
30659
+ });
30660
+ ids.push(...buf.split("\n").map((l) => l.trim()).filter(Boolean));
30661
+ }
30662
+ if (opts.list || ids.length === 0) {
30663
+ const limit = Math.max(1, parseInt(opts.limit ?? "") || 50);
30664
+ try {
30665
+ const { total, tombstones } = await listTombstones2(targets[0], limit);
30666
+ if (total === 0) {
30667
+ console.log(chalk2.dim("Nothing has been deleted."));
30668
+ return;
30669
+ }
30670
+ console.log(chalk2.bold(`${total} deleted session(s) \u2014 newest first:
30671
+ `));
30672
+ for (const t of tombstones) {
30673
+ console.log(` ${t.session_id} ${chalk2.dim(new Date(t.deleted_at).toISOString().replace("T", " ").slice(0, 19))}`);
30674
+ }
30675
+ if (total > tombstones.length) console.log(chalk2.dim(`
30676
+ \u2026 and ${total - tombstones.length} more (--limit to see them)`));
30677
+ console.log(chalk2.dim("\nRestore with: chat-recall restore <id...>"));
30441
30678
  } catch (e) {
30442
- console.error(chalk2.red(`\u2717 ${t.serverUrl}: ${e instanceof Error ? e.message : "failed"}`));
30679
+ console.error(chalk2.red(`Could not list deletions: ${e instanceof Error ? e.message : "failed"}`));
30680
+ process.exit(1);
30681
+ }
30682
+ return;
30683
+ }
30684
+ const res = await restoreSessions2(ids, { targets });
30685
+ for (const [server, r] of Object.entries(res.perTarget)) {
30686
+ if (r.error) console.error(chalk2.red(`\u2717 ${server}: ${r.error}`));
30687
+ else {
30688
+ console.log(chalk2.green(`\u2713 ${server}: ${r.restored} tombstone(s) lifted`));
30689
+ if (r.notDeleted > 0) console.log(chalk2.yellow(` ${r.notDeleted} id(s) were not deleted here \u2014 nothing to lift`));
30443
30690
  }
30444
30691
  }
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.`));
30692
+ if (res.restored === 0) {
30693
+ console.error(chalk2.red("No tombstones were lifted."));
30694
+ process.exit(1);
30695
+ }
30696
+ console.log(chalk2.dim("\nThe content is still gone from the server. Re-upload it from a machine that"));
30697
+ console.log(chalk2.dim("still holds the transcript: chat-recall index --force"));
30447
30698
  });
30448
30699
  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
30700
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
@@ -30533,16 +30784,16 @@ program.command("update").description("Update this CLI now from the server you s
30533
30784
  }
30534
30785
  let installFailed = false;
30535
30786
  for (const cred of targets) {
30536
- const base2 = cred.serverUrl.replace(/\/+$/, "");
30787
+ const base3 = cred.serverUrl.replace(/\/+$/, "");
30537
30788
  const headers = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
30538
- const r = await runAutoUpdate2(base2, headers, cliVersion3());
30789
+ const r = await runAutoUpdate2(base3, headers, cliVersion3());
30539
30790
  if (r.updated) {
30540
- console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${base2})`));
30791
+ console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${base3})`));
30541
30792
  continue;
30542
30793
  }
30543
30794
  const benign = /already current|no CLI release|disabled/.test(r.reason);
30544
30795
  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})`));
30796
+ console.log((benign ? chalk2.dim("\u2022 ") : chalk2.red("\u2717 ")) + r.reason + chalk2.dim(` (${base3})`));
30546
30797
  }
30547
30798
  await refreshUpdateCheck2(true);
30548
30799
  if (installFailed) {