chat-recall 0.5.29 → 0.5.31

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
@@ -7487,7 +7487,7 @@ async function archiveRawSession(store, sessionId) {
7487
7487
  return await store.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
7488
7488
  }
7489
7489
  function detectForkPredecessor(sessionPath) {
7490
- const { readFileSync: readFileSync50, readdirSync: readdirSync35 } = requireFs();
7490
+ const { readFileSync: readFileSync50, readdirSync: readdirSync36 } = requireFs();
7491
7491
  const { dirname: dirname28, join: join61, basename: basename30 } = requirePath();
7492
7492
  let text;
7493
7493
  try {
@@ -7517,7 +7517,7 @@ function detectForkPredecessor(sessionPath) {
7517
7517
  const self = basename30(sessionPath);
7518
7518
  let entries;
7519
7519
  try {
7520
- entries = readdirSync35(dir);
7520
+ entries = readdirSync36(dir);
7521
7521
  } catch {
7522
7522
  return null;
7523
7523
  }
@@ -10086,19 +10086,6 @@ var init_codex2 = __esm({
10086
10086
  // ../engine/dist/core/backends/agy.js
10087
10087
  import { existsSync as existsSync9, readdirSync as readdirSync7, readFileSync as readFileSync5, statSync as statSync8 } from "fs";
10088
10088
  import { basename as basename9, dirname as dirname10, join as join11 } from "path";
10089
- function getFallbackProjectPath(homeDir) {
10090
- try {
10091
- const settingsPath2 = join11(homeDir, "settings.json");
10092
- if (existsSync9(settingsPath2)) {
10093
- const json = JSON.parse(readFileSync5(settingsPath2, "utf-8"));
10094
- if (Array.isArray(json?.trustedWorkspaces) && json.trustedWorkspaces[0]) {
10095
- return json.trustedWorkspaces[0];
10096
- }
10097
- }
10098
- } catch {
10099
- }
10100
- return "";
10101
- }
10102
10089
  function agyTranscriptPath(logsDir) {
10103
10090
  const full = join11(logsDir, "transcript_full.jsonl");
10104
10091
  if (existsSync9(full))
@@ -10111,8 +10098,8 @@ function agyTranscriptPath(logsDir) {
10111
10098
  function resolveAgyProject(logsDir, homeDir) {
10112
10099
  const deriveFrom = agyTranscriptPath(logsDir);
10113
10100
  if (!deriveFrom)
10114
- return getFallbackProjectPath(homeDir);
10115
- return extractProjectPathFromTranscript(deriveFrom, homeDir) || getFallbackProjectPath(homeDir);
10101
+ return "";
10102
+ return extractProjectPathFromTranscript(deriveFrom, homeDir);
10116
10103
  }
10117
10104
  function extractProjectPathFromTranscript(path, homeDir) {
10118
10105
  let content;
@@ -19362,7 +19349,7 @@ var init_extractor_version = __esm({
19362
19349
  command: 1
19363
19350
  };
19364
19351
  TOOL_EXTRACTOR_BUMP = {
19365
- agy: 2,
19352
+ agy: 3,
19366
19353
  claude: 1,
19367
19354
  codex: 1
19368
19355
  };
@@ -19750,7 +19737,7 @@ var init_http = __esm({
19750
19737
  CLI_VERSION = JSON.parse(readFileSync33(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19751
19738
  } catch {
19752
19739
  try {
19753
- if (true) CLI_VERSION = "0.5.29";
19740
+ if (true) CLI_VERSION = "0.5.31";
19754
19741
  } catch {
19755
19742
  }
19756
19743
  }
@@ -20100,9 +20087,9 @@ function cooldownFor(failures) {
20100
20087
  function breakerState(serverUrl, now = Date.now()) {
20101
20088
  const s = state.get(key(serverUrl));
20102
20089
  if (!s || s.openUntil <= now) {
20103
- return { open: false, retryInMs: 0, failures: s?.failures ?? 0, lastError: s?.lastError ?? "" };
20090
+ return { open: false, retryInMs: 0, failures: s?.failures ?? 0, lastError: s?.lastError ?? "", tripped: false };
20104
20091
  }
20105
- return { open: true, retryInMs: s.openUntil - now, failures: s.failures, lastError: s.lastError };
20092
+ return { open: true, retryInMs: s.openUntil - now, failures: s.failures, lastError: s.lastError, tripped: false };
20106
20093
  }
20107
20094
  function noteTargetSuccess(serverUrl) {
20108
20095
  state.delete(key(serverUrl));
@@ -20114,11 +20101,11 @@ function noteTargetFailure(serverUrl, error, now = Date.now()) {
20114
20101
  const lastError = error.slice(0, 200);
20115
20102
  if (failures < TRIP_AFTER_FAILURES) {
20116
20103
  state.set(k, { failures, openUntil: 0, lastError });
20117
- return { open: false, retryInMs: 0, failures, lastError };
20104
+ return { open: false, retryInMs: 0, failures, lastError, tripped: false };
20118
20105
  }
20119
20106
  const cooldown = cooldownFor(failures);
20120
20107
  state.set(k, { failures, openUntil: now + cooldown, lastError });
20121
- return { open: true, retryInMs: cooldown, failures, lastError };
20108
+ return { open: true, retryInMs: cooldown, failures, lastError, tripped: failures === TRIP_AFTER_FAILURES };
20122
20109
  }
20123
20110
  function breakerNotice(serverUrl, v) {
20124
20111
  if (!v.open) return null;
@@ -20245,6 +20232,14 @@ async function flush() {
20245
20232
  }
20246
20233
  }));
20247
20234
  }
20235
+ function stableMark(value) {
20236
+ try {
20237
+ const { createHash: createHash20 } = __require("node:crypto");
20238
+ return createHash20("sha256").update(value).digest("hex").slice(0, 12);
20239
+ } catch {
20240
+ return "";
20241
+ }
20242
+ }
20248
20243
  function hashHost() {
20249
20244
  try {
20250
20245
  const { createHash: createHash20 } = __require("node:crypto");
@@ -20262,7 +20257,7 @@ var init_telemetry = __esm({
20262
20257
  init_sync_client();
20263
20258
  cliVersion2 = () => {
20264
20259
  try {
20265
- return true ? "0.5.29" : "dev";
20260
+ return true ? "0.5.31" : "dev";
20266
20261
  } catch {
20267
20262
  return "dev";
20268
20263
  }
@@ -20599,12 +20594,13 @@ __export(auto_update_exports, {
20599
20594
  planAutoUpdate: () => planAutoUpdate,
20600
20595
  runAutoUpdate: () => runAutoUpdate,
20601
20596
  runningPrefix: () => runningPrefix,
20597
+ sweepStaleStaging: () => sweepStaleStaging,
20602
20598
  tarballUrl: () => tarballUrl
20603
20599
  });
20604
20600
  import { execSync as execSync2 } from "node:child_process";
20605
20601
  import { readFileSync as readFileSync36 } from "node:fs";
20606
20602
  import { fileURLToPath as fileURLToPath2 } from "node:url";
20607
- import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3 } from "node:fs";
20603
+ import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync32, statSync as statSync38 } from "node:fs";
20608
20604
  import { join as join43, dirname as dirname18, sep as sep2 } from "node:path";
20609
20605
  import { tmpdir as tmpdir2 } from "node:os";
20610
20606
  import { createHash as createHash12 } from "node:crypto";
@@ -20643,6 +20639,31 @@ function planAutoUpdate(base2, caps, ownVersion3, flag) {
20643
20639
  if (compareVersions(rel.version, ownVersion3) <= 0) return { update: false, reason: `already current (own ${ownVersion3} >= server ${rel.version})` };
20644
20640
  return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(base2), sha256: rel.sha256, from: ownVersion3, to: rel.version };
20645
20641
  }
20642
+ function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
20643
+ const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
20644
+ const COLD_MS = 60 * 60 * 1e3;
20645
+ let removed = 0;
20646
+ let entries;
20647
+ try {
20648
+ entries = readdirSync32(root);
20649
+ } catch {
20650
+ return 0;
20651
+ }
20652
+ for (const name of entries) {
20653
+ if (!MKDTEMP_SUFFIX.test(name)) continue;
20654
+ const dir = join43(root, name);
20655
+ try {
20656
+ const st = statSync38(dir);
20657
+ if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
20658
+ const inside = readdirSync32(dir);
20659
+ if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
20660
+ rmSync5(dir, { recursive: true, force: true });
20661
+ removed++;
20662
+ } catch {
20663
+ }
20664
+ }
20665
+ return removed;
20666
+ }
20646
20667
  async function executeAutoUpdate(plan, deps) {
20647
20668
  if (!plan.update || !plan.url || !plan.sha256) return { updated: false, reason: plan.reason };
20648
20669
  let buf;
@@ -20654,13 +20675,21 @@ async function executeAutoUpdate(plan, deps) {
20654
20675
  const got = createHash12("sha256").update(buf).digest("hex");
20655
20676
  if (got !== plan.sha256) return { updated: false, reason: `checksum mismatch (expected ${plan.sha256.slice(0, 12)}\u2026, got ${got.slice(0, 12)}\u2026) \u2014 NOT installing` };
20656
20677
  const dir = mkdtempSync3(join43(tmpdir2(), "cr-update-"));
20678
+ const discardStaging = () => {
20679
+ try {
20680
+ rmSync5(dir, { recursive: true, force: true });
20681
+ } catch {
20682
+ }
20683
+ };
20657
20684
  const tgz = join43(dir, "chat-recall.tgz");
20658
20685
  writeFileSync8(tgz, buf);
20659
20686
  try {
20660
20687
  deps.install(tgz);
20661
20688
  } catch (e) {
20689
+ discardStaging();
20662
20690
  return { updated: false, reason: `install failed: ${e instanceof Error ? e.message : e}` };
20663
20691
  }
20692
+ discardStaging();
20664
20693
  const landed = (deps.verify ?? installedVersion)();
20665
20694
  if (landed && plan.to && compareVersions(landed, plan.to) < 0) {
20666
20695
  return {
@@ -20715,7 +20744,17 @@ async function runAutoUpdate(base2, authHeaders, ownVersion3, deps) {
20715
20744
  }
20716
20745
  const effectiveOwn = newerOf(ownVersion3, installedVersion());
20717
20746
  const plan = planAutoUpdate(base2, caps, effectiveOwn, process.env.CHAT_RECALL_AUTO_UPDATE);
20718
- if (!plan.update) return { updated: false, reason: plan.reason };
20747
+ if (!plan.update) {
20748
+ if (/disabled|no CLI release/.test(plan.reason) && !reportedStandingSkip.has(base2)) {
20749
+ reportedStandingSkip.add(base2);
20750
+ try {
20751
+ const { reportClientEvent: reportClientEvent2 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
20752
+ reportClientEvent2("auto_update_skipped", { message: `${plan.reason} (server offers ${caps.cli?.version ?? "?"}, this device runs ${effectiveOwn})` });
20753
+ } catch {
20754
+ }
20755
+ }
20756
+ return { updated: false, reason: plan.reason };
20757
+ }
20719
20758
  const result = await executeAutoUpdate(plan, {
20720
20759
  download: deps?.download ?? realDownload,
20721
20760
  install: deps?.install ?? realInstall,
@@ -20731,12 +20770,13 @@ async function runAutoUpdate(base2, authHeaders, ownVersion3, deps) {
20731
20770
  }
20732
20771
  return result;
20733
20772
  }
20734
- var tarballUrl;
20773
+ var tarballUrl, reportedStandingSkip;
20735
20774
  var init_auto_update = __esm({
20736
20775
  "src/auto-update.ts"() {
20737
20776
  "use strict";
20738
20777
  init_http();
20739
20778
  tarballUrl = (base2) => `${base2.replace(/\/+$/, "")}/install/chat-recall.tgz`;
20779
+ reportedStandingSkip = /* @__PURE__ */ new Set();
20740
20780
  }
20741
20781
  });
20742
20782
 
@@ -20752,6 +20792,9 @@ __export(sync_client_exports, {
20752
20792
  batchScanExternal: () => batchScanExternal,
20753
20793
  buildConversationSync: () => buildConversationSync,
20754
20794
  buildConversationTail: () => buildConversationTail,
20795
+ classedError: () => classedError,
20796
+ classifyError: () => classifyError,
20797
+ isStandingFailure: () => isStandingFailure,
20755
20798
  isSyncSkip: () => isSyncSkip,
20756
20799
  loadAllCredentials: () => loadAllCredentials,
20757
20800
  loadCredentials: () => loadCredentials,
@@ -20767,22 +20810,37 @@ __export(sync_client_exports, {
20767
20810
  syncSessions: () => syncSessions
20768
20811
  });
20769
20812
  import { join as join44, dirname as dirname19, basename as basename22 } from "node:path";
20770
- import { readFileSync as readFileSync37, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync4, rmSync as rmSync5, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync32, statSync as statSync38 } from "node:fs";
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";
20771
20814
  import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
20772
20815
  import { createHash as createHash13 } from "node:crypto";
20773
20816
  import { fileURLToPath as fileURLToPath3 } from "node:url";
20774
20817
  import { gzipSync as gzipSync3 } from "zlib";
20775
- function classifyError(msg) {
20818
+ function classedError(errorClass, message) {
20819
+ const e = new Error(message);
20820
+ Object.defineProperty(e, ERROR_CLASS, { value: errorClass, enumerable: false });
20821
+ return e;
20822
+ }
20823
+ function taggedClass(err) {
20824
+ const v = err?.[ERROR_CLASS];
20825
+ return typeof v === "string" && v ? v : null;
20826
+ }
20827
+ function isStandingFailure(err) {
20828
+ const c = taggedClass(err);
20829
+ return c !== null && STANDING_FAILURE.has(c);
20830
+ }
20831
+ function classifyError(msg, err) {
20832
+ const tagged = taggedClass(err);
20833
+ if (tagged) return tagged;
20776
20834
  const m = msg.toLowerCase();
20777
20835
  if (m.includes("429")) return "rate_limited";
20778
20836
  if (m.includes("402")) return "payment_required";
20779
20837
  if (m.includes("401") || m.includes("403") || m.includes("token")) return "auth";
20780
- if (m.includes("timed out") || m.includes("etimedout")) return "timeout";
20838
+ if (m.includes("timed out") || m.includes("etimedout") || m.includes("aborted due to timeout")) return "timeout";
20781
20839
  if (m.includes("econnrefused")) return "refused";
20782
20840
  if (m.includes("enotfound") || m.includes("dns")) return "dns";
20783
20841
  if (m.includes("fetch failed") || m.includes("econnreset")) return "network";
20784
- if (m.includes("plain http") || m.includes("https")) return "insecure_transport";
20785
- if (/5\d\d/.test(m)) return "server_error";
20842
+ if (m.includes("over plain http") || m.includes("unsupported scheme")) return "insecure_transport";
20843
+ if (/\b5\d\d\b/.test(m)) return "server_error";
20786
20844
  return "other";
20787
20845
  }
20788
20846
  function saveCredentials(c) {
@@ -21000,17 +21058,23 @@ async function syncSessions(opts = {}) {
21000
21058
  let agg = null;
21001
21059
  const errors = [];
21002
21060
  const skippedTargets = [];
21061
+ const perTarget = {};
21003
21062
  for (const cred of targets) {
21004
21063
  const verdict = breakerState(cred.serverUrl);
21005
21064
  if (verdict.open) {
21006
21065
  const notice = breakerNotice(cred.serverUrl, verdict);
21007
21066
  if (notice) console.error(`[sync] ${notice}`);
21008
21067
  skippedTargets.push(cred.serverUrl);
21068
+ perTarget[cred.serverUrl] = {
21069
+ ok: false,
21070
+ error: notice ?? `in failure cooldown for ~${Math.max(1, Math.round(verdict.retryInMs / 6e4))}m`
21071
+ };
21009
21072
  continue;
21010
21073
  }
21011
21074
  try {
21012
21075
  const r = await syncToTarget(cred, opts);
21013
21076
  noteTargetSuccess(cred.serverUrl);
21077
+ perTarget[cred.serverUrl] = { ok: true };
21014
21078
  agg = agg ? {
21015
21079
  uploaded: agg.uploaded + r.uploaded,
21016
21080
  skipped: agg.skipped + r.skipped,
@@ -21027,14 +21091,20 @@ async function syncSessions(opts = {}) {
21027
21091
  } catch (e) {
21028
21092
  const msg = e instanceof Error ? e.message : String(e);
21029
21093
  errors.push(`${cred.serverUrl}: ${msg}`);
21094
+ perTarget[cred.serverUrl] = { ok: false, error: msg };
21030
21095
  const next = noteTargetFailure(cred.serverUrl, msg);
21031
21096
  const notice = breakerNotice(cred.serverUrl, next);
21032
21097
  if (notice) console.error(`[sync] ${notice}`);
21033
21098
  record({
21034
- kind: next.open ? "breaker_trip" : "target_failure",
21099
+ // `tripped`, not `open`: an open breaker re-opens on every attempt until
21100
+ // a success clears it, so `open` counted elapsed time. Every failure is
21101
+ // still reported — as `target_failure`, carrying the streak in
21102
+ // `failures` — so nothing becomes invisible, it just stops being counted
21103
+ // as a fresh incident.
21104
+ kind: next.tripped ? "breaker_trip" : "target_failure",
21035
21105
  failures: next.failures,
21036
21106
  retryInMs: next.retryInMs,
21037
- errorClass: classifyError(msg),
21107
+ errorClass: classifyError(msg, e),
21038
21108
  local: isLocalHost(cred.serverUrl) ? 1 : 0
21039
21109
  });
21040
21110
  }
@@ -21065,7 +21135,7 @@ async function syncSessions(opts = {}) {
21065
21135
  ${errors.join("\n ")}`);
21066
21136
  if (errors.length > 0) console.error(`[sync] ${errors.length} target(s) failed (others succeeded):
21067
21137
  ${errors.join("\n ")}`);
21068
- return agg;
21138
+ return { ...agg, perTarget };
21069
21139
  }
21070
21140
  async function postFieldBatch(base2, token, rows) {
21071
21141
  const headers = { "content-type": "application/json" };
@@ -21139,7 +21209,7 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
21139
21209
  let reaped = 0;
21140
21210
  let names;
21141
21211
  try {
21142
- names = readdirSync32(dir);
21212
+ names = readdirSync33(dir);
21143
21213
  } catch {
21144
21214
  return 0;
21145
21215
  }
@@ -21147,8 +21217,8 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
21147
21217
  if (!name.startsWith("cr-batchscan-")) continue;
21148
21218
  const p = join44(dir, name);
21149
21219
  try {
21150
- if (now - statSync38(p).mtimeMs < maxAgeMs) continue;
21151
- rmSync5(p, { recursive: true, force: true });
21220
+ if (now - statSync39(p).mtimeMs < maxAgeMs) continue;
21221
+ rmSync6(p, { recursive: true, force: true });
21152
21222
  reaped++;
21153
21223
  } catch {
21154
21224
  }
@@ -21180,7 +21250,7 @@ function batchScanExternal(items, opts = {}) {
21180
21250
  slices++;
21181
21251
  }
21182
21252
  try {
21183
- rmSync5(scanDir, { recursive: true, force: true });
21253
+ rmSync6(scanDir, { recursive: true, force: true });
21184
21254
  } catch {
21185
21255
  }
21186
21256
  safeToId = /* @__PURE__ */ new Map();
@@ -21205,7 +21275,7 @@ function batchScanExternal(items, opts = {}) {
21205
21275
  flushSlice();
21206
21276
  } finally {
21207
21277
  try {
21208
- rmSync5(scanDir, { recursive: true, force: true });
21278
+ rmSync6(scanDir, { recursive: true, force: true });
21209
21279
  } catch {
21210
21280
  }
21211
21281
  }
@@ -21252,7 +21322,7 @@ async function syncToTarget(cred, opts = {}) {
21252
21322
  }
21253
21323
  async function syncToTargetInScope(cred, opts = {}) {
21254
21324
  const risk = transportRisk(cred.serverUrl);
21255
- if (risk) throw new Error(risk);
21325
+ if (risk) throw classedError("insecure_transport", risk);
21256
21326
  const sync = loadSettings().sync;
21257
21327
  const serverCfg = await fetchTenantSyncConfig(cred);
21258
21328
  const excludeTools = /* @__PURE__ */ new Set([...sync?.excludeTools ?? [], ...serverCfg.excludeTools]);
@@ -21338,9 +21408,10 @@ async function syncToTargetInScope(cred, opts = {}) {
21338
21408
  setServerIngestConcurrency(base2, Math.floor(adv));
21339
21409
  }
21340
21410
  if (body2.cli && body2.cli.version) {
21341
- const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
21411
+ const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
21342
21412
  const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
21343
21413
  const plan = planAutoUpdate2(base2, { cli: body2.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21414
+ sweepStaleStaging2();
21344
21415
  if (plan.update) {
21345
21416
  void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
21346
21417
  });
@@ -21351,7 +21422,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21351
21422
  const retryable = res.status === 429 || res.status >= 500;
21352
21423
  const text = await res.text().catch(() => "");
21353
21424
  if (res.status === 401 || res.status === 403) {
21354
- throw new Error(`server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base2}`);
21425
+ throw classedError("auth", `server rejected this machine's device token (HTTP ${res.status} \u2014 revoked?). Reconnect with: chat-recall login ${base2}`);
21355
21426
  }
21356
21427
  if (res.status === 402) {
21357
21428
  let detail = text;
@@ -21365,7 +21436,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21365
21436
  ${body2.upgradeUrl}`;
21366
21437
  } catch {
21367
21438
  }
21368
- throw new Error(`sync paused by the server: ${detail}`);
21439
+ throw classedError("payment_required", `sync paused by the server: ${detail}`);
21369
21440
  }
21370
21441
  if (!retryable) throw new Error(`sync failed: HTTP ${res.status} ${text}`);
21371
21442
  if (attempt >= RETRY_DELAYS_MS.length) throw new Error(`sync failed after ${attempt + 1} attempts: HTTP ${res.status} ${text}`);
@@ -21375,7 +21446,7 @@ async function syncToTargetInScope(cred, opts = {}) {
21375
21446
  await sleep2(waitMs);
21376
21447
  continue;
21377
21448
  } catch (err) {
21378
- const fatal = err instanceof Error && /HTTP 4\d\d|sync failed after/.test(err.message);
21449
+ const fatal = isStandingFailure(err) || err instanceof Error && /HTTP 4\d\d|sync failed after/.test(err.message);
21379
21450
  if (fatal || attempt >= RETRY_DELAYS_MS.length) throw err;
21380
21451
  console.error(`[sync] upload attempt ${attempt + 1} failed (${err instanceof Error ? err.message : err}) \u2014 retrying in ${RETRY_DELAYS_MS[attempt] / 1e3}s`);
21381
21452
  await sleep2(RETRY_DELAYS_MS[attempt]);
@@ -21466,9 +21537,10 @@ async function syncToTargetInScope(cred, opts = {}) {
21466
21537
  if (!res.ok) throw new Error(`append sync failed: HTTP ${res.status} ${await res.text().catch(() => "")}`);
21467
21538
  const body = await res.json().catch(() => ({}));
21468
21539
  if (body.cli && body.cli.version) {
21469
- const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
21540
+ const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
21470
21541
  const authHeaders = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
21471
21542
  const plan = planAutoUpdate2(base2, { cli: body.cli }, ownVersion, process.env.CHAT_RECALL_AUTO_UPDATE);
21543
+ sweepStaleStaging2();
21472
21544
  if (plan.update) {
21473
21545
  void runAutoUpdate2(base2, authHeaders, ownVersion).catch(() => {
21474
21546
  });
@@ -21913,7 +21985,12 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21913
21985
  const containerBytes = built ? built.files.reduce((n, f) => n + Buffer.byteLength(f.text), 0) : 0;
21914
21986
  if (containerBytes > FULL_BUILD_MAX_BYTES) {
21915
21987
  console.error(`[sync] ${ref.prefixedId} is ${(containerBytes / 1048576).toFixed(0)}MB (over the ${(FULL_BUILD_MAX_BYTES / 1048576).toFixed(0)}MB ceiling) \u2014 shipping without the raw archive and derived data to keep the walk in memory`);
21916
- record({ kind: "oversized_session", mb: Math.round(containerBytes / 1048576), tool: ref.toolId });
21988
+ record({
21989
+ kind: "oversized_session",
21990
+ mb: Math.round(containerBytes / 1048576),
21991
+ tool: ref.toolId,
21992
+ dedupeKey: stableMark(ref.prefixedId)
21993
+ });
21917
21994
  includeRaw = false;
21918
21995
  includeMeta = false;
21919
21996
  }
@@ -22224,7 +22301,7 @@ async function syncIncremental(opts = {}) {
22224
22301
  lock.release();
22225
22302
  }
22226
22303
  }
22227
- var ownVersion, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, _fetchTenantSyncConfigForTests, SYNC_RULES_REFUSAL, hashPath, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, FULL_BUILD_TAIL_BYTES, BATCH_SCAN_MAX_BYTES;
22304
+ var ownVersion, ERROR_CLASS, STANDING_FAILURE, walkProgressForUpload, lastServerPackSpec, credPath, _securityConfigCache, SECURITY_CONFIG_TTL_MS, _syncConfigCache, EMPTY_SYNC_CONFIG, _fetchTenantSyncConfigForTests, SYNC_RULES_REFUSAL, hashPath, MAX_DERIVED_ROW_BYTES, isLocalHost, CAPS_TTL_MS, capsOk, advertisedIngestConc, advertisedGzipBody, FULL_BUILD_MAX_BYTES, FULL_BUILD_TAIL_BYTES, BATCH_SCAN_MAX_BYTES;
22228
22305
  var init_sync_client = __esm({
22229
22306
  "src/sync-client.ts"() {
22230
22307
  "use strict";
@@ -22270,10 +22347,16 @@ var init_sync_client = __esm({
22270
22347
  ownVersion = JSON.parse(readFileSync37(join44(dir, "../package.json"), "utf-8")).version || ownVersion;
22271
22348
  } catch {
22272
22349
  try {
22273
- if (true) ownVersion = "0.5.29";
22350
+ if (true) ownVersion = "0.5.31";
22274
22351
  } catch {
22275
22352
  }
22276
22353
  }
22354
+ ERROR_CLASS = "__chatRecallErrorClass";
22355
+ STANDING_FAILURE = /* @__PURE__ */ new Set([
22356
+ "auth",
22357
+ "payment_required",
22358
+ "insecure_transport"
22359
+ ]);
22277
22360
  walkProgressForUpload = null;
22278
22361
  lastServerPackSpec = null;
22279
22362
  credPath = () => join44(getDataDir(), "credentials.json");
@@ -22384,11 +22467,11 @@ var init_update_notice = __esm({
22384
22467
  });
22385
22468
 
22386
22469
  // ../engine/dist/core/which.js
22387
- import { existsSync as existsSync43, statSync as statSync39, accessSync, constants } from "node:fs";
22470
+ import { existsSync as existsSync43, statSync as statSync40, accessSync, constants } from "node:fs";
22388
22471
  import { join as join46, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
22389
22472
  function executable(p) {
22390
22473
  try {
22391
- if (!statSync39(p).isFile())
22474
+ if (!statSync40(p).isFile())
22392
22475
  return false;
22393
22476
  } catch {
22394
22477
  return false;
@@ -22524,7 +22607,7 @@ function registerMcpServersJson(target, path, spec) {
22524
22607
  entry.alwaysAllow = existing?.alwaysAllow ?? spec.alwaysAllow;
22525
22608
  if (spec.env)
22526
22609
  entry.env = { ...existing?.env, ...spec.env };
22527
- const isCurrent = !!existing && existing.command === spec.command && JSON.stringify(existing.args ?? null) === JSON.stringify(spec.args ?? null) && Object.entries(spec.env ?? {}).every(([k, v]) => existing.env?.[k] === v);
22610
+ const isCurrent = !!existing && !existing.disabled && existing.command === spec.command && JSON.stringify(existing.args ?? null) === JSON.stringify(spec.args ?? null) && Object.entries(spec.env ?? {}).every(([k, v]) => existing.env?.[k] === v);
22528
22611
  if (isCurrent)
22529
22612
  return { id: target.id, label: target.label, path, state: "current" };
22530
22613
  servers[MCP_SERVER_NAME] = entry;
@@ -22551,7 +22634,7 @@ function registerOpencodeJson(path, spec) {
22551
22634
  const entry = { type: "local", command: argv, enabled: true };
22552
22635
  if (spec.env)
22553
22636
  entry.environment = { ...existing?.environment, ...spec.env };
22554
- const isCurrent = !!existing && JSON.stringify(existing.command ?? null) === JSON.stringify(argv) && existing.enabled !== false && Object.entries(spec.env ?? {}).every(([k, v]) => existing.environment?.[k] === v);
22637
+ const isCurrent = !!existing && existing.type === "local" && existing.enabled === true && JSON.stringify(existing.command ?? null) === JSON.stringify(argv) && Object.entries(spec.env ?? {}).every(([k, v]) => existing.environment?.[k] === v);
22555
22638
  if (isCurrent)
22556
22639
  return { id, label, path, state: "current" };
22557
22640
  mcp[MCP_SERVER_NAME] = entry;
@@ -22730,7 +22813,7 @@ __export(companions_exports, {
22730
22813
  uninstallCodeindex: () => uninstallCodeindex,
22731
22814
  unregisterCodeindexMcp: () => unregisterCodeindexMcp
22732
22815
  });
22733
- import { existsSync as existsSync45, mkdirSync as mkdirSync11, statSync as statSync40, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync40, writeFileSync as writeFileSync12 } from "node:fs";
22816
+ import { existsSync as existsSync45, mkdirSync as mkdirSync11, statSync as statSync41, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync40, writeFileSync as writeFileSync12 } from "node:fs";
22734
22817
  import { homedir as homedir16 } from "node:os";
22735
22818
  import { join as join48 } from "node:path";
22736
22819
  import { execSync as execSync3 } from "node:child_process";
@@ -22781,7 +22864,7 @@ function checkCodeindexStatus() {
22781
22864
  status.installed = true;
22782
22865
  status.path = foundPath;
22783
22866
  try {
22784
- status.size = statSync40(foundPath).size;
22867
+ status.size = statSync41(foundPath).size;
22785
22868
  } catch {
22786
22869
  }
22787
22870
  try {
@@ -22931,7 +23014,7 @@ __export(install_skills_exports, {
22931
23014
  skillsSourceDir: () => skillsSourceDir,
22932
23015
  uninstallSkills: () => uninstallSkills
22933
23016
  });
22934
- import { readdirSync as readdirSync33, existsSync as existsSync46, mkdirSync as mkdirSync12, rmSync as rmSync6, cpSync, writeFileSync as writeFileSync13, readFileSync as readFileSync41, statSync as statSync41, realpathSync as realpathSync4 } from "fs";
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";
22935
23018
  import { createHash as createHash15 } from "crypto";
22936
23019
  import { join as join49, basename as basename23 } from "path";
22937
23020
  function skillsSourceDir() {
@@ -22957,7 +23040,7 @@ function skillsFingerprint() {
22957
23040
  }
22958
23041
  function filesUnder(dir, prefix = "") {
22959
23042
  const out = [];
22960
- for (const e of readdirSync33(dir, { withFileTypes: true })) {
23043
+ for (const e of readdirSync34(dir, { withFileTypes: true })) {
22961
23044
  if (e.name === MARKER) continue;
22962
23045
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
22963
23046
  if (e.isDirectory()) out.push(...filesUnder(join49(dir, e.name), rel));
@@ -22971,7 +23054,7 @@ function skillStamp() {
22971
23054
  function bundledSkillNames() {
22972
23055
  const src = skillsSourceDir();
22973
23056
  if (!existsSync46(src)) return [];
22974
- return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join49(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
23057
+ return readdirSync34(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join49(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
22975
23058
  }
22976
23059
  function realPathOrSelf(p) {
22977
23060
  try {
@@ -23044,7 +23127,7 @@ function installSkills(opts = {}) {
23044
23127
  continue;
23045
23128
  }
23046
23129
  mkdirSync12(t.dir, { recursive: true });
23047
- rmSync6(dest, { recursive: true, force: true });
23130
+ rmSync7(dest, { recursive: true, force: true });
23048
23131
  cpSync(join49(src, name), dest, { recursive: true });
23049
23132
  writeFileSync13(join49(dest, MARKER), `${stamp}
23050
23133
  `);
@@ -23063,11 +23146,11 @@ function uninstallSkills() {
23063
23146
  perTarget.push({ id: t.id, label: t.label, removed });
23064
23147
  continue;
23065
23148
  }
23066
- for (const entry of readdirSync33(t.dir, { withFileTypes: true })) {
23149
+ for (const entry of readdirSync34(t.dir, { withFileTypes: true })) {
23067
23150
  if (!entry.isDirectory()) continue;
23068
23151
  const dir = join49(t.dir, entry.name);
23069
23152
  if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
23070
- rmSync6(dir, { recursive: true, force: true });
23153
+ rmSync7(dir, { recursive: true, force: true });
23071
23154
  removed.push(entry.name);
23072
23155
  }
23073
23156
  }
@@ -23748,11 +23831,11 @@ __export(toolkit_pull_exports, {
23748
23831
  planPull: () => planPull,
23749
23832
  portableCommand: () => portableCommand
23750
23833
  });
23751
- import { existsSync as existsSync48, mkdirSync as mkdirSync14, statSync as statSync42, writeFileSync as writeFileSync15 } from "node:fs";
23834
+ import { existsSync as existsSync48, mkdirSync as mkdirSync14, statSync as statSync43, writeFileSync as writeFileSync15 } from "node:fs";
23752
23835
  import { dirname as dirname22, join as join52, basename as basename26, isAbsolute as isAbsolute3 } from "node:path";
23753
23836
  function isExecutableFile(p) {
23754
23837
  try {
23755
- return statSync42(p).isFile();
23838
+ return statSync43(p).isFile();
23756
23839
  } catch {
23757
23840
  return false;
23758
23841
  }
@@ -25673,7 +25756,7 @@ __export(project_tasks_exports, {
25673
25756
  pushProjectTaskStatuses: () => pushProjectTaskStatuses,
25674
25757
  renderTeamTasksMd: () => renderTeamTasksMd
25675
25758
  });
25676
- import { existsSync as existsSync50, readFileSync as readFileSync45, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, statSync as statSync43 } from "node:fs";
25759
+ import { existsSync as existsSync50, readFileSync as readFileSync45, writeFileSync as writeFileSync16, mkdirSync as mkdirSync15, statSync as statSync44 } from "node:fs";
25677
25760
  import { join as join54, dirname as dirname23 } from "node:path";
25678
25761
  import { homedir as homedir19 } from "node:os";
25679
25762
  function isTeamTaskStatus(v) {
@@ -25762,7 +25845,7 @@ async function pushProjectTaskStatuses(base2, authHeaders, opts = {}) {
25762
25845
  if (!existsSync50(file)) continue;
25763
25846
  let mtime;
25764
25847
  try {
25765
- mtime = statSync43(file).mtimeMs;
25848
+ mtime = statSync44(file).mtimeMs;
25766
25849
  } catch {
25767
25850
  continue;
25768
25851
  }
@@ -25972,8 +26055,9 @@ async function drainSyncIntents(opts = {}) {
25972
26055
  const data = await res.json();
25973
26056
  pending = data.intents || [];
25974
26057
  if (data.cli && data.cli.version) {
25975
- const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
26058
+ const { planAutoUpdate: planAutoUpdate2, runAutoUpdate: runAutoUpdate2, sweepStaleStaging: sweepStaleStaging2 } = await Promise.resolve().then(() => (init_auto_update(), auto_update_exports));
25976
26059
  const plan = planAutoUpdate2(base2, { cli: data.cli }, ownVersion2, process.env.CHAT_RECALL_AUTO_UPDATE);
26060
+ sweepStaleStaging2();
25977
26061
  if (plan.update) {
25978
26062
  void runAutoUpdate2(base2, authHeaders, ownVersion2).catch(() => {
25979
26063
  });
@@ -26027,7 +26111,7 @@ var init_intent_drain = __esm({
26027
26111
  ownVersion2 = JSON.parse(readFileSync46(join55(dir, "../package.json"), "utf-8")).version || ownVersion2;
26028
26112
  } catch {
26029
26113
  try {
26030
- if (true) ownVersion2 = "0.5.29";
26114
+ if (true) ownVersion2 = "0.5.31";
26031
26115
  } catch {
26032
26116
  }
26033
26117
  }
@@ -26228,7 +26312,7 @@ var team_merge_exports = {};
26228
26312
  __export(team_merge_exports, {
26229
26313
  mergePullResult: () => mergePullResult
26230
26314
  });
26231
- import { existsSync as existsSync52, mkdirSync as mkdirSync17, readFileSync as readFileSync47, rmSync as rmSync7, writeFileSync as writeFileSync18, statSync as statSync44 } from "fs";
26315
+ import { existsSync as existsSync52, mkdirSync as mkdirSync17, readFileSync as readFileSync47, rmSync as rmSync8, writeFileSync as writeFileSync18, statSync as statSync45 } from "fs";
26232
26316
  import { dirname as dirname25, join as join56 } from "path";
26233
26317
  import { createHash as createHash18 } from "crypto";
26234
26318
  function installPathFor(art, tool) {
@@ -26345,7 +26429,7 @@ async function mergePullResult(opts) {
26345
26429
  continue;
26346
26430
  try {
26347
26431
  if (existsSync52(path))
26348
- rmSync7(path, { force: true });
26432
+ rmSync8(path, { force: true });
26349
26433
  await teamInstallsForget(id, row.tool);
26350
26434
  report.removed.push({ artifactId: id, path });
26351
26435
  } catch (err) {
@@ -26364,7 +26448,7 @@ function readFileIfExists(path) {
26364
26448
  try {
26365
26449
  if (!existsSync52(path))
26366
26450
  return null;
26367
- const s = statSync44(path);
26451
+ const s = statSync45(path);
26368
26452
  if (!s.isFile())
26369
26453
  return null;
26370
26454
  return readFileSync47(path);
@@ -26449,7 +26533,7 @@ var init_vault_crypto = __esm({
26449
26533
  });
26450
26534
 
26451
26535
  // ../engine/dist/core/vault-source.js
26452
- import { existsSync as existsSync53, readdirSync as readdirSync34, statSync as statSync45 } from "fs";
26536
+ import { existsSync as existsSync53, readdirSync as readdirSync35, statSync as statSync46 } from "fs";
26453
26537
  import { extname, join as join57 } from "path";
26454
26538
  function* walkVaultSources() {
26455
26539
  const t = loadSettings().team.vault;
@@ -26467,7 +26551,7 @@ function* walkVaultSources() {
26467
26551
  }
26468
26552
  function safeStat(path) {
26469
26553
  try {
26470
- const s = statSync45(path);
26554
+ const s = statSync46(path);
26471
26555
  if (!s.isFile())
26472
26556
  return null;
26473
26557
  return { mtimeMs: s.mtimeMs, size: s.size };
@@ -26481,7 +26565,7 @@ function* walkClaude() {
26481
26565
  return;
26482
26566
  let projectDirs = [];
26483
26567
  try {
26484
- projectDirs = readdirSync34(root);
26568
+ projectDirs = readdirSync35(root);
26485
26569
  } catch {
26486
26570
  return;
26487
26571
  }
@@ -26492,7 +26576,7 @@ function* walkClaude() {
26492
26576
  const projDir = join57(root, proj);
26493
26577
  let entries = [];
26494
26578
  try {
26495
- entries = readdirSync34(projDir);
26579
+ entries = readdirSync35(projDir);
26496
26580
  } catch {
26497
26581
  continue;
26498
26582
  }
@@ -26514,7 +26598,7 @@ function* walkGemini() {
26514
26598
  return;
26515
26599
  let hashDirs = [];
26516
26600
  try {
26517
- hashDirs = readdirSync34(tmp);
26601
+ hashDirs = readdirSync35(tmp);
26518
26602
  } catch {
26519
26603
  return;
26520
26604
  }
@@ -26524,7 +26608,7 @@ function* walkGemini() {
26524
26608
  continue;
26525
26609
  let entries = [];
26526
26610
  try {
26527
- entries = readdirSync34(chatsDir);
26611
+ entries = readdirSync35(chatsDir);
26528
26612
  } catch {
26529
26613
  continue;
26530
26614
  }
@@ -26570,7 +26654,7 @@ function* walkCodex() {
26570
26654
  const dir = stack.pop();
26571
26655
  let entries = [];
26572
26656
  try {
26573
- entries = readdirSync34(dir, { withFileTypes: true });
26657
+ entries = readdirSync35(dir, { withFileTypes: true });
26574
26658
  } catch {
26575
26659
  continue;
26576
26660
  }
@@ -26596,14 +26680,14 @@ function* walkAgy() {
26596
26680
  return;
26597
26681
  let sessionDirs = [];
26598
26682
  try {
26599
- sessionDirs = readdirSync34(brainDir);
26683
+ sessionDirs = readdirSync35(brainDir);
26600
26684
  } catch {
26601
26685
  return;
26602
26686
  }
26603
26687
  for (const rawId of sessionDirs) {
26604
26688
  const sessionPath = join57(brainDir, rawId);
26605
26689
  try {
26606
- if (!statSync45(sessionPath).isDirectory())
26690
+ if (!statSync46(sessionPath).isDirectory())
26607
26691
  continue;
26608
26692
  } catch {
26609
26693
  continue;
@@ -27084,7 +27168,7 @@ __export(service_installer_exports, {
27084
27168
  uninstallService: () => uninstallService
27085
27169
  });
27086
27170
  import { execSync as execSync5 } from "child_process";
27087
- import { existsSync as existsSync55, mkdirSync as mkdirSync19, rmSync as rmSync8, writeFileSync as writeFileSync20 } from "fs";
27171
+ import { existsSync as existsSync55, mkdirSync as mkdirSync19, rmSync as rmSync9, writeFileSync as writeFileSync20 } from "fs";
27088
27172
  import { homedir as homedir21 } from "os";
27089
27173
  import { join as join59 } from "path";
27090
27174
  import { fileURLToPath as fileURLToPath5 } from "url";
@@ -27240,7 +27324,7 @@ function uninstallService() {
27240
27324
  }
27241
27325
  const unitPath = join59(homedir21(), ".config", "systemd", "user", "chat-recall-watch.service");
27242
27326
  if (existsSync55(unitPath)) {
27243
- rmSync8(unitPath, { force: true });
27327
+ rmSync9(unitPath, { force: true });
27244
27328
  removed = true;
27245
27329
  }
27246
27330
  try {
@@ -27257,7 +27341,7 @@ function uninstallService() {
27257
27341
  }
27258
27342
  let removed = false;
27259
27343
  if (existsSync55(plistPath)) {
27260
- rmSync8(plistPath, { force: true });
27344
+ rmSync9(plistPath, { force: true });
27261
27345
  removed = true;
27262
27346
  }
27263
27347
  return removed;
@@ -27534,7 +27618,7 @@ init_tool_paths();
27534
27618
  init_project_resolver();
27535
27619
  import { config } from "dotenv";
27536
27620
  import { Command } from "commander";
27537
- import chalk from "chalk";
27621
+ import chalk2 from "chalk";
27538
27622
  import { existsSync as existsSync56, readFileSync as readFileSync49, writeFileSync as writeFileSync21, realpathSync as realpathSync5 } from "fs";
27539
27623
  import { homedir as homedir22 } from "os";
27540
27624
  import { join as join60, resolve as resolve2, dirname as dirname27, basename as basename29 } from "path";
@@ -27595,16 +27679,64 @@ function openBrowser(url) {
27595
27679
  init_which();
27596
27680
  init_collector_health();
27597
27681
  init_telemetry_consent();
27682
+
27683
+ // src/env-needed.ts
27684
+ import chalk from "chalk";
27685
+ function isSecret(name) {
27686
+ return /(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|API_?KEY|PAT)\b/i.test(name);
27687
+ }
27688
+ function unusableHere(server) {
27689
+ const mac = /xcode|swift|apple|ios|macos/i.test(server);
27690
+ if (mac && process.platform !== "darwin") return "macOS only \u2014 nothing to do on this machine";
27691
+ return null;
27692
+ }
27693
+ function collectEnvNeeds(outcomes) {
27694
+ const byServer = /* @__PURE__ */ new Map();
27695
+ for (const o of outcomes) {
27696
+ if (!o.needsEnv?.length) continue;
27697
+ const set = byServer.get(o.name) ?? /* @__PURE__ */ new Set();
27698
+ for (const v of o.needsEnv) set.add(v);
27699
+ byServer.set(o.name, set);
27700
+ }
27701
+ return [...byServer.entries()].map(([server, vars]) => ({ server, vars: [...vars].sort() })).sort((a, b) => a.server.localeCompare(b.server));
27702
+ }
27703
+ function renderEnvNeeds(needs, installedCount) {
27704
+ if (!needs.length) return [];
27705
+ const lines = [];
27706
+ const plural = installedCount === 1 ? "server" : "servers";
27707
+ lines.push(
27708
+ chalk.yellow(`Installed ${installedCount} MCP ${plural} from your account. `) + chalk.yellow(`${needs.length === 1 ? "One needs" : `${needs.length} need`} values you must set here:`)
27709
+ );
27710
+ lines.push("");
27711
+ const width = Math.max(...needs.map((n) => n.server.length));
27712
+ let anySecret = false;
27713
+ for (const n of needs) {
27714
+ const note = unusableHere(n.server);
27715
+ anySecret ||= n.vars.some(isSecret);
27716
+ const name = chalk.bold(n.server.padEnd(width));
27717
+ const vars = n.vars.join(", ");
27718
+ lines.push(` ${name} ${vars}${note ? chalk.dim(` (${note})`) : ""}`);
27719
+ }
27720
+ lines.push("");
27721
+ lines.push(chalk.dim(" Add them to your shell profile, then restart your AI tool."));
27722
+ lines.push(chalk.dim(" Until then these servers will fail to start."));
27723
+ if (anySecret) {
27724
+ lines.push(chalk.dim(" chat-recall never uploads secret values \u2014 only the variable names."));
27725
+ }
27726
+ return lines;
27727
+ }
27728
+
27729
+ // src/cli.ts
27598
27730
  function matchLabel(tier) {
27599
27731
  switch (tier) {
27600
27732
  case "strong":
27601
- return chalk.green("strong");
27733
+ return chalk2.green("strong");
27602
27734
  case "good":
27603
- return chalk.yellow("good");
27735
+ return chalk2.yellow("good");
27604
27736
  case "weak":
27605
- return chalk.dim("weak");
27737
+ return chalk2.dim("weak");
27606
27738
  default:
27607
- return chalk.dim("unranked");
27739
+ return chalk2.dim("unranked");
27608
27740
  }
27609
27741
  }
27610
27742
  config({ quiet: true });
@@ -27639,13 +27771,13 @@ function describeProbeError(err) {
27639
27771
  }
27640
27772
  function failLogin(opts, message) {
27641
27773
  if (opts.fromInit) throw new Error(message);
27642
- console.error(chalk.red("login failed:"), message);
27774
+ console.error(chalk2.red("login failed:"), message);
27643
27775
  process.exit(1);
27644
27776
  }
27645
27777
  function requireTarget() {
27646
27778
  const t = firstTarget();
27647
27779
  if (!t) {
27648
- console.error(chalk.red("Not logged in."), "Run", chalk.bold("chat-recall login <server-url>"), "first.");
27780
+ console.error(chalk2.red("Not logged in."), "Run", chalk2.bold("chat-recall login <server-url>"), "first.");
27649
27781
  process.exit(1);
27650
27782
  }
27651
27783
  return t;
@@ -27803,7 +27935,7 @@ var program = new Command();
27803
27935
  program.name("chat-recall").description("Semantic search for Claude Code sessions - recall and resume past conversations").version(pkgVersion);
27804
27936
  program.command("init").description("Set up chat-recall: connect to your server, configure MCP, ship your local sessions").option("--server <url>", "chat-recall server URL to connect this machine to").option("--token <token>", "Self-host device token (skips the interactive OIDC login)").option("--skip-mcp", "Skip MCP server configuration").option("--skip-sync", "Skip the first session sync").option("--with-codeindex", "Force-download the codeindex binary during init. Default behavior is to detect an already-installed codeindex on PATH and register it as an MCP server.").option("--skip-codeindex", "Skip the codeindex companion entirely (no detection, no registration).").option("--skip-service", "Skip installing the per-user background sync service (Linux/macOS/Windows). By default init installs it so new conversations ship automatically.").option("--yes", "Do not pause before the first upload. The scope summary still prints.", false).option("--prompt-json", "Print the device prompt as one JSON line on stdout, then continue", false).action(async (options) => {
27805
27937
  try {
27806
- console.log(chalk.bold("chat-recall init") + chalk.dim(` v${pkgVersion}`));
27938
+ console.log(chalk2.bold("chat-recall init") + chalk2.dim(` v${pkgVersion}`));
27807
27939
  const { createInitReporter: createInitReporter2 } = await Promise.resolve().then(() => (init_init_report(), init_report_exports));
27808
27940
  const rep = createInitReporter2({
27809
27941
  tty: process.stdout.isTTY === true,
@@ -27827,8 +27959,8 @@ program.command("init").description("Set up chat-recall: connect to your server,
27827
27959
  }
27828
27960
  }
27829
27961
  const foundTools = clis.filter((c) => c.available).map((c) => c.name.replace(/ (CLI|Code)$/, ""));
27830
- if (!foundTools.length) rep.warn(chalk.yellow("No AI tools detected \u2014 chat-recall indexes their transcripts, so there is nothing to ship yet."));
27831
- const serverBad = (text) => rep.warn(`${chalk.yellow("Not connected")} \u2014 ${text}`);
27962
+ if (!foundTools.length) rep.warn(chalk2.yellow("No AI tools detected \u2014 chat-recall indexes their transcripts, so there is nothing to ship yet."));
27963
+ const serverBad = (text) => rep.warn(`${chalk2.yellow("Not connected")} \u2014 ${text}`);
27832
27964
  let target = firstTarget();
27833
27965
  let deadTarget = false;
27834
27966
  if (options.server) {
@@ -27852,24 +27984,24 @@ program.command("init").description("Set up chat-recall: connect to your server,
27852
27984
  if (probeOk2(probe)) {
27853
27985
  } else if (probe.kind === "moved" || probe.kind === "not_api") {
27854
27986
  const retired = target.base;
27855
- console.log(` ${chalk.yellow("The saved server no longer serves chat-recall")} \u2192 ${retired}`);
27987
+ console.log(` ${chalk2.yellow("The saved server no longer serves chat-recall")} \u2192 ${retired}`);
27856
27988
  console.log(` ${probeAdvice2(probe, retired)}`);
27857
- console.log(` Switching to ${chalk.bold(DEFAULT_SERVER)} \u2014 sign in to approve this machine.`);
27989
+ console.log(` Switching to ${chalk2.bold(DEFAULT_SERVER)} \u2014 sign in to approve this machine.`);
27858
27990
  try {
27859
27991
  await runLogin(DEFAULT_SERVER, { token: options.token, fromInit: true, promptJson: options.promptJson });
27860
27992
  const { removeCredentials: removeCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
27861
27993
  if (removeCredentials2(retired)) {
27862
- console.log(` ${chalk.dim(`Removed the retired target ${retired} (log in there again any time).`)}`);
27994
+ console.log(` ${chalk2.dim(`Removed the retired target ${retired} (log in there again any time).`)}`);
27863
27995
  }
27864
27996
  target = firstTarget();
27865
- if (target) console.log(` ${chalk.green("Connected")} \u2192 ${target.base}`);
27997
+ if (target) console.log(` ${chalk2.green("Connected")} \u2192 ${target.base}`);
27866
27998
  } catch (e) {
27867
27999
  serverBad(describeProbeError(e));
27868
- console.log(` ${chalk.dim(`Run ${chalk.bold(`chat-recall login ${DEFAULT_SERVER}`)} when ready, then ${chalk.bold(`chat-recall logout ${retired}`)}.`)}`);
28000
+ console.log(` ${chalk2.dim(`Run ${chalk2.bold(`chat-recall login ${DEFAULT_SERVER}`)} when ready, then ${chalk2.bold(`chat-recall logout ${retired}`)}.`)}`);
27869
28001
  deadTarget = true;
27870
28002
  }
27871
28003
  } else {
27872
- console.log(` ${chalk.yellow("Saved server did not answer")} \u2192 ${target.base}`);
28004
+ console.log(` ${chalk2.yellow("Saved server did not answer")} \u2192 ${target.base}`);
27873
28005
  console.log(` ${probeAdvice2(probe, target.base)}`);
27874
28006
  }
27875
28007
  } else if (target) {
@@ -27899,8 +28031,8 @@ program.command("init").description("Set up chat-recall: connect to your server,
27899
28031
  }
27900
28032
  for (const r of bad) {
27901
28033
  const why = (r.error ?? "").replace(/^SyntaxError:\s*/, "").split("\n")[0];
27902
- rep.warn(`${chalk.yellow(`${r.label} config left alone \u2014 ${why || "the file does not parse"}`)} ${chalk.dim(r.path)}`);
27903
- rep.warn(`${chalk.dim(` fix that file, then: chat-recall init (nothing else needs redoing)`)}`);
28034
+ rep.warn(`${chalk2.yellow(`${r.label} config left alone \u2014 ${why || "the file does not parse"}`)} ${chalk2.dim(r.path)}`);
28035
+ rep.warn(`${chalk2.dim(` fix that file, then: chat-recall init (nothing else needs redoing)`)}`);
27904
28036
  }
27905
28037
  if (process.env.CHAT_RECALL_VERBOSE) {
27906
28038
  }
@@ -27932,14 +28064,14 @@ program.command("init").description("Set up chat-recall: connect to your server,
27932
28064
  const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
27933
28065
  }
27934
28066
  } else if (!result.prebuiltAvailable) {
27935
- rep.warn(`${chalk.yellow("codeindex")}: ${result.unsupportedReason}`);
28067
+ rep.warn(`${chalk2.yellow("codeindex")}: ${result.unsupportedReason}`);
27936
28068
  }
27937
28069
  } catch (err) {
27938
28070
  const msg = String(err);
27939
- rep.warn(`${chalk.yellow("codeindex install failed")}: ${msg}`);
28071
+ rep.warn(`${chalk2.yellow("codeindex install failed")}: ${msg}`);
27940
28072
  if (msg.includes("404")) {
27941
- console.log(chalk.dim(" The codeindex release returned 404. If the repo is private,"));
27942
- console.log(chalk.dim(" authenticate `gh` to it, or build from source."));
28073
+ console.log(chalk2.dim(" The codeindex release returned 404. If the repo is private,"));
28074
+ console.log(chalk2.dim(" authenticate `gh` to it, or build from source."));
27943
28075
  }
27944
28076
  }
27945
28077
  } else {
@@ -27955,7 +28087,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
27955
28087
  }
27956
28088
  const userOwned = [...new Set(r.perTarget.flatMap((t) => t.skippedUserOwned))];
27957
28089
  } catch (err) {
27958
- rep.warn(`${chalk.yellow("Skill install failed")} \u2014 ${err instanceof Error ? err.message : err}`);
28090
+ rep.warn(`${chalk2.yellow("Skill install failed")} \u2014 ${err instanceof Error ? err.message : err}`);
27959
28091
  }
27960
28092
  if (!options.skipMcp && firstTarget()) {
27961
28093
  try {
@@ -27993,28 +28125,30 @@ program.command("init").description("Set up chat-recall: connect to your server,
27993
28125
  const written = report.outcomes.filter((o) => o.status === "written");
27994
28126
  const present = report.outcomes.filter((o) => o.status === "present").length;
27995
28127
  if (answered === 0) {
27996
- rep.warn(chalk.yellow("Could not ask your server what MCP servers are on your account"));
27997
- for (const f of askFailed) console.log(` ${chalk.dim(f)}`);
27998
- console.log(` ${chalk.dim("Re-run `chat-recall toolkit pull` once the server answers.")}`);
28128
+ rep.warn(chalk2.yellow("Could not ask your server what MCP servers are on your account"));
28129
+ for (const f of askFailed) console.log(` ${chalk2.dim(f)}`);
28130
+ console.log(` ${chalk2.dim("Re-run `chat-recall toolkit pull` once the server answers.")}`);
27999
28131
  } else if (written.length === 0 && present === 0) {
28000
28132
  } else {
28001
- const envVars = [...new Set(written.flatMap((o) => o.needsEnv || []))].sort();
28002
- if (envVars.length) {
28003
- rep.warn(`${chalk.yellow("Set these env vars \u2014 their values were never uploaded:")} ${envVars.join(", ")}`);
28133
+ const needs = collectEnvNeeds(written);
28134
+ if (needs.length) {
28135
+ const [head, ...rest] = renderEnvNeeds(needs, written.length);
28136
+ rep.warn(head);
28137
+ for (const line of rest) console.log(line);
28004
28138
  }
28005
28139
  }
28006
28140
  } catch (err) {
28007
- rep.warn(`${chalk.yellow("Could not install account MCPs")} \u2014 ${err instanceof Error ? err.message : err}`);
28008
- console.log(` ${chalk.dim("Re-run `chat-recall toolkit pull` later.")}`);
28141
+ rep.warn(`${chalk2.yellow("Could not install account MCPs")} \u2014 ${err instanceof Error ? err.message : err}`);
28142
+ console.log(` ${chalk2.dim("Re-run `chat-recall toolkit pull` later.")}`);
28009
28143
  }
28010
28144
  }
28011
28145
  if (deadTarget) {
28012
- console.log(chalk.bold("Skipping first sync \u2014 the saved server is not serving chat-recall"));
28013
- console.log(` ${chalk.dim(`Re-point with the two commands above, then run ${chalk.bold("chat-recall sync")}.`)}`);
28146
+ console.log(chalk2.bold("Skipping first sync \u2014 the saved server is not serving chat-recall"));
28147
+ console.log(` ${chalk2.dim(`Re-point with the two commands above, then run ${chalk2.bold("chat-recall sync")}.`)}`);
28014
28148
  } else if (!options.skipSync && firstTarget()) {
28015
28149
  const { watchFirstSync: watchFirstSync2, spawnDetachedSync: spawnDetachedSync2, handOffLine: handOffLine2, progressLine: renderProgress } = await Promise.resolve().then(() => (init_init_progress(), init_progress_exports));
28016
28150
  const { readCollectorHealth: readCollectorHealth2 } = await Promise.resolve().then(() => (init_collector_health(), collector_health_exports));
28017
- const label = (text) => `${chalk.bold("Indexing")} ${text}`;
28151
+ const label = (text) => `${chalk2.bold("Indexing")} ${text}`;
28018
28152
  const tty = process.stdout.isTTY === true;
28019
28153
  const outcome = await watchFirstSync2({
28020
28154
  startWorker: () => spawnDetachedSync2(),
@@ -28032,22 +28166,22 @@ program.command("init").description("Set up chat-recall: connect to your server,
28032
28166
  });
28033
28167
  if (tty) process.stdout.write("\r");
28034
28168
  if (outcome.finished) {
28035
- console.log(label(chalk.green(renderProgress(outcome.snapshot))));
28169
+ console.log(label(chalk2.green(renderProgress(outcome.snapshot))));
28036
28170
  } else {
28037
- console.log(label(chalk.dim(handOffLine2(outcome.snapshot))));
28038
- console.log(` ${chalk.dim("watch it with: " + chalk.bold("chat-recall status"))}`);
28171
+ console.log(label(chalk2.dim(handOffLine2(outcome.snapshot))));
28172
+ console.log(` ${chalk2.dim("watch it with: " + chalk2.bold("chat-recall status"))}`);
28039
28173
  }
28040
28174
  const failed = Object.entries(readCollectorHealth2()?.targets ?? {}).filter(([, t]) => (t.failures ?? 0) > 0 && t.lastError);
28041
28175
  for (const [server, t] of failed) {
28042
- console.log(` ${chalk.yellow("sync error")} ${chalk.dim(server)}: ${t.lastError}`);
28176
+ console.log(` ${chalk2.yellow("sync error")} ${chalk2.dim(server)}: ${t.lastError}`);
28043
28177
  }
28044
28178
  } else if (!options.skipSync) {
28045
28179
  } else {
28046
28180
  }
28047
28181
  void options.skipService;
28048
- rep.done(chalk.green.bold("\u2713 Done"));
28182
+ rep.done(chalk2.green.bold("\u2713 Done"));
28049
28183
  } catch (err) {
28050
- console.error(chalk.red("Error:"), err);
28184
+ console.error(chalk2.red("Error:"), err);
28051
28185
  process.exit(1);
28052
28186
  }
28053
28187
  });
@@ -28057,57 +28191,57 @@ program.command("install-skills").description("Install the chat-recall skills in
28057
28191
  const r2 = mod.uninstallSkills();
28058
28192
  const hits2 = r2.perTarget.filter((t) => t.removed.length > 0);
28059
28193
  if (!hits2.length) {
28060
- console.log(chalk.dim("No chat-recall skills were installed."));
28194
+ console.log(chalk2.dim("No chat-recall skills were installed."));
28061
28195
  return;
28062
28196
  }
28063
- for (const t of hits2) console.log(`${chalk.green("\u2713")} ${t.label}: removed ${t.removed.length} skill(s)`);
28197
+ for (const t of hits2) console.log(`${chalk2.green("\u2713")} ${t.label}: removed ${t.removed.length} skill(s)`);
28064
28198
  return;
28065
28199
  }
28066
28200
  const r = mod.installSkills({ onlyAvailable: !opts.allTools });
28067
28201
  const names = mod.bundledSkillNames();
28068
- console.log(chalk.bold(`Installing ${names.length} chat-recall skill(s) (v${r.version}):`), chalk.dim(names.join(", ")));
28202
+ console.log(chalk2.bold(`Installing ${names.length} chat-recall skill(s) (v${r.version}):`), chalk2.dim(names.join(", ")));
28069
28203
  const hits = r.perTarget.filter((t) => t.installed.length > 0);
28070
28204
  if (!hits.length) {
28071
- console.log(chalk.yellow("No supported AI tools detected. Use --all-tools to install anyway."));
28205
+ console.log(chalk2.yellow("No supported AI tools detected. Use --all-tools to install anyway."));
28072
28206
  return;
28073
28207
  }
28074
- for (const t of hits) console.log(`${chalk.green("\u2713")} ${t.label}: ${t.installed.length} \u2192 ${chalk.dim(t.dir)}`);
28208
+ for (const t of hits) console.log(`${chalk2.green("\u2713")} ${t.label}: ${t.installed.length} \u2192 ${chalk2.dim(t.dir)}`);
28075
28209
  const userOwned = [...new Set(r.perTarget.flatMap((t) => t.skippedUserOwned))];
28076
- if (userOwned.length) console.log(chalk.yellow(`Kept your existing skill(s) of the same name: ${userOwned.join(", ")}`));
28210
+ if (userOwned.length) console.log(chalk2.yellow(`Kept your existing skill(s) of the same name: ${userOwned.join(", ")}`));
28077
28211
  });
28078
28212
  program.command("index").description("Collect local sessions and ship them to your server (alias for sync)").option("-f, --force", "Ignore the per-server ledger and re-ship everything", false).action(async (options) => {
28079
28213
  try {
28080
28214
  const { syncSessions: syncSessions2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
28081
28215
  const r = await syncSessions2(options.force ? { useLedger: false } : {});
28082
- console.log(chalk.green(`\u2713 Synced ${r.uploaded} session(s), ${r.items} item(s)`) + chalk.dim(` \u2014 ${r.links} links, ${r.derived} derived rows, ${r.kgTriples} KG triples, ${r.skipped} skipped, ${r.redactions} secrets redacted, ${r.findings} secret findings${r.scanned ? ` (scanned ${r.scanned} in ${r.scanMs}ms)` : ""}`));
28216
+ console.log(chalk2.green(`\u2713 Synced ${r.uploaded} session(s), ${r.items} item(s)`) + chalk2.dim(` \u2014 ${r.links} links, ${r.derived} derived rows, ${r.kgTriples} KG triples, ${r.skipped} skipped, ${r.redactions} secrets redacted, ${r.findings} secret findings${r.scanned ? ` (scanned ${r.scanned} in ${r.scanMs}ms)` : ""}`));
28083
28217
  } catch (err) {
28084
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28218
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28085
28219
  process.exit(1);
28086
28220
  }
28087
28221
  });
28088
28222
  program.command("repair [sessionIds...]").description("Rebuild sessions an upstream tool truncated in place (e.g. Claude Code resume rewrite) from the shrink-protected raw archive. DRY-RUN by default \u2014 pass --apply to write.").option("--all", "Scan the recency window and repair EVERY damaged session (no ids needed)", false).option("--since-hours <n>", "With --all: only scan sessions modified in the last N hours", "72").option("--apply", "Actually write the recovered conversations to the server(s). Without this, nothing is mutated.", false).option("--server <url>", "Target ONLY this server (e.g. the SaaS), instead of every configured sync endpoint").option("--force", "Push even to servers that already look full", false).option("-v, --verbose", "Per-session detail", false).action(async (sessionIds, options) => {
28089
28223
  try {
28090
- const mode = options.apply ? chalk.red("APPLY (writing to server)") : chalk.cyan("DRY-RUN (read-only)");
28224
+ const mode = options.apply ? chalk2.red("APPLY (writing to server)") : chalk2.cyan("DRY-RUN (read-only)");
28091
28225
  const render = (r) => {
28092
28226
  const head = `${r.sessionId.slice(0, 8)}\u2026`;
28093
28227
  if (r.status === "no-archive") {
28094
- console.log(chalk.yellow(` ? ${head} no archive found`));
28228
+ console.log(chalk2.yellow(` ? ${head} no archive found`));
28095
28229
  return;
28096
28230
  }
28097
28231
  if (r.status === "already-full") {
28098
- console.log(chalk.dim(` = ${head} already full (${r.fullestMessages} msgs)`));
28232
+ console.log(chalk2.dim(` = ${head} already full (${r.fullestMessages} msgs)`));
28099
28233
  return;
28100
28234
  }
28101
28235
  if (r.status === "error") {
28102
- console.log(chalk.red(` \u2717 ${head} ${r.note}`));
28236
+ console.log(chalk2.red(` \u2717 ${head} ${r.note}`));
28103
28237
  return;
28104
28238
  }
28105
28239
  const verb = r.status === "would-repair" ? "would recover" : "recovered";
28106
- console.log(chalk.green(` ${r.status === "would-repair" ? "\xB7" : "+"} ${head} ${verb} ${r.fullestMessages} msgs`) + chalk.dim(` from ${r.fullestSource}`));
28107
- for (const p of r.pushed) console.log(chalk.dim(` ${p.server}: ${p.before} \u2192 ${p.after}`));
28240
+ console.log(chalk2.green(` ${r.status === "would-repair" ? "\xB7" : "+"} ${head} ${verb} ${r.fullestMessages} msgs`) + chalk2.dim(` from ${r.fullestSource}`));
28241
+ for (const p of r.pushed) console.log(chalk2.dim(` ${p.server}: ${p.before} \u2192 ${p.after}`));
28108
28242
  };
28109
28243
  if (options.all) {
28110
- console.log(chalk.bold(`repair --all \xB7 ${mode} \xB7 window ${options.sinceHours}h`));
28244
+ console.log(chalk2.bold(`repair --all \xB7 ${mode} \xB7 window ${options.sinceHours}h`));
28111
28245
  const { repairAll: repairAll2 } = await Promise.resolve().then(() => (init_repair(), repair_exports));
28112
28246
  const report = await repairAll2({
28113
28247
  sinceHours: Number(options.sinceHours) || 72,
@@ -28118,24 +28252,24 @@ program.command("repair [sessionIds...]").description("Rebuild sessions an upstr
28118
28252
  report.candidates.forEach(render);
28119
28253
  const fixed2 = report.candidates.filter((r) => r.status === "repaired").length;
28120
28254
  const would2 = report.candidates.filter((r) => r.status === "would-repair").length;
28121
- console.log(chalk.bold(
28255
+ console.log(chalk2.bold(
28122
28256
  `Scanned ${report.scanned} session(s) on ${report.discoveryServer}; ` + (options.apply ? `repaired ${fixed2}.` : `${would2} damaged, would repair. Re-run with --apply to write.`)
28123
28257
  ));
28124
28258
  return;
28125
28259
  }
28126
28260
  if (!sessionIds || sessionIds.length === 0) {
28127
- console.error(chalk.red("Give session ids, or use --all. e.g. `chat-recall repair <uuid>` or `chat-recall repair --all`."));
28261
+ console.error(chalk2.red("Give session ids, or use --all. e.g. `chat-recall repair <uuid>` or `chat-recall repair --all`."));
28128
28262
  process.exit(1);
28129
28263
  }
28130
- console.log(chalk.bold(`repair \xB7 ${mode}`));
28264
+ console.log(chalk2.bold(`repair \xB7 ${mode}`));
28131
28265
  const { repairSessions: repairSessions2 } = await Promise.resolve().then(() => (init_repair(), repair_exports));
28132
28266
  const results = await repairSessions2(sessionIds, { dryRun: !options.apply, force: options.force, verbose: options.verbose, server: options.server });
28133
28267
  results.forEach(render);
28134
28268
  const fixed = results.filter((r) => r.status === "repaired").length;
28135
28269
  const would = results.filter((r) => r.status === "would-repair").length;
28136
- console.log(chalk.bold(options.apply ? `Repaired ${fixed} session(s).` : `${would} session(s) would be repaired. Re-run with --apply to write.`));
28270
+ console.log(chalk2.bold(options.apply ? `Repaired ${fixed} session(s).` : `${would} session(s) would be repaired. Re-run with --apply to write.`));
28137
28271
  } catch (err) {
28138
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28272
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28139
28273
  process.exit(1);
28140
28274
  }
28141
28275
  });
@@ -28145,11 +28279,11 @@ program.command("search <query>").description("Search your server for relevant s
28145
28279
  const remote = await serverPost("/api/search", { query, topK, projectFilter: options.project });
28146
28280
  const results = remote.results;
28147
28281
  if (results.length === 0) {
28148
- console.log(chalk.yellow("No matching sessions found."));
28282
+ console.log(chalk2.yellow("No matching sessions found."));
28149
28283
  process.exit(0);
28150
28284
  }
28151
28285
  console.log();
28152
- console.log(`${chalk.bold("Results for:")} "${query}"`);
28286
+ console.log(`${chalk2.bold("Results for:")} "${query}"`);
28153
28287
  console.log();
28154
28288
  const tiers = tierAll(results);
28155
28289
  for (let i = 0; i < results.length; i++) {
@@ -28158,13 +28292,13 @@ program.command("search <query>").description("Search your server for relevant s
28158
28292
  if (projectPath.length > 50) projectPath = "..." + projectPath.slice(-47);
28159
28293
  let title = (result.firstPrompt || "").replace(/\n/g, " ").trim();
28160
28294
  if (title.length > 80) title = title.slice(0, 80) + "...";
28161
- console.log(`${chalk.bold.cyan(`#${i + 1}`)} ${title}`);
28162
- if (projectPath) console.log(` ${chalk.dim("Project:")} ${projectPath}`);
28163
- console.log(` ${chalk.dim("Match:")} ${matchLabel(tiers[i])}`);
28295
+ console.log(`${chalk2.bold.cyan(`#${i + 1}`)} ${title}`);
28296
+ if (projectPath) console.log(` ${chalk2.dim("Project:")} ${projectPath}`);
28297
+ console.log(` ${chalk2.dim("Match:")} ${matchLabel(tiers[i])}`);
28164
28298
  if (result.summary) {
28165
28299
  let summary = result.summary.replace(/\n/g, " ").trim();
28166
28300
  if (summary.length > 200) summary = summary.slice(0, 200) + "...";
28167
- console.log(` ${chalk.yellow("Summary:")} ${summary}`);
28301
+ console.log(` ${chalk2.yellow("Summary:")} ${summary}`);
28168
28302
  }
28169
28303
  const chunk = (result.matchedChunks || []).find(
28170
28304
  (c) => c.chunkType !== "summary" && c.chunkType !== "first_prompt"
@@ -28173,16 +28307,16 @@ program.command("search <query>").description("Search your server for relevant s
28173
28307
  const label = chunk.chunkType === "assistant" ? "Discussed" : chunk.chunkType === "user_context" ? "Asked about" : chunk.chunkType === "tool_result" ? "Tool result" : "Context";
28174
28308
  let text = chunk.text.replace(/\n/g, " ").trim();
28175
28309
  if (text.length > 150) text = text.slice(0, 150) + "...";
28176
- console.log(` ${chalk.magenta(label + ":")} ${text}`);
28310
+ console.log(` ${chalk2.magenta(label + ":")} ${text}`);
28177
28311
  }
28178
28312
  {
28179
28313
  const rc = resumeCommandFor(result.sessionId);
28180
- if (rc) console.log(` ${chalk.green("Resume:")} ${rc}`);
28314
+ if (rc) console.log(` ${chalk2.green("Resume:")} ${rc}`);
28181
28315
  }
28182
28316
  console.log();
28183
28317
  }
28184
28318
  } catch (err) {
28185
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28319
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28186
28320
  process.exit(1);
28187
28321
  }
28188
28322
  });
@@ -28190,7 +28324,7 @@ program.command("status").description("Show your server's sync status and statis
28190
28324
  try {
28191
28325
  const status = await serverGet("/api/status");
28192
28326
  const sync = await serverGet("/api/status/sync");
28193
- console.log(chalk.bold("Chat-Recall Server Status"));
28327
+ console.log(chalk2.bold("Chat-Recall Server Status"));
28194
28328
  console.log();
28195
28329
  console.log(`Synced sessions: ${sync.sessions}`);
28196
28330
  console.log(`FTS5 chunks: ${status.totalChunks}`);
@@ -28202,34 +28336,34 @@ program.command("status").description("Show your server's sync status and statis
28202
28336
  const types = Object.entries(sync.sourceTypes).filter(([, n]) => Number(n) > 0);
28203
28337
  if (types.length > 0) {
28204
28338
  console.log();
28205
- console.log(chalk.bold("By source type:"));
28339
+ console.log(chalk2.bold("By source type:"));
28206
28340
  for (const [type, n] of types) console.log(` ${type}: ${n} items`);
28207
28341
  }
28208
28342
  const projects = Object.entries(status.projects || {}).sort((a, b) => b[1] - a[1]).slice(0, 10);
28209
28343
  if (projects.length > 0) {
28210
28344
  console.log();
28211
- console.log(chalk.bold("Top projects:"));
28345
+ console.log(chalk2.bold("Top projects:"));
28212
28346
  for (const [proj, n] of projects) console.log(` ${proj}: ${n} sessions`);
28213
28347
  }
28214
28348
  } catch (err) {
28215
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28349
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28216
28350
  process.exit(1);
28217
28351
  }
28218
28352
  });
28219
28353
  program.command("optimize").description("Deprecated: storage is server-side now, nothing to optimize locally").action(() => {
28220
- console.log(chalk.dim("Storage is server-side now; there is no local index to optimize."));
28221
- console.log(chalk.dim("Run `chat-recall sync` to ship sessions; the server manages its own storage."));
28354
+ console.log(chalk2.dim("Storage is server-side now; there is no local index to optimize."));
28355
+ console.log(chalk2.dim("Run `chat-recall sync` to ship sessions; the server manages its own storage."));
28222
28356
  process.exit(0);
28223
28357
  });
28224
28358
  program.command("show <session_id>").description("Show conversation content from a session (requires login)").option("-m, --messages <number>", "Number of messages to show", "10").option("-f, --full", "Show full conversation (all messages)", false).action(async (sessionId, options) => {
28225
28359
  try {
28226
28360
  const soft = await serverGetSoft(`/api/conversations/${encodeURIComponent(sessionId)}?limit=0`);
28227
28361
  if (!soft.data || soft.data.messages.length === 0) {
28228
- console.log(chalk.yellow(soft.message || `Session not found: ${sessionId}`));
28362
+ console.log(chalk2.yellow(soft.message || `Session not found: ${sessionId}`));
28229
28363
  process.exit(soft.status === 404 ? 1 : 0);
28230
28364
  }
28231
28365
  const messagesList = soft.data.messages;
28232
- console.log(chalk.bold("Session:"), sessionId);
28366
+ console.log(chalk2.bold("Session:"), sessionId);
28233
28367
  console.log();
28234
28368
  const maxMessages = parseInt(options.messages, 10);
28235
28369
  const displayMessages = options.full ? messagesList : messagesList.slice(0, maxMessages);
@@ -28237,22 +28371,22 @@ program.command("show <session_id>").description("Show conversation content from
28237
28371
  let text = msg.content;
28238
28372
  if (!options.full && text.length > 1e3) text = text.slice(0, 1e3) + "...";
28239
28373
  if (msg.role === "user") {
28240
- console.log(`${chalk.bold.blue("User")} ${chalk.dim(`(line ${msg.line})`)}`);
28374
+ console.log(`${chalk2.bold.blue("User")} ${chalk2.dim(`(line ${msg.line})`)}`);
28241
28375
  } else if (msg.role === "assistant") {
28242
- console.log(`${chalk.bold.green("Assistant")} ${chalk.dim(`(line ${msg.line})`)}`);
28376
+ console.log(`${chalk2.bold.green("Assistant")} ${chalk2.dim(`(line ${msg.line})`)}`);
28243
28377
  } else {
28244
- console.log(chalk.bold.yellow(msg.role));
28378
+ console.log(chalk2.bold.yellow(msg.role));
28245
28379
  }
28246
28380
  console.log(text);
28247
28381
  console.log();
28248
28382
  }
28249
- console.log(chalk.dim(`Showing ${displayMessages.length} of ${messagesList.length} messages. Use --full for the complete conversation.`));
28383
+ console.log(chalk2.dim(`Showing ${displayMessages.length} of ${messagesList.length} messages. Use --full for the complete conversation.`));
28250
28384
  {
28251
28385
  const rc = resumeCommandFor(sessionId);
28252
- if (rc) console.log(chalk.dim(`Resume: ${rc}`));
28386
+ if (rc) console.log(chalk2.dim(`Resume: ${rc}`));
28253
28387
  }
28254
28388
  } catch (err) {
28255
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28389
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28256
28390
  process.exit(1);
28257
28391
  }
28258
28392
  });
@@ -28261,9 +28395,9 @@ memory.command("index").description("Collect local sessions and ship them to you
28261
28395
  try {
28262
28396
  const { syncSessions: syncSessions2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
28263
28397
  const r = await syncSessions2(options.force ? { useLedger: false } : {});
28264
- console.log(chalk.green(`\u2713 Synced ${r.uploaded} session(s), ${r.items} item(s)`) + chalk.dim(` \u2014 ${r.links} links, ${r.derived} derived rows, ${r.kgTriples} KG triples, ${r.skipped} skipped, ${r.redactions} secrets redacted, ${r.findings} secret findings${r.scanned ? ` (scanned ${r.scanned} in ${r.scanMs}ms)` : ""}`));
28398
+ console.log(chalk2.green(`\u2713 Synced ${r.uploaded} session(s), ${r.items} item(s)`) + chalk2.dim(` \u2014 ${r.links} links, ${r.derived} derived rows, ${r.kgTriples} KG triples, ${r.skipped} skipped, ${r.redactions} secrets redacted, ${r.findings} secret findings${r.scanned ? ` (scanned ${r.scanned} in ${r.scanMs}ms)` : ""}`));
28265
28399
  } catch (err) {
28266
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28400
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28267
28401
  process.exit(1);
28268
28402
  }
28269
28403
  });
@@ -28274,55 +28408,55 @@ memory.command("search <query>").description("Search across all memory types on
28274
28408
  const remote = await serverPost("/api/memory/search", { query, topK, sourceTypes });
28275
28409
  const results = remote.results;
28276
28410
  if (results.length === 0) {
28277
- console.log(chalk.yellow("No matching results found."));
28411
+ console.log(chalk2.yellow("No matching results found."));
28278
28412
  process.exit(0);
28279
28413
  }
28280
28414
  console.log();
28281
- console.log(`${chalk.bold("Memory search:")} "${query}"`);
28415
+ console.log(`${chalk2.bold("Memory search:")} "${query}"`);
28282
28416
  console.log();
28283
28417
  const tiers = tierAll(results);
28284
28418
  for (let i = 0; i < results.length; i++) {
28285
28419
  const r = results[i];
28286
28420
  const typeColor = {
28287
- session: chalk.blue,
28288
- plan: chalk.green,
28289
- task: chalk.yellow,
28290
- claude_md: chalk.magenta,
28291
- history: chalk.cyan,
28292
- paste: chalk.gray,
28293
- diary: chalk.redBright
28294
- }[r.sourceType] || chalk.white;
28421
+ session: chalk2.blue,
28422
+ plan: chalk2.green,
28423
+ task: chalk2.yellow,
28424
+ claude_md: chalk2.magenta,
28425
+ history: chalk2.cyan,
28426
+ paste: chalk2.gray,
28427
+ diary: chalk2.redBright
28428
+ }[r.sourceType] || chalk2.white;
28295
28429
  const typeBadge = typeColor(`[${r.sourceType}]`);
28296
28430
  let title = (r.title || "").replace(/\n/g, " ").trim();
28297
28431
  if (title.length > 70) title = title.slice(0, 67) + "...";
28298
- console.log(`${chalk.bold.cyan(`#${i + 1}`)} ${typeBadge} ${title}`);
28432
+ console.log(`${chalk2.bold.cyan(`#${i + 1}`)} ${typeBadge} ${title}`);
28299
28433
  if (r.projectPath) {
28300
28434
  let pp = r.projectPath;
28301
28435
  if (pp.length > 50) pp = "..." + pp.slice(-47);
28302
- console.log(` ${chalk.dim("Project:")} ${pp}`);
28436
+ console.log(` ${chalk2.dim("Project:")} ${pp}`);
28303
28437
  }
28304
- const typeSuffix = r.chunkType ? ` ${chalk.dim("Type:")} ${r.chunkType}` : "";
28305
- console.log(` ${chalk.dim("Match:")} ${matchLabel(tiers[i])}${typeSuffix}`);
28438
+ const typeSuffix = r.chunkType ? ` ${chalk2.dim("Type:")} ${r.chunkType}` : "";
28439
+ console.log(` ${chalk2.dim("Match:")} ${matchLabel(tiers[i])}${typeSuffix}`);
28306
28440
  let preview = (r.text || "").replace(/\n/g, " ").trim();
28307
28441
  if (preview.length > 150) preview = preview.slice(0, 147) + "...";
28308
28442
  if (preview) console.log(` ${preview}`);
28309
28443
  if (r.sourceType === "session") {
28310
28444
  {
28311
28445
  const rc = resumeCommandFor(r.itemId);
28312
- if (rc) console.log(` ${chalk.green("Resume:")} ${rc}`);
28446
+ if (rc) console.log(` ${chalk2.green("Resume:")} ${rc}`);
28313
28447
  }
28314
28448
  }
28315
28449
  console.log();
28316
28450
  }
28317
28451
  } catch (err) {
28318
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28452
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28319
28453
  process.exit(1);
28320
28454
  }
28321
28455
  });
28322
28456
  memory.command("status").description("Show your server's memory statistics across all source types (requires login)").action(async () => {
28323
28457
  try {
28324
28458
  const status = await serverGet("/api/memory/status");
28325
- console.log(chalk.bold("Memory System Status"));
28459
+ console.log(chalk2.bold("Memory System Status"));
28326
28460
  console.log();
28327
28461
  console.log(`Total items: ${status.totalItems}`);
28328
28462
  console.log(`FTS5 chunks: ${status.totalChunks}`);
@@ -28330,7 +28464,7 @@ memory.command("status").description("Show your server's memory statistics acros
28330
28464
  const bySource = Object.entries(status.bySourceType || {});
28331
28465
  if (bySource.length > 0) {
28332
28466
  console.log();
28333
- console.log(chalk.bold("By source type:"));
28467
+ console.log(chalk2.bold("By source type:"));
28334
28468
  for (const [type, data] of bySource) {
28335
28469
  console.log(` ${type}: ${data.items} items, ${data.chunks} chunks`);
28336
28470
  }
@@ -28338,14 +28472,14 @@ memory.command("status").description("Show your server's memory statistics acros
28338
28472
  const byTool = Object.entries(status.bySourceAndTool || {});
28339
28473
  if (byTool.length > 0) {
28340
28474
  console.log();
28341
- console.log(chalk.bold("By source type and tool:"));
28475
+ console.log(chalk2.bold("By source type and tool:"));
28342
28476
  for (const [type, tools] of byTool) {
28343
28477
  const parts = Object.entries(tools).map(([t, n]) => `${t}: ${n}`).join(", ");
28344
28478
  console.log(` ${type}: ${parts}`);
28345
28479
  }
28346
28480
  }
28347
28481
  } catch (err) {
28348
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28482
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28349
28483
  process.exit(1);
28350
28484
  }
28351
28485
  });
@@ -28353,20 +28487,20 @@ memory.command("links <source_type> <item_id>").description("Show relationships
28353
28487
  try {
28354
28488
  const { links } = await serverGet(`/api/memory/links/${encodeURIComponent(sourceType)}/${encodeURIComponent(itemId)}`);
28355
28489
  if (links.length === 0) {
28356
- console.log(chalk.yellow("No links found."));
28490
+ console.log(chalk2.yellow("No links found."));
28357
28491
  return;
28358
28492
  }
28359
- console.log(chalk.bold(`Links for ${sourceType}:${itemId}`));
28493
+ console.log(chalk2.bold(`Links for ${sourceType}:${itemId}`));
28360
28494
  console.log();
28361
28495
  for (const link of links) {
28362
28496
  const direction = link.source_id === itemId ? "->" : "<-";
28363
28497
  const otherType = link.source_id === itemId ? link.target_type : link.source_type;
28364
28498
  const otherId = link.source_id === itemId ? link.target_id : link.source_id;
28365
28499
  const confidence = Math.round(link.confidence * 100);
28366
- console.log(` ${direction} ${chalk.bold(otherType)}:${otherId.slice(0, 20)} [${link.link_type}] (${confidence}%)`);
28500
+ console.log(` ${direction} ${chalk2.bold(otherType)}:${otherId.slice(0, 20)} [${link.link_type}] (${confidence}%)`);
28367
28501
  }
28368
28502
  } catch (err) {
28369
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28503
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28370
28504
  process.exit(1);
28371
28505
  }
28372
28506
  });
@@ -28378,27 +28512,27 @@ memory.command("wake-up").description("Generate wake-up context (high-importance
28378
28512
  identity = readFileSync49(identityFile, "utf-8").trim();
28379
28513
  }
28380
28514
  const lines = [
28381
- chalk.bold("# Wake-Up Context"),
28382
- chalk.bold("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
28515
+ chalk2.bold("# Wake-Up Context"),
28516
+ chalk2.bold("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
28383
28517
  "",
28384
- chalk.bold("## Identity"),
28518
+ chalk2.bold("## Identity"),
28385
28519
  identity,
28386
28520
  ""
28387
28521
  ];
28388
28522
  const qs = options.project ? `?project_filter=${encodeURIComponent(options.project)}` : "";
28389
28523
  const wake = await serverGet(`/api/memory/wake-up${qs}`);
28390
28524
  if (wake.highFacts.length > 0) {
28391
- lines.push(chalk.bold("## High-Importance Facts"));
28525
+ lines.push(chalk2.bold("## High-Importance Facts"));
28392
28526
  for (const fact of wake.highFacts) {
28393
28527
  lines.push(` [${fact.type}] ${fact.text}`);
28394
28528
  }
28395
28529
  lines.push("");
28396
28530
  }
28397
28531
  if (wake.kg.facts.length > 0) {
28398
- lines.push(chalk.bold("## Knowledge Graph"));
28532
+ lines.push(chalk2.bold("## Knowledge Graph"));
28399
28533
  const { entities, current_facts } = wake.kg.stats;
28400
28534
  if (entities !== void 0 || current_facts !== void 0) {
28401
- lines.push(chalk.dim(`${entities ?? 0} entities, ${current_facts ?? 0} current facts`));
28535
+ lines.push(chalk2.dim(`${entities ?? 0} entities, ${current_facts ?? 0} current facts`));
28402
28536
  }
28403
28537
  for (const fact of wake.kg.facts) {
28404
28538
  lines.push(` ${fact.subject} \u2192 ${fact.predicate} \u2192 ${fact.object}`);
@@ -28407,32 +28541,75 @@ memory.command("wake-up").description("Generate wake-up context (high-importance
28407
28541
  }
28408
28542
  console.log(lines.join("\n"));
28409
28543
  } catch (err) {
28410
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
28544
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
28411
28545
  process.exit(1);
28412
28546
  }
28413
28547
  });
28414
28548
  program.command("escalate [sessionId]").description("Escalate a finished session's learnings (decisions, user corrections, outcome) into the knowledge graph (requires login)").option("--latest", "Pick the most recent synced session of the current project").option("-p, --project <name>", "Knowledge-graph entity name for the facts (default: project folder name)").option("--dry-run", "Extract and print the learnings without writing them").action(async (sessionId, opts) => {
28415
28549
  if (!sessionId && !opts.latest) {
28416
- console.error(chalk.red("Pass a session id or --latest."));
28550
+ console.error(chalk2.red("Pass a session id or --latest."));
28417
28551
  process.exit(1);
28418
28552
  }
28419
28553
  const target = firstTarget();
28420
28554
  if (!target) {
28421
- console.log(chalk.dim("escalate: not logged in \u2014 nothing to do. Run `chat-recall login <server-url>` to enable learnings escalation."));
28555
+ console.log(chalk2.dim("escalate: not logged in \u2014 nothing to do. Run `chat-recall login <server-url>` to enable learnings escalation."));
28422
28556
  return;
28423
28557
  }
28424
28558
  try {
28425
28559
  const { runEscalate: runEscalate2 } = await Promise.resolve().then(() => (init_escalate(), escalate_exports));
28426
28560
  await runEscalate2(
28427
- { get: serverGet, getSoft: serverGetSoft, post: serverPost, log: (line) => console.log(chalk.dim(line)) },
28561
+ { get: serverGet, getSoft: serverGetSoft, post: serverPost, log: (line) => console.log(chalk2.dim(line)) },
28428
28562
  { sessionId, latest: opts.latest, project: opts.project, dryRun: opts.dryRun, cwd: process.cwd() }
28429
28563
  );
28430
28564
  } catch (err) {
28431
- console.log(chalk.dim(`escalate: skipped \u2014 ${err instanceof Error ? err.message : err}`));
28565
+ console.log(chalk2.dim(`escalate: skipped \u2014 ${err instanceof Error ? err.message : err}`));
28566
+ }
28567
+ });
28568
+ program.command("feedback").argument("<message...>", "what you want to say \u2014 anything at all").description("Send a message to the people who build chat-recall. No account needed.").option("--email <address>", "so we can reply; optional").action(async (words, opts) => {
28569
+ const message = words.join(" ").trim();
28570
+ if (message.length < 3) {
28571
+ console.error(chalk2.red("Say a little more than that."));
28572
+ process.exitCode = 1;
28573
+ return;
28574
+ }
28575
+ const target = firstTarget();
28576
+ const base2 = target?.base || (process.env.CHAT_RECALL_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
28577
+ try {
28578
+ const res = await fetch(`${base2}/api/contact/feedback`, {
28579
+ method: "POST",
28580
+ headers: {
28581
+ "content-type": "application/json",
28582
+ // Attached when logged in so a reply can find the account. Absent is
28583
+ // fine and expected.
28584
+ ...target ? { authorization: `Bearer ${target.token}` } : {}
28585
+ },
28586
+ body: JSON.stringify({
28587
+ message,
28588
+ email: opts.email ?? null,
28589
+ cliVersion: pkgVersion,
28590
+ os: `${process.platform}-${process.arch}`
28591
+ }),
28592
+ signal: AbortSignal.timeout(1e4)
28593
+ });
28594
+ if (!res.ok) {
28595
+ const body = await res.json().catch(() => ({}));
28596
+ console.error(chalk2.red(`Could not send: ${body.error || res.status}`));
28597
+ process.exitCode = 1;
28598
+ return;
28599
+ }
28600
+ console.log(chalk2.green("Sent. Thank you \u2014 this genuinely gets read."));
28601
+ if (!opts.email) {
28602
+ console.log(chalk2.dim(" Add --email <address> next time if you want a reply."));
28603
+ }
28604
+ } catch (err) {
28605
+ console.error(chalk2.red(`Could not reach ${base2}.`));
28606
+ console.error(chalk2.dim(" Your message, so it is not lost:"));
28607
+ console.error(chalk2.dim(` ${message}`));
28608
+ process.exitCode = 1;
28432
28609
  }
28433
28610
  });
28434
28611
  program.command("doctor").description("Quick health check across login, server, credentials, hooks, MCP server, and codeindex").option("--purge-local", "Remove the legacy local index (cache.db + vector/KG files) left by pre-thin-collector versions \u2014 storage is server-side now. Requires login; keeps credentials, sync ledger, identity, and the audit log.").action(async (opts) => {
28435
- const { existsSync: existsSync57, readFileSync: readFileSync50, statSync: statSync46 } = await import("fs");
28612
+ const { existsSync: existsSync57, readFileSync: readFileSync50, statSync: statSync47 } = await import("fs");
28436
28613
  const { execSync: execSync6 } = await import("child_process");
28437
28614
  const rows = [];
28438
28615
  const note = (ok, label, detail) => rows.push({ ok, label, detail });
@@ -28500,7 +28677,7 @@ program.command("doctor").description("Quick health check across login, server,
28500
28677
  note(false, "Credentials file", `missing \u2014 run \`chat-recall login <server-url>\``);
28501
28678
  } else {
28502
28679
  try {
28503
- const mode = statSync46(credFile).mode & 511;
28680
+ const mode = statSync47(credFile).mode & 511;
28504
28681
  const isWin = process.platform === "win32";
28505
28682
  const safe = isWin || (mode & 63) === 0;
28506
28683
  note(safe, "Credentials file", isWin ? `${credFile} (Windows ACL \u2014 POSIX mode does not apply)` : `${credFile} (mode ${mode.toString(8).padStart(3, "0")}${safe ? "" : " \u2014 should be 600"})`);
@@ -28638,11 +28815,11 @@ program.command("doctor").description("Quick health check across login, server,
28638
28815
  } else if (legacyPresent.length === 0) {
28639
28816
  note(true, "Purge legacy index", "nothing to remove");
28640
28817
  } else {
28641
- const { rmSync: rmSync9 } = await import("fs");
28818
+ const { rmSync: rmSync10 } = await import("fs");
28642
28819
  let removed = 0;
28643
28820
  for (const p of legacyPresent) {
28644
28821
  try {
28645
- rmSync9(p, { recursive: true, force: true });
28822
+ rmSync10(p, { recursive: true, force: true });
28646
28823
  removed++;
28647
28824
  } catch {
28648
28825
  }
@@ -28654,67 +28831,67 @@ program.command("doctor").description("Quick health check across login, server,
28654
28831
  } else {
28655
28832
  note(true, "Legacy local index", "none (clean)");
28656
28833
  }
28657
- console.log(chalk.bold("\nchat-recall doctor\n"));
28834
+ console.log(chalk2.bold("\nchat-recall doctor\n"));
28658
28835
  for (const r of rows) {
28659
- const icon = r.ok ? chalk.green("\u2713") : chalk.yellow("\u2022");
28660
- console.log(` ${icon} ${r.label.padEnd(28)} ${chalk.dim(r.detail || "")}`);
28836
+ const icon = r.ok ? chalk2.green("\u2713") : chalk2.yellow("\u2022");
28837
+ console.log(` ${icon} ${r.label.padEnd(28)} ${chalk2.dim(r.detail || "")}`);
28661
28838
  }
28662
28839
  const failures = rows.filter((r) => !r.ok).length;
28663
28840
  console.log();
28664
28841
  if (failures === 0) {
28665
- console.log(chalk.green("All systems green."));
28842
+ console.log(chalk2.green("All systems green."));
28666
28843
  } else {
28667
- console.log(chalk.dim(`${failures} item${failures === 1 ? "" : "s"} need attention.`));
28844
+ console.log(chalk2.dim(`${failures} item${failures === 1 ? "" : "s"} need attention.`));
28668
28845
  }
28669
28846
  });
28670
28847
  var companions = program.command("companions").description("Manage companion tools (codeindex)");
28671
28848
  companions.command("status").description("Show whether codeindex is installed and what it provides").action(async () => {
28672
28849
  const { checkCodeindexStatus: checkCodeindexStatus2 } = await Promise.resolve().then(() => (init_companions(), companions_exports));
28673
28850
  const s = checkCodeindexStatus2();
28674
- console.log(chalk.bold("codeindex status"));
28851
+ console.log(chalk2.bold("codeindex status"));
28675
28852
  if (s.installed) {
28676
- console.log(` ${chalk.green("\u2713 installed")}`);
28853
+ console.log(` ${chalk2.green("\u2713 installed")}`);
28677
28854
  if (s.path) console.log(` path: ${s.path}`);
28678
28855
  if (s.size) console.log(` size: ${(s.size / 1024 / 1024).toFixed(1)} MB`);
28679
28856
  if (s.version) console.log(` version: ${s.version}`);
28680
28857
  } else {
28681
- console.log(` ${chalk.yellow("not installed")}`);
28858
+ console.log(` ${chalk2.yellow("not installed")}`);
28682
28859
  if (!s.prebuiltAvailable) {
28683
- console.log(` ${chalk.dim(s.unsupportedReason || "no prebuilt binary for this platform")}`);
28860
+ console.log(` ${chalk2.dim(s.unsupportedReason || "no prebuilt binary for this platform")}`);
28684
28861
  } else {
28685
- console.log(` ${chalk.dim(`run \`chat-recall companions install\` to fetch ${s.artifactName}`)}`);
28862
+ console.log(` ${chalk2.dim(`run \`chat-recall companions install\` to fetch ${s.artifactName}`)}`);
28686
28863
  }
28687
28864
  }
28688
28865
  console.log();
28689
- console.log(chalk.dim("codeindex provides code-level lookup as a separate MCP server:"));
28690
- console.log(chalk.dim(" find_symbol, find_callers, get_imports, plan_change,"));
28691
- console.log(chalk.dim(" get_change_impact, analyze (security/dead_code/coupling/cycles/...)"));
28866
+ console.log(chalk2.dim("codeindex provides code-level lookup as a separate MCP server:"));
28867
+ console.log(chalk2.dim(" find_symbol, find_callers, get_imports, plan_change,"));
28868
+ console.log(chalk2.dim(" get_change_impact, analyze (security/dead_code/coupling/cycles/...)"));
28692
28869
  });
28693
28870
  companions.command("install").description("Install codeindex (downloads from munhq/codeindex GitHub release)").option("--force", "Re-download even if already installed").action(async (opts) => {
28694
28871
  const { installCodeindex: installCodeindex2, registerCodeindexMcp: registerCodeindexMcp2, CODEINDEX_BIN_PATH: CODEINDEX_BIN_PATH2 } = await Promise.resolve().then(() => (init_companions(), companions_exports));
28695
28872
  try {
28696
28873
  const result = await installCodeindex2({ force: opts.force });
28697
28874
  if (!result.prebuiltAvailable && !result.installed) {
28698
- console.log(chalk.yellow(`codeindex: ${result.unsupportedReason}`));
28699
- console.log(chalk.dim(" Build from source: https://github.com/munhq/codeindex#install"));
28875
+ console.log(chalk2.yellow(`codeindex: ${result.unsupportedReason}`));
28876
+ console.log(chalk2.dim(" Build from source: https://github.com/munhq/codeindex#install"));
28700
28877
  return;
28701
28878
  }
28702
28879
  if (result.installed) {
28703
28880
  const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
28704
- console.log(chalk.green(`\u2713 codeindex installed (${sizeMb}) \u2192 ${result.path}`));
28881
+ console.log(chalk2.green(`\u2713 codeindex installed (${sizeMb}) \u2192 ${result.path}`));
28705
28882
  const mcpJsonPath = join60(homedir22(), ".mcp.json");
28706
28883
  const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
28707
- if (reg.added) console.log(chalk.dim(` Registered as MCP server in ${mcpJsonPath}`));
28708
- else console.log(chalk.dim(` Already registered as MCP server`));
28884
+ if (reg.added) console.log(chalk2.dim(` Registered as MCP server in ${mcpJsonPath}`));
28885
+ else console.log(chalk2.dim(` Already registered as MCP server`));
28709
28886
  }
28710
28887
  } catch (err) {
28711
28888
  const msg = String(err);
28712
- console.error(chalk.red(`codeindex install failed: ${msg}`));
28889
+ console.error(chalk2.red(`codeindex install failed: ${msg}`));
28713
28890
  if (msg.includes("404")) {
28714
- console.error(chalk.dim(" The codeindex release is currently private. Either authenticate `gh`"));
28715
- console.error(chalk.dim(" to munhq/codeindex, or build from source: https://github.com/munhq/codeindex"));
28891
+ console.error(chalk2.dim(" The codeindex release is currently private. Either authenticate `gh`"));
28892
+ console.error(chalk2.dim(" to munhq/codeindex, or build from source: https://github.com/munhq/codeindex"));
28716
28893
  } else {
28717
- console.error(chalk.dim(" This does not block chat-recall \u2014 it just disables code-level lookup."));
28894
+ console.error(chalk2.dim(" This does not block chat-recall \u2014 it just disables code-level lookup."));
28718
28895
  }
28719
28896
  process.exit(1);
28720
28897
  }
@@ -28722,15 +28899,15 @@ companions.command("install").description("Install codeindex (downloads from mun
28722
28899
  companions.command("uninstall").description("Remove the codeindex binary chat-recall installed (does not touch your other tools)").action(async () => {
28723
28900
  const { uninstallCodeindex: uninstallCodeindex2, unregisterCodeindexMcp: unregisterCodeindexMcp2 } = await Promise.resolve().then(() => (init_companions(), companions_exports));
28724
28901
  const r = uninstallCodeindex2();
28725
- if (r.removed) console.log(chalk.green(`\u2713 Removed ${r.path}`));
28726
- else console.log(chalk.dim(` Nothing to remove at ${r.path}`));
28902
+ if (r.removed) console.log(chalk2.green(`\u2713 Removed ${r.path}`));
28903
+ else console.log(chalk2.dim(` Nothing to remove at ${r.path}`));
28727
28904
  const mcpJsonPath = join60(homedir22(), ".mcp.json");
28728
28905
  const u = unregisterCodeindexMcp2(mcpJsonPath);
28729
- if (u.removed) console.log(chalk.dim(` Unregistered MCP server from ${mcpJsonPath}`));
28906
+ if (u.removed) console.log(chalk2.dim(` Unregistered MCP server from ${mcpJsonPath}`));
28730
28907
  });
28731
28908
  program.command("verify").description("Check that the server holds everything this machine has, per session").option("--deep", "Compare per-session content size against each server", false).option("--records", "With --deep: also diff the exact record (uuid) sets \u2014 proof, not inference", false).option("--since-hours <n>", "Only check sessions modified in the last N hours (default: all)").option("--repair", "Re-ship every STRANDED session found (clears its ledger cursor)", false).option("--json", "Machine-readable output", false).action(async (opts) => {
28732
28909
  if (!opts.deep) {
28733
- console.log(chalk.yellow("verify currently implements only --deep; re-run with --deep."));
28910
+ console.log(chalk2.yellow("verify currently implements only --deep; re-run with --deep."));
28734
28911
  return;
28735
28912
  }
28736
28913
  const { verifyAgainstServer: verifyAgainstServer2, verifyTargets: verifyTargets2 } = await Promise.resolve().then(() => (init_verify_deep(), verify_deep_exports));
@@ -28741,7 +28918,7 @@ program.command("verify").description("Check that the server holds everything th
28741
28918
  const { forceFullResync: forceFullResync2 } = await Promise.resolve().then(() => (init_verify_repair(), verify_repair_exports));
28742
28919
  const targets = verifyTargets2();
28743
28920
  if (targets.length === 0) {
28744
- console.error(chalk.red("No server configured \u2014 run `chat-recall login` first."));
28921
+ console.error(chalk2.red("No server configured \u2014 run `chat-recall login` first."));
28745
28922
  process.exit(1);
28746
28923
  }
28747
28924
  const sinceMs = opts.sinceHours ? Date.now() - Number(opts.sinceHours) * 36e5 : 0;
@@ -28806,37 +28983,37 @@ program.command("verify").description("Check that the server holds everything th
28806
28983
  try {
28807
28984
  report = await verifyAgainstServer2(t.serverUrl, t.token, sinceMs, deps);
28808
28985
  } catch (e) {
28809
- console.error(chalk.red(`${t.serverUrl}: ${e instanceof Error ? e.message : e}`));
28986
+ console.error(chalk2.red(`${t.serverUrl}: ${e instanceof Error ? e.message : e}`));
28810
28987
  exitCode = 1;
28811
28988
  continue;
28812
28989
  }
28813
28990
  reports.push(report);
28814
28991
  if (opts.json) continue;
28815
- console.log(chalk.bold(`
28992
+ console.log(chalk2.bold(`
28816
28993
  ${t.serverUrl}`));
28817
28994
  console.log(` checked ${report.checked} \xB7 complete ${report.complete} \xB7 no archive ${report.missingArchive}`);
28818
28995
  if (report.pending.length > 0) {
28819
- console.log(chalk.dim(` ${report.pending.length} pending (the ledger knows there is more to send \u2014 these go on their own)`));
28996
+ console.log(chalk2.dim(` ${report.pending.length} pending (the ledger knows there is more to send \u2014 these go on their own)`));
28820
28997
  }
28821
28998
  if (report.stranded.length === 0) {
28822
- console.log(chalk.green(" \u2713 nothing stranded"));
28999
+ console.log(chalk2.green(" \u2713 nothing stranded"));
28823
29000
  } else {
28824
29001
  exitCode = 2;
28825
- console.log(chalk.red(` \u2717 ${report.stranded.length} STRANDED \u2014 the ledger claims complete while the server holds less:`));
29002
+ console.log(chalk2.red(` \u2717 ${report.stranded.length} STRANDED \u2014 the ledger claims complete while the server holds less:`));
28826
29003
  for (const f of report.stranded.slice(0, 25)) {
28827
29004
  console.log(` ${f.sessionId.slice(0, 8)} server short by ${String(f.deficit).padStart(9)} B ${(f.projectPath || "").slice(-44)}`);
28828
29005
  }
28829
- if (report.stranded.length > 25) console.log(chalk.dim(` \u2026and ${report.stranded.length - 25} more`));
29006
+ if (report.stranded.length > 25) console.log(chalk2.dim(` \u2026and ${report.stranded.length - 25} more`));
28830
29007
  if (!opts.repair) {
28831
- console.log(chalk.dim(" Re-run with --repair to clear their cursors and re-ship."));
29008
+ console.log(chalk2.dim(" Re-run with --repair to clear their cursors and re-ship."));
28832
29009
  }
28833
29010
  }
28834
29011
  if (opts.records) {
28835
29012
  const suspects = [...report.stranded, ...report.pending];
28836
29013
  if (suspects.length === 0) {
28837
- console.log(chalk.dim(" --records: nothing to deep-check (size pass found no gaps)"));
29014
+ console.log(chalk2.dim(" --records: nothing to deep-check (size pass found no gaps)"));
28838
29015
  } else {
28839
- console.log(chalk.bold(`
29016
+ console.log(chalk2.bold(`
28840
29017
  record-level diff (${suspects.length} session(s)):`));
28841
29018
  for (const f of suspects) {
28842
29019
  let serverIds;
@@ -28861,9 +29038,9 @@ ${t.serverUrl}`));
28861
29038
  }
28862
29039
  const missing = [...localIds].filter((u) => !serverIds.has(u)).length;
28863
29040
  const onlyServer = [...serverIds].filter((u) => !localIds.has(u)).length;
28864
- const verdict = missing === 0 ? chalk.green("in sync") : chalk.red(`${missing} MISSING server-side`);
29041
+ const verdict = missing === 0 ? chalk2.green("in sync") : chalk2.red(`${missing} MISSING server-side`);
28865
29042
  console.log(
28866
- ` ${f.sessionId.slice(0, 8)} local ${String(localIds.size).padStart(6)} server ${String(serverIds.size).padStart(6)} ${verdict}` + (onlyServer > 0 ? chalk.dim(` (+${onlyServer} server-only \u2014 resume truncation)`) : "")
29043
+ ` ${f.sessionId.slice(0, 8)} local ${String(localIds.size).padStart(6)} server ${String(serverIds.size).padStart(6)} ${verdict}` + (onlyServer > 0 ? chalk2.dim(` (+${onlyServer} server-only \u2014 resume truncation)`) : "")
28867
29044
  );
28868
29045
  if (missing > 0) exitCode = 2;
28869
29046
  }
@@ -28871,7 +29048,7 @@ ${t.serverUrl}`));
28871
29048
  }
28872
29049
  if (opts.repair && report.stranded.length > 0) {
28873
29050
  const n = forceFullResync2(t.serverUrl, report.stranded.map((f) => f.sessionId));
28874
- console.log(chalk.green(` \u21BB cleared ${n} ledger cursor(s) \u2014 run \`chat-recall sync\` to re-ship`));
29051
+ console.log(chalk2.green(` \u21BB cleared ${n} ledger cursor(s) \u2014 run \`chat-recall sync\` to re-ship`));
28875
29052
  }
28876
29053
  }
28877
29054
  if (opts.json) console.log(JSON.stringify({ reports }, null, 2));
@@ -28879,19 +29056,19 @@ ${t.serverUrl}`));
28879
29056
  });
28880
29057
  var sources2 = program.command("sources").description("See and choose which AI-tool transcript folders get synced");
28881
29058
  function decisionBadge(d) {
28882
- if (d === "primary") return chalk.green("syncing") + chalk.dim(" (main)");
28883
- if (d === "approved") return chalk.green("syncing");
28884
- if (d === "declined") return chalk.dim("not syncing");
28885
- return chalk.yellow("NEEDS A DECISION");
29059
+ if (d === "primary") return chalk2.green("syncing") + chalk2.dim(" (main)");
29060
+ if (d === "approved") return chalk2.green("syncing");
29061
+ if (d === "declined") return chalk2.dim("not syncing");
29062
+ return chalk2.yellow("NEEDS A DECISION");
28886
29063
  }
28887
29064
  async function listSources() {
28888
29065
  const { discoverHomes: discoverHomes2 } = await Promise.resolve().then(() => (init_home_discovery(), home_discovery_exports));
28889
29066
  const { homeDecision: homeDecision2, grandfatherLegacyHomes: grandfatherLegacyHomes3 } = await Promise.resolve().then(() => (init_home_approval(), home_approval_exports));
28890
29067
  grandfatherLegacyHomes3();
28891
29068
  const homes = discoverHomes2();
28892
- console.log(chalk.bold("Transcript folders found on this machine"));
29069
+ console.log(chalk2.bold("Transcript folders found on this machine"));
28893
29070
  if (homes.length === 0) {
28894
- console.log(chalk.dim(" none \u2014 no AI tool transcripts detected"));
29071
+ console.log(chalk2.dim(" none \u2014 no AI tool transcripts detected"));
28895
29072
  return { pending: 0 };
28896
29073
  }
28897
29074
  let pending = 0;
@@ -28900,14 +29077,14 @@ async function listSources() {
28900
29077
  if (d === "pending") pending++;
28901
29078
  const count = h.sessions > 0 ? `${h.sessions} session${h.sessions === 1 ? "" : "s"}` : "\u2014";
28902
29079
  console.log(
28903
- ` ${h.tool.padEnd(9)} ${count.padStart(14)} ${decisionBadge(d).padEnd(30)} ${h.path}` + (h.via === "declared" ? chalk.dim(" (you configured this)") : "")
29080
+ ` ${h.tool.padEnd(9)} ${count.padStart(14)} ${decisionBadge(d).padEnd(30)} ${h.path}` + (h.via === "declared" ? chalk2.dim(" (you configured this)") : "")
28904
29081
  );
28905
29082
  }
28906
29083
  if (pending > 0) {
28907
29084
  console.log();
28908
- console.log(chalk.yellow(` ${pending} folder(s) need a decision \u2014 they are NOT being synced yet.`));
28909
- console.log(chalk.dim(" chat-recall sources approve <path> start syncing it"));
28910
- console.log(chalk.dim(" chat-recall sources decline <path> keep it out (e.g. a work account)"));
29085
+ console.log(chalk2.yellow(` ${pending} folder(s) need a decision \u2014 they are NOT being synced yet.`));
29086
+ console.log(chalk2.dim(" chat-recall sources approve <path> start syncing it"));
29087
+ console.log(chalk2.dim(" chat-recall sources decline <path> keep it out (e.g. a work account)"));
28911
29088
  }
28912
29089
  return { pending };
28913
29090
  }
@@ -28920,74 +29097,74 @@ sources2.command("add <path>").description("Sync a transcript folder we did not
28920
29097
  const abs = normalizeHomePath2(path);
28921
29098
  const tool = identifyHome2(abs);
28922
29099
  if (!tool) {
28923
- console.error(chalk.red(`Not a transcript folder: ${abs}`));
28924
- console.error(chalk.dim(" Expected one of:"));
28925
- console.error(chalk.dim(" <dir>/projects/<project>/<uuid>.jsonl (Claude Code)"));
28926
- console.error(chalk.dim(" <dir>/sessions/YYYY/MM/DD/rollout-*.jsonl (Codex)"));
28927
- console.error(chalk.dim(" <dir>/tmp/<project>/chats/session-*.json (Gemini)"));
28928
- console.error(chalk.dim(" <dir>/brain/<id>/.system_generated/logs/* (Antigravity)"));
28929
- console.error(chalk.dim(" <dir>/opencode.db (OpenCode)"));
29100
+ console.error(chalk2.red(`Not a transcript folder: ${abs}`));
29101
+ console.error(chalk2.dim(" Expected one of:"));
29102
+ console.error(chalk2.dim(" <dir>/projects/<project>/<uuid>.jsonl (Claude Code)"));
29103
+ console.error(chalk2.dim(" <dir>/sessions/YYYY/MM/DD/rollout-*.jsonl (Codex)"));
29104
+ console.error(chalk2.dim(" <dir>/tmp/<project>/chats/session-*.json (Gemini)"));
29105
+ console.error(chalk2.dim(" <dir>/brain/<id>/.system_generated/logs/* (Antigravity)"));
29106
+ console.error(chalk2.dim(" <dir>/opencode.db (OpenCode)"));
28930
29107
  process.exit(1);
28931
29108
  }
28932
29109
  if (!approveHome2(abs)) {
28933
- console.error(chalk.red("Could not write settings."));
29110
+ console.error(chalk2.red("Could not write settings."));
28934
29111
  process.exit(1);
28935
29112
  }
28936
- console.log(chalk.green(`\u2713 syncing ${tool} transcripts from ${abs}`));
28937
- console.log(chalk.dim(" Takes effect on the next sync; run `chat-recall sync` to do it now."));
29113
+ console.log(chalk2.green(`\u2713 syncing ${tool} transcripts from ${abs}`));
29114
+ console.log(chalk2.dim(" Takes effect on the next sync; run `chat-recall sync` to do it now."));
28938
29115
  });
28939
29116
  sources2.command("approve <path>").description("Start syncing a folder that was waiting for a decision").action(async (path) => {
28940
29117
  const { approveHome: approveHome2, normalizeHomePath: normalizeHomePath2 } = await Promise.resolve().then(() => (init_home_approval(), home_approval_exports));
28941
29118
  const abs = normalizeHomePath2(path);
28942
29119
  if (!approveHome2(abs)) {
28943
- console.error(chalk.red("Could not write settings."));
29120
+ console.error(chalk2.red("Could not write settings."));
28944
29121
  process.exit(1);
28945
29122
  }
28946
- console.log(chalk.green(`\u2713 now syncing ${abs}`));
29123
+ console.log(chalk2.green(`\u2713 now syncing ${abs}`));
28947
29124
  });
28948
29125
  sources2.command("decline <path>").description("Keep a folder out of sync (e.g. a work account you do not want here)").option("--delete-remote", "Also delete sessions already uploaded FROM this folder (irreversible)", false).option("--yes", "Skip the confirmation prompt for --delete-remote", false).action(async (path, opts) => {
28949
29126
  const { declineHome: declineHome2, normalizeHomePath: normalizeHomePath2 } = await Promise.resolve().then(() => (init_home_approval(), home_approval_exports));
28950
29127
  const { identifyHome: identifyHome2, sessionIdsInHome: sessionIdsInHome2 } = await Promise.resolve().then(() => (init_home_discovery(), home_discovery_exports));
28951
29128
  const abs = normalizeHomePath2(path);
28952
29129
  if (!declineHome2(abs)) {
28953
- console.error(chalk.red("Could not write settings."));
29130
+ console.error(chalk2.red("Could not write settings."));
28954
29131
  process.exit(1);
28955
29132
  }
28956
- console.log(chalk.green(`\u2713 not syncing ${abs}`));
29133
+ console.log(chalk2.green(`\u2713 not syncing ${abs}`));
28957
29134
  if (!opts.deleteRemote) {
28958
- console.log(chalk.dim(" Sessions already uploaded from it stay on the server."));
28959
- console.log(chalk.dim(` To remove those too: chat-recall sources decline ${path} --delete-remote`));
29135
+ console.log(chalk2.dim(" Sessions already uploaded from it stay on the server."));
29136
+ console.log(chalk2.dim(` To remove those too: chat-recall sources decline ${path} --delete-remote`));
28960
29137
  return;
28961
29138
  }
28962
29139
  const tool = identifyHome2(abs);
28963
29140
  if (!tool) {
28964
- console.error(chalk.red(`Not a transcript folder: ${abs}`));
29141
+ console.error(chalk2.red(`Not a transcript folder: ${abs}`));
28965
29142
  process.exit(1);
28966
29143
  }
28967
29144
  const ids = sessionIdsInHome2(abs, tool);
28968
29145
  if (ids.length === 0) {
28969
- console.log(chalk.dim(" Nothing to delete \u2014 no session files found in that folder."));
29146
+ console.log(chalk2.dim(" Nothing to delete \u2014 no session files found in that folder."));
28970
29147
  if (tool === "opencode") {
28971
- console.log(chalk.dim(" (OpenCode keys sessions by database row, so they cannot be enumerated from the path.)"));
29148
+ console.log(chalk2.dim(" (OpenCode keys sessions by database row, so they cannot be enumerated from the path.)"));
28972
29149
  }
28973
29150
  return;
28974
29151
  }
28975
29152
  const targets = loadAllCredentials();
28976
29153
  if (targets.length === 0) {
28977
- console.error(chalk.red("Not logged in \u2014 nothing to delete from."));
29154
+ console.error(chalk2.red("Not logged in \u2014 nothing to delete from."));
28978
29155
  process.exit(1);
28979
29156
  }
28980
29157
  if (!opts.yes) {
28981
29158
  console.log();
28982
- console.log(chalk.yellow(`About to DELETE ${ids.length} session(s) from ${targets.length} server(s).`));
28983
- console.log(chalk.yellow("This is irreversible \u2014 the transcripts stay on this machine, but the"));
28984
- console.log(chalk.yellow("server copies (search index, conversation view, archive) are removed."));
29159
+ console.log(chalk2.yellow(`About to DELETE ${ids.length} session(s) from ${targets.length} server(s).`));
29160
+ console.log(chalk2.yellow("This is irreversible \u2014 the transcripts stay on this machine, but the"));
29161
+ console.log(chalk2.yellow("server copies (search index, conversation view, archive) are removed."));
28985
29162
  const { createInterface: createInterface3 } = await import("node:readline/promises");
28986
29163
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
28987
29164
  const answer = (await rl.question("Type the number of sessions to confirm: ")).trim();
28988
29165
  rl.close();
28989
29166
  if (answer !== String(ids.length)) {
28990
- console.log(chalk.dim("Cancelled \u2014 nothing deleted."));
29167
+ console.log(chalk2.dim("Cancelled \u2014 nothing deleted."));
28991
29168
  return;
28992
29169
  }
28993
29170
  }
@@ -29007,8 +29184,8 @@ sources2.command("decline <path>").description("Keep a folder out of sync (e.g.
29007
29184
  failed++;
29008
29185
  }
29009
29186
  }
29010
- const line = ` ${base2}: deleted ${deleted}` + (missing ? `, ${missing} were not there` : "") + (failed ? chalk.red(`, ${failed} FAILED`) : "");
29011
- console.log(failed ? chalk.yellow(line) : chalk.green(line));
29187
+ const line = ` ${base2}: deleted ${deleted}` + (missing ? `, ${missing} were not there` : "") + (failed ? chalk2.red(`, ${failed} FAILED`) : "");
29188
+ console.log(failed ? chalk2.yellow(line) : chalk2.green(line));
29012
29189
  if (failed) process.exitCode = 1;
29013
29190
  }
29014
29191
  });
@@ -29016,30 +29193,30 @@ sources2.command("forget <path>").description("Undo a decision, so the folder is
29016
29193
  const { resetHomeDecision: resetHomeDecision2, normalizeHomePath: normalizeHomePath2 } = await Promise.resolve().then(() => (init_home_approval(), home_approval_exports));
29017
29194
  const abs = normalizeHomePath2(path);
29018
29195
  resetHomeDecision2(abs);
29019
- console.log(chalk.green(`\u2713 ${abs} is undecided again (not syncing until approved)`));
29196
+ console.log(chalk2.green(`\u2713 ${abs} is undecided again (not syncing until approved)`));
29020
29197
  });
29021
29198
  var detectors = program.command("detectors").description("Manage the optional external secret detectors (gitleaks, trufflehog)");
29022
29199
  detectors.command("status").description("Show which detectors are installed, pinned and verified").action(async () => {
29023
29200
  const { detectorStatus: detectorStatus2 } = await Promise.resolve().then(() => (init_detector_install(), detector_install_exports));
29024
29201
  const { externalScannersEnabled: externalScannersEnabled2 } = await Promise.resolve().then(() => (init_secret_scanner(), secret_scanner_exports));
29025
- console.log(chalk.bold("external secret detectors"));
29202
+ console.log(chalk2.bold("external secret detectors"));
29026
29203
  console.log(
29027
- externalScannersEnabled2() ? ` gate: ${chalk.green("enabled")} (CHAT_RECALL_EXTERNAL_SCANNERS)` : ` gate: ${chalk.yellow("disabled")} \u2014 set CHAT_RECALL_EXTERNAL_SCANNERS=1 to use them`
29204
+ externalScannersEnabled2() ? ` gate: ${chalk2.green("enabled")} (CHAT_RECALL_EXTERNAL_SCANNERS)` : ` gate: ${chalk2.yellow("disabled")} \u2014 set CHAT_RECALL_EXTERNAL_SCANNERS=1 to use them`
29028
29205
  );
29029
29206
  if (process.env.CHAT_RECALL_DETECTOR_DIR) {
29030
- console.log(` dir: ${chalk.dim(process.env.CHAT_RECALL_DETECTOR_DIR)} ${chalk.dim("(operator-supplied, unverified)")}`);
29207
+ console.log(` dir: ${chalk2.dim(process.env.CHAT_RECALL_DETECTOR_DIR)} ${chalk2.dim("(operator-supplied, unverified)")}`);
29031
29208
  }
29032
29209
  console.log();
29033
29210
  for (const d of detectorStatus2()) {
29034
- const label = `${d.name} ${chalk.dim(d.pinnedVersion)} ${chalk.dim(`[${d.license}]`)}`;
29035
- if (d.state === "ready") console.log(` ${chalk.green("\u2713")} ${label} \u2192 ${chalk.dim(d.path)}`);
29036
- else if (d.state === "configured") console.log(` ${chalk.green("\u2713")} ${label} \u2192 ${chalk.dim(d.path)} ${chalk.dim("(operator-supplied)")}`);
29037
- else if (d.state === "not-installed") console.log(` ${chalk.dim("\xB7")} ${label} \u2014 not installed`);
29038
- else if (!d.supported) console.log(` ${chalk.dim("\xB7")} ${label} \u2014 no build for this platform`);
29039
- else console.log(` ${chalk.red("\u2717")} ${label} \u2014 ${d.state}, reinstall with \`chat-recall detectors install ${d.name}\``);
29211
+ const label = `${d.name} ${chalk2.dim(d.pinnedVersion)} ${chalk2.dim(`[${d.license}]`)}`;
29212
+ if (d.state === "ready") console.log(` ${chalk2.green("\u2713")} ${label} \u2192 ${chalk2.dim(d.path)}`);
29213
+ else if (d.state === "configured") console.log(` ${chalk2.green("\u2713")} ${label} \u2192 ${chalk2.dim(d.path)} ${chalk2.dim("(operator-supplied)")}`);
29214
+ else if (d.state === "not-installed") console.log(` ${chalk2.dim("\xB7")} ${label} \u2014 not installed`);
29215
+ else if (!d.supported) console.log(` ${chalk2.dim("\xB7")} ${label} \u2014 no build for this platform`);
29216
+ else console.log(` ${chalk2.red("\u2717")} ${label} \u2014 ${d.state}, reinstall with \`chat-recall detectors install ${d.name}\``);
29040
29217
  }
29041
29218
  console.log();
29042
- console.log(chalk.dim("Builtin in-process detection runs for every user and needs none of this."));
29219
+ console.log(chalk2.dim("Builtin in-process detection runs for every user and needs none of this."));
29043
29220
  });
29044
29221
  detectors.command("install [name]").description("Download + verify a pinned detector into ~/.chat-recall/bin (default: both)").option("--force", "Re-download even if the pinned version is already installed").action(async (name, opts) => {
29045
29222
  const { installDetector: installDetector2, DETECTOR_MANIFEST: DETECTOR_MANIFEST2 } = await Promise.resolve().then(() => (init_detector_install(), detector_install_exports));
@@ -29048,33 +29225,33 @@ detectors.command("install [name]").description("Download + verify a pinned dete
29048
29225
  let failed = false;
29049
29226
  for (const n of names) {
29050
29227
  if (!valid.includes(n)) {
29051
- console.error(chalk.red(`unknown detector "${n}" \u2014 expected one of: ${valid.join(", ")}`));
29228
+ console.error(chalk2.red(`unknown detector "${n}" \u2014 expected one of: ${valid.join(", ")}`));
29052
29229
  failed = true;
29053
29230
  continue;
29054
29231
  }
29055
29232
  try {
29056
29233
  const r = await installDetector2(n, { force: opts.force });
29057
- console.log(r.installed ? chalk.green(`\u2713 ${r.name} ${r.version} installed \u2192 ${r.path}`) : chalk.dim(`\xB7 ${r.name} ${r.version} already installed and verified \u2192 ${r.path}`));
29058
- if (r.installed) console.log(chalk.dim(` sha256 ${r.binarySha256}`));
29234
+ console.log(r.installed ? chalk2.green(`\u2713 ${r.name} ${r.version} installed \u2192 ${r.path}`) : chalk2.dim(`\xB7 ${r.name} ${r.version} already installed and verified \u2192 ${r.path}`));
29235
+ if (r.installed) console.log(chalk2.dim(` sha256 ${r.binarySha256}`));
29059
29236
  } catch (err) {
29060
29237
  failed = true;
29061
- console.error(chalk.red(`${n} install failed: ${err instanceof Error ? err.message : String(err)}`));
29238
+ console.error(chalk2.red(`${n} install failed: ${err instanceof Error ? err.message : String(err)}`));
29062
29239
  }
29063
29240
  }
29064
29241
  if (failed) process.exit(1);
29065
29242
  console.log();
29066
- console.log(chalk.dim("Enable them with CHAT_RECALL_EXTERNAL_SCANNERS=1. They enrich the findings"));
29067
- console.log(chalk.dim("dashboard; they are not what redacts your data before it syncs."));
29243
+ console.log(chalk2.dim("Enable them with CHAT_RECALL_EXTERNAL_SCANNERS=1. They enrich the findings"));
29244
+ console.log(chalk2.dim("dashboard; they are not what redacts your data before it syncs."));
29068
29245
  });
29069
29246
  detectors.command("remove [name]").description("Remove detectors chat-recall installed (never touches your own binaries)").action(async (name) => {
29070
29247
  const { removeDetector: removeDetector2, DETECTOR_MANIFEST: DETECTOR_MANIFEST2, managedDetectorPath: managedDetectorPath2 } = await Promise.resolve().then(() => (init_detector_install(), detector_install_exports));
29071
29248
  for (const n of name ? [name] : Object.keys(DETECTOR_MANIFEST2)) {
29072
29249
  if (!(n in DETECTOR_MANIFEST2)) {
29073
- console.error(chalk.red(`unknown detector "${n}"`));
29250
+ console.error(chalk2.red(`unknown detector "${n}"`));
29074
29251
  continue;
29075
29252
  }
29076
29253
  const removed = removeDetector2(n);
29077
- console.log(removed ? chalk.green(`\u2713 removed ${n}`) : chalk.dim(`\xB7 nothing to remove at ${managedDetectorPath2(n)}`));
29254
+ console.log(removed ? chalk2.green(`\u2713 removed ${n}`) : chalk2.dim(`\xB7 nothing to remove at ${managedDetectorPath2(n)}`));
29078
29255
  }
29079
29256
  });
29080
29257
  var code = program.command("code").description("Index a codebase with codeindex and ship findings/hotspots/actions to your server");
@@ -29083,22 +29260,22 @@ code.command("index [path]").description("Run codeindex on a repo (default: cwd)
29083
29260
  const target = requireTarget();
29084
29261
  const { collectCode: collectCode2 } = await Promise.resolve().then(() => (init_collector(), collector_exports));
29085
29262
  const workspace = resolve2(path || process.cwd());
29086
- console.log(chalk.bold(`code index ${workspace}`));
29263
+ console.log(chalk2.bold(`code index ${workspace}`));
29087
29264
  const result = await collectCode2({
29088
29265
  workspace,
29089
29266
  autoInstall: opts.install !== false,
29090
- log: (m) => console.log(chalk.dim(" \xB7 " + m))
29267
+ log: (m) => console.log(chalk2.dim(" \xB7 " + m))
29091
29268
  });
29092
29269
  const resp = await serverPost(
29093
29270
  "/api/code/index",
29094
29271
  result
29095
29272
  );
29096
- console.log(chalk.green("\u2713 synced"), chalk.dim(`\u2192 ${target.base}`));
29273
+ console.log(chalk2.green("\u2713 synced"), chalk2.dim(`\u2192 ${target.base}`));
29097
29274
  console.log(` project: ${resp.projectId}`);
29098
29275
  console.log(` health: ${result.project.health.score}/100`);
29099
29276
  console.log(` findings: ${resp.findings} hotspots: ${resp.hotspots} actions: ${resp.actions}`);
29100
29277
  } catch (err) {
29101
- console.error(chalk.red("code index failed:"), err instanceof Error ? err.message : err);
29278
+ console.error(chalk2.red("code index failed:"), err instanceof Error ? err.message : err);
29102
29279
  process.exit(1);
29103
29280
  }
29104
29281
  });
@@ -29106,17 +29283,17 @@ code.command("status").description("List code-indexed projects on your server").
29106
29283
  try {
29107
29284
  const { projects } = await serverGet("/api/code/projects");
29108
29285
  if (!projects.length) {
29109
- console.log(chalk.dim("No code-indexed projects yet. Run `chat-recall code index` in a repo."));
29286
+ console.log(chalk2.dim("No code-indexed projects yet. Run `chat-recall code index` in a repo."));
29110
29287
  return;
29111
29288
  }
29112
- console.log(chalk.bold(`${projects.length} code project(s)`));
29289
+ console.log(chalk2.bold(`${projects.length} code project(s)`));
29113
29290
  for (const p of projects) {
29114
29291
  const when = new Date(p.lastIndexedAt).toISOString().slice(0, 16).replace("T", " ");
29115
- console.log(` ${chalk.bold(p.projectId)}${p.label ? chalk.cyan(` [${p.label}]`) : ""}`);
29116
- console.log(chalk.dim(` health ${p.health.score}/100 \xB7 ${p.health.findings} findings \xB7 ${when}`));
29292
+ console.log(` ${chalk2.bold(p.projectId)}${p.label ? chalk2.cyan(` [${p.label}]`) : ""}`);
29293
+ console.log(chalk2.dim(` health ${p.health.score}/100 \xB7 ${p.health.findings} findings \xB7 ${when}`));
29117
29294
  }
29118
29295
  } catch (err) {
29119
- console.error(chalk.red("code status failed:"), err instanceof Error ? err.message : err);
29296
+ console.error(chalk2.red("code status failed:"), err instanceof Error ? err.message : err);
29120
29297
  process.exit(1);
29121
29298
  }
29122
29299
  });
@@ -29125,12 +29302,12 @@ code.command("show <project>").description("Show a project: health, findings sum
29125
29302
  const enc = encodeURIComponent(project);
29126
29303
  const p = await serverGet(`/api/code/projects/${enc}`);
29127
29304
  const { actions } = await serverGet(`/api/code/actions?project=${enc}&limit=12`);
29128
- console.log(chalk.bold(p.projectId), chalk.dim(p.rootPath));
29305
+ console.log(chalk2.bold(p.projectId), chalk2.dim(p.rootPath));
29129
29306
  console.log(` health ${p.health.score}/100 \u2014 ${p.health.critical}C ${p.health.high}H ${p.health.medium}M ${p.health.low}L \xB7 ${p.health.hotspots} hotspots \xB7 ${Math.round((p.health.aiAuthoredPct || 0) * 100)}% AI-authored`);
29130
- console.log(chalk.bold("\n Top actions:"));
29131
- for (const a of actions) console.log(` ${chalk.yellow("P" + a.pri)} [${a.category}] ${a.title}`);
29307
+ console.log(chalk2.bold("\n Top actions:"));
29308
+ for (const a of actions) console.log(` ${chalk2.yellow("P" + a.pri)} [${a.category}] ${a.title}`);
29132
29309
  } catch (err) {
29133
- console.error(chalk.red("code show failed:"), err instanceof Error ? err.message : err);
29310
+ console.error(chalk2.red("code show failed:"), err instanceof Error ? err.message : err);
29134
29311
  process.exit(1);
29135
29312
  }
29136
29313
  });
@@ -29139,18 +29316,18 @@ async function pushToolkitToServer() {
29139
29316
  try {
29140
29317
  const { syncIncremental: syncIncremental2, isSyncSkip: isSyncSkip2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
29141
29318
  const res = await syncIncremental2();
29142
- if (!isSyncSkip2(res) && res.items > 0) console.log(chalk.dim(` synced ${res.items} toolkit item(s) to the server.`));
29319
+ if (!isSyncSkip2(res) && res.items > 0) console.log(chalk2.dim(` synced ${res.items} toolkit item(s) to the server.`));
29143
29320
  } catch (err) {
29144
- console.error(chalk.dim(` (could not sync to server: ${err instanceof Error ? err.message : err})`));
29321
+ console.error(chalk2.dim(` (could not sync to server: ${err instanceof Error ? err.message : err})`));
29145
29322
  }
29146
29323
  }
29147
29324
  toolkit.command("sync").description("Fan every artifact out to every tool that is missing it (runs locally now)").option("--types <list>", "comma-separated subset: skill,mcp,command,agent").action(async (options) => {
29148
29325
  const { executeSyncAll: executeSyncAll2 } = await Promise.resolve().then(() => (init_toolkit_sync(), toolkit_sync_exports));
29149
29326
  const types = options.types ? options.types.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
29150
29327
  const r = await executeSyncAll2(types);
29151
- for (const c of r.copied) console.log(chalk.green(` + ${c.type} ${c.name} \u2192 ${c.toTool}`) + chalk.dim(` ${c.path || ""}`));
29152
- for (const f of r.failed) console.log(chalk.red(` \u2717 ${f.type} ${f.name} \u2192 ${f.toTool}: ${f.error}`));
29153
- console.log(chalk.bold(`Synced: ${r.copied.length} copied, ${r.skipped.length} already present, ${r.failed.length} failed.`));
29328
+ for (const c of r.copied) console.log(chalk2.green(` + ${c.type} ${c.name} \u2192 ${c.toTool}`) + chalk2.dim(` ${c.path || ""}`));
29329
+ for (const f of r.failed) console.log(chalk2.red(` \u2717 ${f.type} ${f.name} \u2192 ${f.toTool}: ${f.error}`));
29330
+ console.log(chalk2.bold(`Synced: ${r.copied.length} copied, ${r.skipped.length} already present, ${r.failed.length} failed.`));
29154
29331
  if (r.copied.length > 0) await pushToolkitToServer();
29155
29332
  });
29156
29333
  toolkit.command("pull").description("Install artifacts this device is missing, from the server (cross-device setup)").option("--types <list>", "comma-separated subset: mcp").option("--dry-run", "report what would change, write nothing").action(async (options) => {
@@ -29159,7 +29336,7 @@ toolkit.command("pull").description("Install artifacts this device is missing, f
29159
29336
  const { hostname: hostname3 } = await import("node:os");
29160
29337
  const targets = loadAllCredentials2();
29161
29338
  if (targets.length === 0) {
29162
- console.error(chalk.red("Not logged in."), "Run `chat-recall login <server-url>` first.");
29339
+ console.error(chalk2.red("Not logged in."), "Run `chat-recall login <server-url>` first.");
29163
29340
  process.exit(1);
29164
29341
  }
29165
29342
  const rows = [];
@@ -29173,7 +29350,7 @@ toolkit.command("pull").description("Install artifacts this device is missing, f
29173
29350
  const body = await res.json();
29174
29351
  for (const it of body.items || []) rows.push(it);
29175
29352
  } catch (err) {
29176
- console.log(chalk.dim(` (${t.serverUrl} ${type}: ${err instanceof Error ? err.message : err})`));
29353
+ console.log(chalk2.dim(` (${t.serverUrl} ${type}: ${err instanceof Error ? err.message : err})`));
29177
29354
  }
29178
29355
  }
29179
29356
  }
@@ -29188,41 +29365,40 @@ toolkit.command("pull").description("Install artifacts this device is missing, f
29188
29365
  const failed = report.outcomes.filter((o) => o.status === "failed");
29189
29366
  const skipped = report.outcomes.filter((o) => o.status === "skipped");
29190
29367
  for (const o of written) {
29191
- const note = o.rewrittenCommand ? chalk.cyan(` [using this machine's ${o.rewrittenCommand}]`) : "";
29192
- console.log(chalk.green(` + ${o.type} ${o.name} \u2192 ${o.tool}`) + chalk.dim(` ${o.path || "(dry run)"}`) + note);
29368
+ const note = o.rewrittenCommand ? chalk2.cyan(` [using this machine's ${o.rewrittenCommand}]`) : "";
29369
+ console.log(chalk2.green(` + ${o.type} ${o.name} \u2192 ${o.tool}`) + chalk2.dim(` ${o.path || "(dry run)"}`) + note);
29193
29370
  }
29194
- for (const o of failed) console.log(chalk.red(` \u2717 ${o.type} ${o.name} \u2192 ${o.tool}: ${o.reason}`));
29195
- for (const o of skipped) console.log(chalk.yellow(` ~ ${o.type} ${o.name} \u2192 ${o.tool}: ${o.reason}`));
29196
- console.log(chalk.bold(
29371
+ for (const o of failed) console.log(chalk2.red(` \u2717 ${o.type} ${o.name} \u2192 ${o.tool}: ${o.reason}`));
29372
+ for (const o of skipped) console.log(chalk2.yellow(` ~ ${o.type} ${o.name} \u2192 ${o.tool}: ${o.reason}`));
29373
+ console.log(chalk2.bold(
29197
29374
  `${options.dryRun ? "Would install" : "Installed"}: ${written.length} \xB7 already present: ${present.length} \xB7 failed: ${failed.length} \xB7 skipped: ${skipped.length}`
29198
29375
  ));
29199
- const envVars = [...new Set(written.flatMap((o) => o.needsEnv || []))].sort();
29200
- if (envVars.length) {
29376
+ const needs = collectEnvNeeds(written);
29377
+ if (needs.length) {
29201
29378
  console.log("");
29202
- console.log(chalk.yellow.bold("Set these environment variables \u2014 their values were never uploaded:"));
29203
- for (const v of envVars) console.log(` ${v}`);
29379
+ for (const line of renderEnvNeeds(needs, written.length)) console.log(line);
29204
29380
  }
29205
29381
  if (report.unsupported.length) {
29206
29382
  console.log("");
29207
- console.log(chalk.dim("Not installable from the server yet:"));
29383
+ console.log(chalk2.dim("Not installable from the server yet:"));
29208
29384
  for (const u of report.unsupported) {
29209
- console.log(chalk.dim(` ${u.type} (${u.rows} on the server) \u2014 ${u.reason}`));
29385
+ console.log(chalk2.dim(` ${u.type} (${u.rows} on the server) \u2014 ${u.reason}`));
29210
29386
  }
29211
- console.log(chalk.dim(" Those need artifact content upload; the server holds an inventory only."));
29387
+ console.log(chalk2.dim(" Those need artifact content upload; the server holds an inventory only."));
29212
29388
  }
29213
29389
  });
29214
29390
  toolkit.command("drain").description("Process cross-tool sync intents queued from the web UI, now").action(async () => {
29215
29391
  const { drainSyncIntents: drainSyncIntents2 } = await Promise.resolve().then(() => (init_intent_drain(), intent_drain_exports));
29216
29392
  const r = await drainSyncIntents2({ verbose: true });
29217
- console.log(chalk.bold(`Processed ${r.processed} intent(s): ${r.done} done, ${r.errored} errored.`));
29393
+ console.log(chalk2.bold(`Processed ${r.processed} intent(s): ${r.done} done, ${r.errored} errored.`));
29218
29394
  if (r.done > 0) await pushToolkitToServer();
29219
29395
  });
29220
29396
  toolkit.command("copy <type> <name> <fromTool> <toTool>").description("Copy one artifact (skill|mcp|command|agent) from one tool to another").action(async (type, name, fromTool, toTool) => {
29221
29397
  const { executeCopy: executeCopy2 } = await Promise.resolve().then(() => (init_toolkit_sync(), toolkit_sync_exports));
29222
29398
  const r = await executeCopy2(type, name, fromTool, toTool);
29223
- if (r.ok) console.log(chalk.green(`Copied to ${r.targetPath}`));
29399
+ if (r.ok) console.log(chalk2.green(`Copied to ${r.targetPath}`));
29224
29400
  else {
29225
- console.error(chalk.red(r.error || "failed"));
29401
+ console.error(chalk2.red(r.error || "failed"));
29226
29402
  process.exit(1);
29227
29403
  }
29228
29404
  });
@@ -29230,31 +29406,31 @@ program.command("shares").description("List the projects you share with your tea
29230
29406
  try {
29231
29407
  const { shares } = await serverGet("/api/shares");
29232
29408
  if (!shares.length) {
29233
- console.log(chalk.gray("You are not sharing any projects. Share one with"), chalk.bold("chat-recall share <project_id>"));
29409
+ console.log(chalk2.gray("You are not sharing any projects. Share one with"), chalk2.bold("chat-recall share <project_id>"));
29234
29410
  return;
29235
29411
  }
29236
- console.log(chalk.bold(`Shared projects (${shares.length}):`));
29237
- for (const s of shares) console.log(` ${chalk.cyan(s.projectId)} ${chalk.gray(s.scope)}`);
29412
+ console.log(chalk2.bold(`Shared projects (${shares.length}):`));
29413
+ for (const s of shares) console.log(` ${chalk2.cyan(s.projectId)} ${chalk2.gray(s.scope)}`);
29238
29414
  } catch (e) {
29239
- console.error(chalk.red(e instanceof Error ? e.message : String(e)));
29415
+ console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
29240
29416
  process.exit(1);
29241
29417
  }
29242
29418
  });
29243
29419
  program.command("share <project_id>").description("Share a project with your team (teammates can then see your work on it)").action(async (projectId) => {
29244
29420
  try {
29245
29421
  await serverPost("/api/shares", { project_id: projectId });
29246
- console.log(chalk.green("\u2713 shared"), chalk.cyan(projectId), chalk.gray("with your team"));
29422
+ console.log(chalk2.green("\u2713 shared"), chalk2.cyan(projectId), chalk2.gray("with your team"));
29247
29423
  } catch (e) {
29248
- console.error(chalk.red(e instanceof Error ? e.message : String(e)));
29424
+ console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
29249
29425
  process.exit(1);
29250
29426
  }
29251
29427
  });
29252
29428
  program.command("unshare <project_id>").description("Stop sharing a project with your team").action(async (projectId) => {
29253
29429
  try {
29254
29430
  const r = await serverDelete("/api/shares", { project_id: projectId });
29255
- console.log(r.removed ? chalk.green("\u2713 unshared") : chalk.yellow("was not shared"), chalk.cyan(projectId));
29431
+ console.log(r.removed ? chalk2.green("\u2713 unshared") : chalk2.yellow("was not shared"), chalk2.cyan(projectId));
29256
29432
  } catch (e) {
29257
- console.error(chalk.red(e instanceof Error ? e.message : String(e)));
29433
+ console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
29258
29434
  process.exit(1);
29259
29435
  }
29260
29436
  });
@@ -29263,12 +29439,12 @@ tasks.command("list").description("List team tasks assigned to you").action(asyn
29263
29439
  try {
29264
29440
  const { tasks: tasks2 } = await serverGet("/api/tasks?assignee=@me");
29265
29441
  if (!tasks2.length) {
29266
- console.log(chalk.gray("No tasks assigned to you."));
29442
+ console.log(chalk2.gray("No tasks assigned to you."));
29267
29443
  return;
29268
29444
  }
29269
- for (const t of tasks2) console.log(` [${t.status}] ${t.title} ${chalk.gray(t.id)}${t.projectId ? chalk.gray(" \xB7 " + t.projectId) : ""}`);
29445
+ for (const t of tasks2) console.log(` [${t.status}] ${t.title} ${chalk2.gray(t.id)}${t.projectId ? chalk2.gray(" \xB7 " + t.projectId) : ""}`);
29270
29446
  } catch (e) {
29271
- console.error(chalk.red(e instanceof Error ? e.message : String(e)));
29447
+ console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
29272
29448
  process.exit(1);
29273
29449
  }
29274
29450
  });
@@ -29281,7 +29457,7 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
29281
29457
  const { join: join61 } = await import("node:path");
29282
29458
  const file = join61(cwd, "TEAM_TASKS.md");
29283
29459
  if (!opts.force && existsSync57(file) && parseTeamTasksFile2(readFileSync50(file, "utf8")).length > 0) {
29284
- console.error(chalk.red("TEAM_TASKS.md has unpushed edits."), "Run", chalk.bold("chat-recall tasks push"), "first, or", chalk.bold("tasks pull --force"), "to discard.");
29460
+ console.error(chalk2.red("TEAM_TASKS.md has unpushed edits."), "Run", chalk2.bold("chat-recall tasks push"), "first, or", chalk2.bold("tasks pull --force"), "to discard.");
29285
29461
  process.exit(1);
29286
29462
  }
29287
29463
  const resolved = resolveProjectId2(cwd);
@@ -29289,9 +29465,9 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
29289
29465
  const { tasks: tasks2 } = await serverGet("/api/tasks?assignee=@me");
29290
29466
  const mine = tasks2.filter((t) => !pid || !t.projectId || t.projectId === pid);
29291
29467
  writeFileSync22(file, renderTeamTasksMd2(mine));
29292
- console.log(chalk.green("\u2713 wrote"), "TEAM_TASKS.md", chalk.gray(`(${mine.length} task(s))`), "\u2014 edit, then", chalk.bold("chat-recall tasks push"));
29468
+ console.log(chalk2.green("\u2713 wrote"), "TEAM_TASKS.md", chalk2.gray(`(${mine.length} task(s))`), "\u2014 edit, then", chalk2.bold("chat-recall tasks push"));
29293
29469
  } catch (e) {
29294
- console.error(chalk.red(e instanceof Error ? e.message : String(e)));
29470
+ console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
29295
29471
  process.exit(1);
29296
29472
  }
29297
29473
  });
@@ -29303,7 +29479,7 @@ tasks.command("push [dir]").description("Push TEAM_TASKS.md status edits back to
29303
29479
  const { join: join61 } = await import("node:path");
29304
29480
  const file = join61(cwd, "TEAM_TASKS.md");
29305
29481
  if (!existsSync57(file)) {
29306
- console.error(chalk.red("no TEAM_TASKS.md here \u2014"), "run", chalk.bold("chat-recall tasks pull"), "first");
29482
+ console.error(chalk2.red("no TEAM_TASKS.md here \u2014"), "run", chalk2.bold("chat-recall tasks pull"), "first");
29307
29483
  process.exit(1);
29308
29484
  }
29309
29485
  const edits = parseTeamTasksFile2(readFileSync50(file, "utf8"));
@@ -29312,9 +29488,9 @@ tasks.command("push [dir]").description("Push TEAM_TASKS.md status edits back to
29312
29488
  await serverPatch(`/api/tasks/${encodeURIComponent(e.id)}`, { status: e.intent });
29313
29489
  n++;
29314
29490
  }
29315
- console.log(n ? chalk.green(`\u2713 pushed ${n} status change(s)`) : chalk.gray("no changes to push"));
29491
+ console.log(n ? chalk2.green(`\u2713 pushed ${n} status change(s)`) : chalk2.gray("no changes to push"));
29316
29492
  } catch (e) {
29317
- console.error(chalk.red(e instanceof Error ? e.message : String(e)));
29493
+ console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
29318
29494
  process.exit(1);
29319
29495
  }
29320
29496
  });
@@ -29325,12 +29501,12 @@ async function runTeam(label, fn) {
29325
29501
  await fn();
29326
29502
  } catch (err) {
29327
29503
  if (err instanceof TeamConfigError2) {
29328
- console.error(chalk.red(`${label}: ${err.message}`));
29504
+ console.error(chalk2.red(`${label}: ${err.message}`));
29329
29505
  } else if (err instanceof TeamAuthError2) {
29330
- console.error(chalk.red(`${label}: auth failed \u2014 ${err.message}`));
29331
- console.error(chalk.dim(" Check the env var named in settings.team.tokenRef holds a valid Keycloak access token."));
29506
+ console.error(chalk2.red(`${label}: auth failed \u2014 ${err.message}`));
29507
+ console.error(chalk2.dim(" Check the env var named in settings.team.tokenRef holds a valid Keycloak access token."));
29332
29508
  } else if (err instanceof TeamHttpError2) {
29333
- console.error(chalk.red(`${label}: ${err.message}`));
29509
+ console.error(chalk2.red(`${label}: ${err.message}`));
29334
29510
  } else {
29335
29511
  throw err;
29336
29512
  }
@@ -29340,76 +29516,76 @@ async function runTeam(label, fn) {
29340
29516
  team.command("whoami").description("Show the user + team memberships the server sees for your token").action(async () => runTeam("team whoami", async () => {
29341
29517
  const { teamMe: teamMe2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
29342
29518
  const me = await teamMe2();
29343
- console.log(chalk.bold(me.user.email) + chalk.dim(` (${me.user.id})`));
29519
+ console.log(chalk2.bold(me.user.email) + chalk2.dim(` (${me.user.id})`));
29344
29520
  if (me.memberships.length === 0) {
29345
- console.log(chalk.dim(" no team memberships yet \u2014 run `chat-recall team create <name>` or `team join <token>`"));
29521
+ console.log(chalk2.dim(" no team memberships yet \u2014 run `chat-recall team create <name>` or `team join <token>`"));
29346
29522
  return;
29347
29523
  }
29348
29524
  for (const m of me.memberships) {
29349
- const role = m.role === "owner" ? chalk.yellow(m.role) : chalk.dim(m.role);
29350
- console.log(` ${chalk.cyan(m.teamName)} ${role} ${chalk.dim(m.plan)} ${chalk.dim(m.teamId)}`);
29525
+ const role = m.role === "owner" ? chalk2.yellow(m.role) : chalk2.dim(m.role);
29526
+ console.log(` ${chalk2.cyan(m.teamName)} ${role} ${chalk2.dim(m.plan)} ${chalk2.dim(m.teamId)}`);
29351
29527
  }
29352
29528
  }));
29353
29529
  team.command("create <name>").description("Create a new team. You become the owner. Stores teamId in settings.").action(async (name) => runTeam("team create", async () => {
29354
29530
  const { teamCreate: teamCreate2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
29355
29531
  const t = await teamCreate2(name);
29356
- console.log(chalk.green(`\u2713 Team created: ${chalk.bold(t.name)}`));
29357
- console.log(chalk.dim(` id: ${t.id}`));
29358
- console.log(chalk.dim(` Now generate an invite: chat-recall team invite`));
29532
+ console.log(chalk2.green(`\u2713 Team created: ${chalk2.bold(t.name)}`));
29533
+ console.log(chalk2.dim(` id: ${t.id}`));
29534
+ console.log(chalk2.dim(` Now generate an invite: chat-recall team invite`));
29359
29535
  }));
29360
29536
  team.command("invite").description("Generate a single-use invite token for your team (owner-only)").option("--email <hint>", "Display hint (e.g. teammate@company.com); not validated").action(async (opts) => runTeam("team invite", async () => {
29361
29537
  const { teamInvite: teamInvite2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
29362
29538
  const r = await teamInvite2(opts.email);
29363
- console.log(chalk.bold("Invite token (copy now \u2014 shown once):"));
29364
- console.log(" " + chalk.cyan(r.inviteToken));
29365
- console.log(chalk.dim(` expires: ${r.expiresAt}`));
29539
+ console.log(chalk2.bold("Invite token (copy now \u2014 shown once):"));
29540
+ console.log(" " + chalk2.cyan(r.inviteToken));
29541
+ console.log(chalk2.dim(` expires: ${r.expiresAt}`));
29366
29542
  console.log();
29367
- console.log(chalk.dim("Recipient runs: chat-recall team join <token>"));
29543
+ console.log(chalk2.dim("Recipient runs: chat-recall team join <token>"));
29368
29544
  }));
29369
29545
  team.command("join <invite-token>").description("Accept a team invite. Stores team id locally.").action(async (inviteToken) => runTeam("team join", async () => {
29370
29546
  const { teamJoin: teamJoin2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
29371
29547
  const t = await teamJoin2(inviteToken);
29372
- console.log(chalk.green(`\u2713 Joined team: ${chalk.bold(t.name)}`));
29373
- console.log(chalk.dim(` id: ${t.id}`));
29374
- console.log(chalk.dim(" Run `chat-recall team pull` to fetch the team library."));
29548
+ console.log(chalk2.green(`\u2713 Joined team: ${chalk2.bold(t.name)}`));
29549
+ console.log(chalk2.dim(` id: ${t.id}`));
29550
+ console.log(chalk2.dim(" Run `chat-recall team pull` to fetch the team library."));
29375
29551
  }));
29376
29552
  team.command("pull").description("Pull artifacts changed since the last pull and write them into your local ~/.claude/, ~/.gemini/, ~/.codex/ dirs").option("--dry-run", "Fetch but don't write to disk (preview only)").action(async (opts) => runTeam("team pull", async () => {
29377
29553
  const { teamPull: teamPull2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
29378
29554
  const r = await teamPull2();
29379
29555
  if (r.pulled.length === 0 && r.removed.length === 0) {
29380
- console.log(chalk.dim("Up to date."));
29556
+ console.log(chalk2.dim("Up to date."));
29381
29557
  return;
29382
29558
  }
29383
29559
  for (const a of r.pulled) {
29384
- console.log(` ${chalk.green("+")} ${chalk.cyan(a.type + "/" + a.name)} v${a.version} ${chalk.dim("(" + a.tool + ")")}`);
29560
+ console.log(` ${chalk2.green("+")} ${chalk2.cyan(a.type + "/" + a.name)} v${a.version} ${chalk2.dim("(" + a.tool + ")")}`);
29385
29561
  }
29386
29562
  for (const id of r.removed) {
29387
- console.log(` ${chalk.red("-")} ${chalk.dim(id + " (revoked)")}`);
29563
+ console.log(` ${chalk2.red("-")} ${chalk2.dim(id + " (revoked)")}`);
29388
29564
  }
29389
29565
  if (opts.dryRun) {
29390
- console.log(chalk.dim(`Watermark: ${r.serverNow} (dry-run \u2014 nothing written)`));
29566
+ console.log(chalk2.dim(`Watermark: ${r.serverNow} (dry-run \u2014 nothing written)`));
29391
29567
  return;
29392
29568
  }
29393
29569
  const { mergePullResult: mergePullResult2 } = await Promise.resolve().then(() => (init_team_merge(), team_merge_exports));
29394
29570
  const m = await mergePullResult2({ pulled: r.pulled, removed: r.removed });
29395
29571
  console.log();
29396
- if (m.written.length) console.log(chalk.green(`\u2713 Wrote ${m.written.length} file${m.written.length === 1 ? "" : "s"}`));
29397
- if (m.skipped.length) console.log(chalk.dim(` Skipped ${m.skipped.length} (unchanged or no target tool)`));
29398
- if (m.removed.length) console.log(chalk.yellow(` Removed ${m.removed.length} revoked file${m.removed.length === 1 ? "" : "s"}`));
29572
+ if (m.written.length) console.log(chalk2.green(`\u2713 Wrote ${m.written.length} file${m.written.length === 1 ? "" : "s"}`));
29573
+ if (m.skipped.length) console.log(chalk2.dim(` Skipped ${m.skipped.length} (unchanged or no target tool)`));
29574
+ if (m.removed.length) console.log(chalk2.yellow(` Removed ${m.removed.length} revoked file${m.removed.length === 1 ? "" : "s"}`));
29399
29575
  for (const f of m.failures) {
29400
- console.log(chalk.red(` ! ${f.path}: ${f.error}`));
29576
+ console.log(chalk2.red(` ! ${f.path}: ${f.error}`));
29401
29577
  }
29402
- console.log(chalk.dim(`Watermark: ${r.serverNow}`));
29578
+ console.log(chalk2.dim(`Watermark: ${r.serverNow}`));
29403
29579
  }));
29404
29580
  team.command("publish <type> <name> <file>").description("Publish a local file as an artifact (skill|command|agent|mcp|plan|plugin|instructions|hook)").option("--tool <tool>", "Target tool: claude|gemini|opencode|codex|agy|cursor|cross_tool", "cross_tool").option("--pinned-to <glob>", "Limit which projects pull this artifact").action(async (type, name, file, opts) => runTeam("team publish", async () => {
29405
29581
  const validTypes = ["skill", "command", "agent", "mcp", "plan", "plugin", "instructions", "hook"];
29406
29582
  if (!validTypes.includes(type)) {
29407
- console.error(chalk.red(`Invalid type: ${type}. Must be one of: ${validTypes.join(", ")}`));
29583
+ console.error(chalk2.red(`Invalid type: ${type}. Must be one of: ${validTypes.join(", ")}`));
29408
29584
  process.exit(1);
29409
29585
  }
29410
29586
  const validTools = ["claude", "gemini", "opencode", "codex", "agy", "cursor", "cross_tool"];
29411
29587
  if (!validTools.includes(opts.tool)) {
29412
- console.error(chalk.red(`Invalid tool: ${opts.tool}. Must be one of: ${validTools.join(", ")}`));
29588
+ console.error(chalk2.red(`Invalid tool: ${opts.tool}. Must be one of: ${validTools.join(", ")}`));
29413
29589
  process.exit(1);
29414
29590
  }
29415
29591
  const { readFileSync: readFileSync50 } = await import("fs");
@@ -29422,32 +29598,32 @@ team.command("publish <type> <name> <file>").description("Publish a local file a
29422
29598
  body,
29423
29599
  pinnedTo: opts.pinnedTo
29424
29600
  });
29425
- console.log(chalk.green(`\u2713 Published ${chalk.bold(type + "/" + name)} v${a.version}`));
29426
- console.log(chalk.dim(` id: ${a.id}`));
29427
- console.log(chalk.dim(` sha256: ${a.sha256}`));
29601
+ console.log(chalk2.green(`\u2713 Published ${chalk2.bold(type + "/" + name)} v${a.version}`));
29602
+ console.log(chalk2.dim(` id: ${a.id}`));
29603
+ console.log(chalk2.dim(` sha256: ${a.sha256}`));
29428
29604
  }));
29429
29605
  team.command("list").description("List artifacts in the team library (metadata only, no bodies)").action(async () => runTeam("team list", async () => {
29430
29606
  const { teamList: teamList2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
29431
29607
  const items = await teamList2();
29432
29608
  if (items.length === 0) {
29433
- console.log(chalk.dim("Library is empty. Try `chat-recall team publish <type> <name> <file>`."));
29609
+ console.log(chalk2.dim("Library is empty. Try `chat-recall team publish <type> <name> <file>`."));
29434
29610
  return;
29435
29611
  }
29436
29612
  for (const a of items) {
29437
- const pinned = a.pinnedTo ? chalk.dim(` pinned=${a.pinnedTo}`) : "";
29438
- console.log(` ${chalk.cyan(a.type + "/" + a.name)} v${a.version} ${chalk.dim("(" + a.tool + ", " + a.bytes + " bytes)")}${pinned}`);
29613
+ const pinned = a.pinnedTo ? chalk2.dim(` pinned=${a.pinnedTo}`) : "";
29614
+ console.log(` ${chalk2.cyan(a.type + "/" + a.name)} v${a.version} ${chalk2.dim("(" + a.tool + ", " + a.bytes + " bytes)")}${pinned}`);
29439
29615
  }
29440
29616
  }));
29441
29617
  team.command("revoke <artifact-id>").alias("unpublish").description("Revoke an artifact from the team library (owner-only)").action(async (artifactId) => runTeam("team revoke", async () => {
29442
29618
  const { teamRevoke: teamRevoke2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
29443
29619
  const r = await teamRevoke2(artifactId);
29444
- console.log(chalk.green(`\u2713 Revoked ${chalk.bold(r.type + "/" + r.name)} (${r.id})`));
29620
+ console.log(chalk2.green(`\u2713 Revoked ${chalk2.bold(r.type + "/" + r.name)} (${r.id})`));
29445
29621
  }));
29446
29622
  team.command("leave").description("Clear local team config (does not delete pulled artifacts; does not remove your server-side membership)").action(async () => runTeam("team leave", async () => {
29447
29623
  const { teamLeave: teamLeave2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
29448
29624
  await teamLeave2();
29449
- console.log(chalk.green("\u2713 Local team config cleared."));
29450
- console.log(chalk.dim(" Server-side membership unchanged. Owner must remove you to fully leave."));
29625
+ console.log(chalk2.green("\u2713 Local team config cleared."));
29626
+ console.log(chalk2.dim(" Server-side membership unchanged. Owner must remove you to fully leave."));
29451
29627
  }));
29452
29628
  var vault = program.command("vault").description("Encrypted multi-device chat backup (E2EE \u2014 server cannot decrypt)");
29453
29629
  async function runVault(label, fn) {
@@ -29455,9 +29631,9 @@ async function runVault(label, fn) {
29455
29631
  try {
29456
29632
  await fn();
29457
29633
  } catch (err) {
29458
- if (err instanceof VaultConfigError2) console.error(chalk.red(`${label}: ${err.message}`));
29459
- else if (err instanceof VaultAuthError2) console.error(chalk.red(`${label}: auth failed \u2014 ${err.message}`));
29460
- else if (err instanceof VaultHttpError2) console.error(chalk.red(`${label}: ${err.message}`));
29634
+ if (err instanceof VaultConfigError2) console.error(chalk2.red(`${label}: ${err.message}`));
29635
+ else if (err instanceof VaultAuthError2) console.error(chalk2.red(`${label}: auth failed \u2014 ${err.message}`));
29636
+ else if (err instanceof VaultHttpError2) console.error(chalk2.red(`${label}: ${err.message}`));
29461
29637
  else throw err;
29462
29638
  process.exit(1);
29463
29639
  }
@@ -29467,7 +29643,7 @@ async function resolvePassphrase(opts) {
29467
29643
  if (opts.envVar) {
29468
29644
  const v = process.env[opts.envVar];
29469
29645
  if (v) return v;
29470
- console.error(chalk.red(`Env var ${opts.envVar} is not set.`));
29646
+ console.error(chalk2.red(`Env var ${opts.envVar} is not set.`));
29471
29647
  process.exit(1);
29472
29648
  }
29473
29649
  if (!opts.noKeyring) {
@@ -29504,49 +29680,49 @@ vault.command("enable").description("Set up the Vault on this device. Generates
29504
29680
  r = await vaultEnableWithRemote2(passphrase, { baseUrl: cred.serverUrl, token: cred.token });
29505
29681
  } catch (err) {
29506
29682
  if (err instanceof VaultPassphraseMismatchError2) {
29507
- console.error(chalk.red(`vault enable: ${err.message}`));
29683
+ console.error(chalk2.red(`vault enable: ${err.message}`));
29508
29684
  process.exit(1);
29509
29685
  }
29510
29686
  throw err;
29511
29687
  }
29512
29688
  }
29513
- console.log(chalk.green("\u2713 Vault enabled on this device."));
29514
- console.log(chalk.dim(` keyId: ${r.keyId}`));
29515
- console.log(chalk.dim(` salt: ${r.saltHex}`) + (r.saltSource === "server" ? chalk.dim(" (from your workspace)") : ""));
29689
+ console.log(chalk2.green("\u2713 Vault enabled on this device."));
29690
+ console.log(chalk2.dim(` keyId: ${r.keyId}`));
29691
+ console.log(chalk2.dim(` salt: ${r.saltHex}`) + (r.saltSource === "server" ? chalk2.dim(" (from your workspace)") : ""));
29516
29692
  if (opts.keyring === false) {
29517
- console.log(chalk.yellow(" keyring: skipped (--no-keyring) \u2014 the background daemon will not be able to decrypt."));
29693
+ console.log(chalk2.yellow(" keyring: skipped (--no-keyring) \u2014 the background daemon will not be able to decrypt."));
29518
29694
  } else {
29519
29695
  const { keyringSet: keyringSet2, probeKeyring: probeKeyring2 } = await Promise.resolve().then(() => (init_keyring(), keyring_exports));
29520
29696
  try {
29521
29697
  keyringSet2(passphrase);
29522
- console.log(chalk.dim(` keyring: stored (${probeKeyring2().backend}) \u2014 the daemon can decrypt unattended.`));
29698
+ console.log(chalk2.dim(` keyring: stored (${probeKeyring2().backend}) \u2014 the daemon can decrypt unattended.`));
29523
29699
  } catch (err) {
29524
- console.log(chalk.yellow(` keyring: unavailable \u2014 ${err instanceof Error ? err.message : err}`));
29525
- console.log(chalk.yellow(" The vault still works for foreground commands; cross-device artifacts will not apply on this machine."));
29700
+ console.log(chalk2.yellow(` keyring: unavailable \u2014 ${err instanceof Error ? err.message : err}`));
29701
+ console.log(chalk2.yellow(" The vault still works for foreground commands; cross-device artifacts will not apply on this machine."));
29526
29702
  }
29527
29703
  }
29528
29704
  console.log();
29529
29705
  if (r.saltSource === "server") {
29530
- console.log(chalk.dim("This device joined your existing vault \u2014 same passphrase, same key."));
29706
+ console.log(chalk2.dim("This device joined your existing vault \u2014 same passphrase, same key."));
29531
29707
  } else {
29532
- console.log(chalk.yellow("To set up another device, run there:"));
29708
+ console.log(chalk2.yellow("To set up another device, run there:"));
29533
29709
  console.log(" chat-recall vault enable");
29534
- console.log(chalk.dim(" (it picks up this workspace's salt automatically \u2014 only the passphrase is needed)"));
29710
+ console.log(chalk2.dim(" (it picks up this workspace's salt automatically \u2014 only the passphrase is needed)"));
29535
29711
  }
29536
- console.log(chalk.yellow("Lose the passphrase = lose encrypted history. There is no recovery."));
29712
+ console.log(chalk2.yellow("Lose the passphrase = lose encrypted history. There is no recovery."));
29537
29713
  }));
29538
29714
  vault.command("sync").description("Walk local chat sources, encrypt new/changed sessions, upload ciphertext to the server").option("--passphrase <s>", "Passphrase (insecure \u2014 prefer interactive or --passphrase-env)").option("--passphrase-env <name>", "Read passphrase from this env var").action(async (opts) => runVault("vault sync", async () => {
29539
29715
  const passphrase = await resolvePassphrase({ passphrase: opts.passphrase, envVar: opts.passphraseEnv });
29540
29716
  const { vaultSync: vaultSync2 } = await Promise.resolve().then(() => (init_vault_client(), vault_client_exports));
29541
29717
  const r = await vaultSync2(passphrase);
29542
29718
  if (r.uploaded.length === 0 && r.skipped.length === 0 && r.failures.length === 0) {
29543
- console.log(chalk.dim("No chat sources found."));
29719
+ console.log(chalk2.dim("No chat sources found."));
29544
29720
  return;
29545
29721
  }
29546
- if (r.uploaded.length) console.log(chalk.green(`\u2713 Uploaded ${r.uploaded.length} session${r.uploaded.length === 1 ? "" : "s"}`));
29547
- if (r.skipped.length) console.log(chalk.dim(` Skipped ${r.skipped.length} (unchanged or denylisted)`));
29722
+ if (r.uploaded.length) console.log(chalk2.green(`\u2713 Uploaded ${r.uploaded.length} session${r.uploaded.length === 1 ? "" : "s"}`));
29723
+ if (r.skipped.length) console.log(chalk2.dim(` Skipped ${r.skipped.length} (unchanged or denylisted)`));
29548
29724
  for (const f of r.failures) {
29549
- console.log(chalk.red(` ! ${f.tool}/${f.sessionId}: ${f.error}`));
29725
+ console.log(chalk2.red(` ! ${f.tool}/${f.sessionId}: ${f.error}`));
29550
29726
  }
29551
29727
  }));
29552
29728
  vault.command("restore").description("Pull encrypted blobs from the server, decrypt locally, write them where chat-recall can index them").option("--passphrase <s>", "Passphrase (insecure \u2014 prefer interactive or --passphrase-env)").option("--passphrase-env <name>", "Read passphrase from this env var").option("--since <ms>", "Only restore blobs uploaded after this ms-epoch (default 0 = all)").action(async (opts) => runVault("vault restore", async () => {
@@ -29554,13 +29730,13 @@ vault.command("restore").description("Pull encrypted blobs from the server, decr
29554
29730
  const { vaultRestore: vaultRestore2 } = await Promise.resolve().then(() => (init_vault_client(), vault_client_exports));
29555
29731
  const r = await vaultRestore2(passphrase, { since: opts.since ? Number(opts.since) : void 0 });
29556
29732
  if (r.restored.length === 0 && r.skipped.length === 0 && r.failures.length === 0) {
29557
- console.log(chalk.dim("No blobs to restore."));
29733
+ console.log(chalk2.dim("No blobs to restore."));
29558
29734
  return;
29559
29735
  }
29560
- if (r.restored.length) console.log(chalk.green(`\u2713 Restored ${r.restored.length} session${r.restored.length === 1 ? "" : "s"}`));
29561
- if (r.skipped.length) console.log(chalk.dim(` Skipped ${r.skipped.length} (unchanged or different key)`));
29736
+ if (r.restored.length) console.log(chalk2.green(`\u2713 Restored ${r.restored.length} session${r.restored.length === 1 ? "" : "s"}`));
29737
+ if (r.skipped.length) console.log(chalk2.dim(` Skipped ${r.skipped.length} (unchanged or different key)`));
29562
29738
  for (const f of r.failures) {
29563
- console.log(chalk.red(` ! ${f.tool}/${f.sessionId}: ${f.error}`));
29739
+ console.log(chalk2.red(` ! ${f.tool}/${f.sessionId}: ${f.error}`));
29564
29740
  }
29565
29741
  }));
29566
29742
  vault.command("status").description("Show whether the Vault is enabled, salt fingerprint, last sync time, and whether this machine can decrypt unattended").action(async () => runVault("vault status", async () => {
@@ -29570,8 +29746,8 @@ vault.command("status").description("Show whether the Vault is enabled, salt fin
29570
29746
  const probe = probeKeyring2();
29571
29747
  const reportKeyring = () => {
29572
29748
  if (!probe.available) {
29573
- console.log(chalk.yellow(` keyring: unavailable (${probe.backend}) \u2014 cross-device artifacts will not apply on this machine`));
29574
- if (probe.hint) console.log(chalk.dim(` ${probe.hint}`));
29749
+ console.log(chalk2.yellow(` keyring: unavailable (${probe.backend}) \u2014 cross-device artifacts will not apply on this machine`));
29750
+ if (probe.hint) console.log(chalk2.dim(` ${probe.hint}`));
29575
29751
  return;
29576
29752
  }
29577
29753
  let stored = false;
@@ -29580,27 +29756,27 @@ vault.command("status").description("Show whether the Vault is enabled, salt fin
29580
29756
  } catch {
29581
29757
  stored = false;
29582
29758
  }
29583
- console.log(stored ? chalk.dim(` keyring: ${probe.backend}, passphrase stored \u2014 the daemon can decrypt unattended`) : chalk.yellow(` keyring: ${probe.backend}, no passphrase stored \u2014 run \`chat-recall vault enable\` to let the daemon decrypt`));
29759
+ console.log(stored ? chalk2.dim(` keyring: ${probe.backend}, passphrase stored \u2014 the daemon can decrypt unattended`) : chalk2.yellow(` keyring: ${probe.backend}, no passphrase stored \u2014 run \`chat-recall vault enable\` to let the daemon decrypt`));
29584
29760
  };
29585
29761
  if (!s.enabled) {
29586
- console.log(chalk.dim("Vault: disabled. Run `chat-recall vault enable`."));
29762
+ console.log(chalk2.dim("Vault: disabled. Run `chat-recall vault enable`."));
29587
29763
  reportKeyring();
29588
29764
  return;
29589
29765
  }
29590
- console.log(chalk.green("Vault: enabled"));
29591
- console.log(chalk.dim(` keyId: ${s.keyId ?? "(unknown \u2014 re-enable)"}`));
29592
- console.log(chalk.dim(` salt: ${s.saltHex ? s.saltHex.slice(0, 16) + "\u2026" : "(missing)"}`));
29593
- console.log(chalk.dim(` lastSyncAt: ${s.lastSyncAt ? new Date(s.lastSyncAt).toISOString() : "never"}`));
29594
- console.log(chalk.dim(` syncTools: ${s.syncTools.join(", ")}`));
29595
- console.log(chalk.dim(` excludeProj: ${s.excludeProjects.length ? s.excludeProjects.join(", ") : "(none)"}`));
29766
+ console.log(chalk2.green("Vault: enabled"));
29767
+ console.log(chalk2.dim(` keyId: ${s.keyId ?? "(unknown \u2014 re-enable)"}`));
29768
+ console.log(chalk2.dim(` salt: ${s.saltHex ? s.saltHex.slice(0, 16) + "\u2026" : "(missing)"}`));
29769
+ console.log(chalk2.dim(` lastSyncAt: ${s.lastSyncAt ? new Date(s.lastSyncAt).toISOString() : "never"}`));
29770
+ console.log(chalk2.dim(` syncTools: ${s.syncTools.join(", ")}`));
29771
+ console.log(chalk2.dim(` excludeProj: ${s.excludeProjects.length ? s.excludeProjects.join(", ") : "(none)"}`));
29596
29772
  reportKeyring();
29597
29773
  }));
29598
29774
  vault.command("forget").description("Remove the stored passphrase from this machine's OS keyring (the vault stays enabled; the daemon stops being able to decrypt)").action(async () => runVault("vault forget", async () => {
29599
29775
  const { keyringDelete: keyringDelete2 } = await Promise.resolve().then(() => (init_keyring(), keyring_exports));
29600
- console.log(keyringDelete2() ? chalk.green("\u2713 Passphrase removed from the OS keyring.") : chalk.dim("Nothing stored in the OS keyring."));
29776
+ console.log(keyringDelete2() ? chalk2.green("\u2713 Passphrase removed from the OS keyring.") : chalk2.dim("Nothing stored in the OS keyring."));
29601
29777
  }));
29602
29778
  program.command("install-hooks").description("Install Claude Code hooks (Stop + PreCompact + UserPromptSubmit + SessionEnd + SessionStart) into every Claude profile").option("--uninstall", "Remove hooks instead of installing them").option("--no-resume-hint", "Don't install the UserPromptSubmit resume-hint hook").option("--no-escalate", "Don't install the SessionEnd learnings-escalation hook").option("--no-wakeup", "Don't install the SessionStart wake-up hook").action(async (opts) => {
29603
- const { mkdirSync: mkdirSync20, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync57, readFileSync: readFileSync50, writeFileSync: writeFileSync22, statSync: statSync46 } = await import("fs");
29779
+ const { mkdirSync: mkdirSync20, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync57, readFileSync: readFileSync50, writeFileSync: writeFileSync22, statSync: statSync47 } = await import("fs");
29604
29780
  const { fileURLToPath: fileURLToPath6 } = await import("url");
29605
29781
  const here = fileURLToPath6(new URL(".", import.meta.url));
29606
29782
  const findHook = (name) => {
@@ -29615,7 +29791,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
29615
29791
  const sourceEscalateHook = findHook("chat_recall_escalate_hook.sh");
29616
29792
  const sourceWakeupHook = findHook("chat_recall_wakeup_hook.sh");
29617
29793
  if (!sourceSaveHook) {
29618
- console.error(chalk.red("Could not locate chat_recall_save_hook.sh in the package."));
29794
+ console.error(chalk2.red("Could not locate chat_recall_save_hook.sh in the package."));
29619
29795
  process.exit(1);
29620
29796
  }
29621
29797
  const hooksDir = getHooksDir();
@@ -29642,8 +29818,8 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
29642
29818
  if (!c.hooks || typeof c.hooks !== "object") c.hooks = {};
29643
29819
  return c;
29644
29820
  } catch (err) {
29645
- console.error(chalk.red(`Could not parse ${file}: ${err}`));
29646
- console.error(chalk.dim("Fix the file or move it aside before re-running."));
29821
+ console.error(chalk2.red(`Could not parse ${file}: ${err}`));
29822
+ console.error(chalk2.dim("Fix the file or move it aside before re-running."));
29647
29823
  process.exit(1);
29648
29824
  }
29649
29825
  };
@@ -29667,23 +29843,23 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
29667
29843
  if (removed === 0 && !existsSync57(hooksJson)) continue;
29668
29844
  writeFileSync22(hooksJson, JSON.stringify(config2, null, 2) + "\n");
29669
29845
  total += removed;
29670
- console.log(chalk.green(`\u2713 Removed ${removed} chat-recall hook entr${removed === 1 ? "y" : "ies"} from ${hooksJson}`));
29846
+ console.log(chalk2.green(`\u2713 Removed ${removed} chat-recall hook entr${removed === 1 ? "y" : "ies"} from ${hooksJson}`));
29671
29847
  }
29672
- if (total === 0) console.log(chalk.dim("No chat-recall hook entries were registered."));
29673
- console.log(chalk.dim(` (Hook scripts left in ${hooksDir} \u2014 delete manually if you want.)`));
29848
+ if (total === 0) console.log(chalk2.dim("No chat-recall hook entries were registered."));
29849
+ console.log(chalk2.dim(` (Hook scripts left in ${hooksDir} \u2014 delete manually if you want.)`));
29674
29850
  return;
29675
29851
  }
29676
29852
  if (process.platform === "win32") {
29677
- console.log(chalk.yellow("Hooks are not installed on Windows."));
29678
- console.log(chalk.dim(" They are POSIX shell scripts, and Claude Code runs hook commands through"));
29679
- console.log(chalk.dim(" cmd.exe, which cannot execute them. Registering them would put an error on"));
29680
- console.log(chalk.dim(" every turn instead of saving anything."));
29681
- console.log(chalk.dim(""));
29682
- console.log(chalk.dim(" Everything else works: indexing, sync, search and the MCP server are"));
29683
- console.log(chalk.dim(" unaffected. Hooks only add automatic fact-saving and the wake-up bundle,"));
29684
- console.log(chalk.dim(" and you can get the same context by calling recall_wake_up from the agent."));
29685
- console.log(chalk.dim(""));
29686
- console.log(chalk.dim(" Under WSL, run this from inside the WSL shell and it installs normally."));
29853
+ console.log(chalk2.yellow("Hooks are not installed on Windows."));
29854
+ console.log(chalk2.dim(" They are POSIX shell scripts, and Claude Code runs hook commands through"));
29855
+ console.log(chalk2.dim(" cmd.exe, which cannot execute them. Registering them would put an error on"));
29856
+ console.log(chalk2.dim(" every turn instead of saving anything."));
29857
+ console.log(chalk2.dim(""));
29858
+ console.log(chalk2.dim(" Everything else works: indexing, sync, search and the MCP server are"));
29859
+ console.log(chalk2.dim(" unaffected. Hooks only add automatic fact-saving and the wake-up bundle,"));
29860
+ console.log(chalk2.dim(" and you can get the same context by calling recall_wake_up from the agent."));
29861
+ console.log(chalk2.dim(""));
29862
+ console.log(chalk2.dim(" Under WSL, run this from inside the WSL shell and it installs normally."));
29687
29863
  return;
29688
29864
  }
29689
29865
  mkdirSync20(hooksDir, { recursive: true });
@@ -29733,35 +29909,35 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
29733
29909
  mkdirSync20(dirname27(hooksJson), { recursive: true });
29734
29910
  writeFileSync22(hooksJson, JSON.stringify(config2, null, 2) + "\n");
29735
29911
  }
29736
- const saveSz = statSync46(installedSaveHook).size;
29737
- console.log(chalk.green(`\u2713 Installed chat-recall save hook (${saveSz} bytes)`));
29912
+ const saveSz = statSync47(installedSaveHook).size;
29913
+ console.log(chalk2.green(`\u2713 Installed chat-recall save hook (${saveSz} bytes)`));
29738
29914
  if (installResume) {
29739
- const resumeSz = statSync46(installedResumeHook).size;
29740
- console.log(chalk.green(`\u2713 Installed chat-recall resume-hint hook (${resumeSz} bytes)`));
29915
+ const resumeSz = statSync47(installedResumeHook).size;
29916
+ console.log(chalk2.green(`\u2713 Installed chat-recall resume-hint hook (${resumeSz} bytes)`));
29741
29917
  }
29742
29918
  if (installEscalate) {
29743
- const escalateSz = statSync46(installedEscalateHook).size;
29744
- console.log(chalk.green(`\u2713 Installed chat-recall learnings-escalation hook (${escalateSz} bytes)`));
29919
+ const escalateSz = statSync47(installedEscalateHook).size;
29920
+ console.log(chalk2.green(`\u2713 Installed chat-recall learnings-escalation hook (${escalateSz} bytes)`));
29745
29921
  }
29746
29922
  if (installWakeup) {
29747
- const wakeupSz = statSync46(installedWakeupHook).size;
29748
- console.log(chalk.green(`\u2713 Installed chat-recall wake-up hook (${wakeupSz} bytes)`));
29923
+ const wakeupSz = statSync47(installedWakeupHook).size;
29924
+ console.log(chalk2.green(`\u2713 Installed chat-recall wake-up hook (${wakeupSz} bytes)`));
29749
29925
  for (const f of wakeupSkipped) {
29750
- console.log(chalk.dim(` (SessionStart already wired in ${f} \u2014 left as is, not duplicated)`));
29926
+ console.log(chalk2.dim(` (SessionStart already wired in ${f} \u2014 left as is, not duplicated)`));
29751
29927
  }
29752
29928
  }
29753
- console.log(chalk.dim(` scripts: ${hooksDir}`));
29929
+ console.log(chalk2.dim(` scripts: ${hooksDir}`));
29754
29930
  for (const [i, f] of hookConfigFiles.entries()) {
29755
- console.log(chalk.dim(` ${i === 0 ? "config: " : " "} ${f}`));
29931
+ console.log(chalk2.dim(` ${i === 0 ? "config: " : " "} ${f}`));
29756
29932
  }
29757
- console.log(chalk.dim(` events: Stop, PreCompact${installResume ? ", UserPromptSubmit" : ""}${installEscalate ? ", SessionEnd" : ""}${installWakeup ? ", SessionStart" : ""}`));
29933
+ console.log(chalk2.dim(` events: Stop, PreCompact${installResume ? ", UserPromptSubmit" : ""}${installEscalate ? ", SessionEnd" : ""}${installWakeup ? ", SessionStart" : ""}`));
29758
29934
  console.log();
29759
- console.log(chalk.dim("Run `chat-recall install-hooks --uninstall` to remove later."));
29935
+ console.log(chalk2.dim("Run `chat-recall install-hooks --uninstall` to remove later."));
29760
29936
  if (!installResume) {
29761
- console.log(chalk.dim("(--no-resume-hint passed \u2014 UserPromptSubmit hook skipped)"));
29937
+ console.log(chalk2.dim("(--no-resume-hint passed \u2014 UserPromptSubmit hook skipped)"));
29762
29938
  }
29763
29939
  if (!installEscalate) {
29764
- console.log(chalk.dim("(--no-escalate passed \u2014 SessionEnd hook skipped)"));
29940
+ console.log(chalk2.dim("(--no-escalate passed \u2014 SessionEnd hook skipped)"));
29765
29941
  }
29766
29942
  });
29767
29943
  program.command("dossier <project>").description("Generate a project dossier (overview/architecture/decisions/etc) as markdown (requires login)").option("--sessions <n>", "Max sessions to enumerate", "10").option("--tasks <n>", "Max open tasks to list", "20").option("--plans <n>", "Max plans to list", "20").option("--out <file>", "Write report to this file instead of stdout").action(async (project, options) => {
@@ -29777,12 +29953,12 @@ program.command("dossier <project>").description("Generate a project dossier (ov
29777
29953
  const md = dossier.markdown;
29778
29954
  if (options.out) {
29779
29955
  writeFileSync21(options.out, md);
29780
- console.log(chalk.green(`Wrote dossier to ${options.out} (${md.length} chars)`));
29956
+ console.log(chalk2.green(`Wrote dossier to ${options.out} (${md.length} chars)`));
29781
29957
  } else {
29782
29958
  console.log(md);
29783
29959
  }
29784
29960
  } catch (err) {
29785
- console.error(chalk.red("Error:"), err instanceof Error ? err.message : err);
29961
+ console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
29786
29962
  process.exit(1);
29787
29963
  }
29788
29964
  });
@@ -29795,17 +29971,17 @@ program.command("watch").description("Run the live indexer daemon (watches all A
29795
29971
  try {
29796
29972
  const { installService: installService2, isServiceRunning: isServiceRunning2, platformName: platformName2 } = await Promise.resolve().then(() => (init_service_installer(), service_installer_exports));
29797
29973
  if (isServiceRunning2()) {
29798
- console.log(chalk.green(`\u2713 chat-recall-watch service already running (${platformName2()}).`));
29974
+ console.log(chalk2.green(`\u2713 chat-recall-watch service already running (${platformName2()}).`));
29799
29975
  return;
29800
29976
  }
29801
29977
  const paths = installService2();
29802
- console.log(chalk.green(`\u2713 chat-recall-watch service installed and started (${platformName2()}).`));
29803
- if (paths.definitionPath) console.log(chalk.dim(` unit: ${paths.definitionPath}`));
29804
- console.log(chalk.dim(` logs: ${paths.logFile} \xB7 manage: \`chat-recall service status\``));
29978
+ console.log(chalk2.green(`\u2713 chat-recall-watch service installed and started (${platformName2()}).`));
29979
+ if (paths.definitionPath) console.log(chalk2.dim(` unit: ${paths.definitionPath}`));
29980
+ console.log(chalk2.dim(` logs: ${paths.logFile} \xB7 manage: \`chat-recall service status\``));
29805
29981
  } catch (err) {
29806
29982
  const se = err;
29807
- console.error(chalk.red(se.message || String(err)));
29808
- if (se.hint) console.error(chalk.dim(` ${se.hint}`));
29983
+ console.error(chalk2.red(se.message || String(err)));
29984
+ if (se.hint) console.error(chalk2.dim(` ${se.hint}`));
29809
29985
  process.exit(1);
29810
29986
  }
29811
29987
  return;
@@ -29823,7 +29999,7 @@ program.command("watch").description("Run the live indexer daemon (watches all A
29823
29999
  lastErr = err;
29824
30000
  }
29825
30001
  }
29826
- console.error(chalk.red("Could not load the watch daemon:"), lastErr instanceof Error ? lastErr.message : lastErr);
30002
+ console.error(chalk2.red("Could not load the watch daemon:"), lastErr instanceof Error ? lastErr.message : lastErr);
29827
30003
  process.exit(1);
29828
30004
  });
29829
30005
  program.command("service <action>").description("Manage the per-user background sync service (Linux: systemd --user \xB7 macOS: launchd \xB7 Windows: Scheduled Task). No admin rights.").action(async (action) => {
@@ -29832,25 +30008,25 @@ program.command("service <action>").description("Manage the per-user background
29832
30008
  if (action === "install") {
29833
30009
  const wasRunning = isServiceRunning2();
29834
30010
  const paths = installService2();
29835
- console.log(chalk.green(wasRunning ? `\u2713 chat-recall-watch service restarted with the current binary (${platformName2()}).` : `\u2713 chat-recall-watch service installed and started (${platformName2()}).`));
29836
- if (paths.definitionPath) console.log(chalk.dim(` unit: ${paths.definitionPath}`));
29837
- console.log(chalk.dim(` logs: ${paths.logFile}`));
30011
+ console.log(chalk2.green(wasRunning ? `\u2713 chat-recall-watch service restarted with the current binary (${platformName2()}).` : `\u2713 chat-recall-watch service installed and started (${platformName2()}).`));
30012
+ if (paths.definitionPath) console.log(chalk2.dim(` unit: ${paths.definitionPath}`));
30013
+ console.log(chalk2.dim(` logs: ${paths.logFile}`));
29838
30014
  } else if (action === "uninstall") {
29839
30015
  const removed = uninstallService2();
29840
- if (removed) console.log(chalk.green(`\u2713 chat-recall-watch service removed (${platformName2()}).`));
29841
- else console.log(chalk.dim("Service was not installed \u2014 nothing to remove."));
30016
+ if (removed) console.log(chalk2.green(`\u2713 chat-recall-watch service removed (${platformName2()}).`));
30017
+ else console.log(chalk2.dim("Service was not installed \u2014 nothing to remove."));
29842
30018
  } else if (action === "status") {
29843
30019
  const running = isServiceRunning2();
29844
- console.log(`chat-recall-watch service (${platformName2()}): ${running ? chalk.green("running") : chalk.yellow("not running")}`);
29845
- if (!running) console.log(chalk.dim(" install: `chat-recall service install`"));
30020
+ console.log(`chat-recall-watch service (${platformName2()}): ${running ? chalk2.green("running") : chalk2.yellow("not running")}`);
30021
+ if (!running) console.log(chalk2.dim(" install: `chat-recall service install`"));
29846
30022
  } else {
29847
- console.error(chalk.red(`Unknown action '${action}'.`) + chalk.dim(" Use: install | uninstall | status"));
30023
+ console.error(chalk2.red(`Unknown action '${action}'.`) + chalk2.dim(" Use: install | uninstall | status"));
29848
30024
  process.exit(1);
29849
30025
  }
29850
30026
  } catch (err) {
29851
30027
  const se = err;
29852
- console.error(chalk.red(se.message || String(err)));
29853
- if (se.hint) console.error(chalk.dim(` ${se.hint}`));
30028
+ console.error(chalk2.red(se.message || String(err)));
30029
+ if (se.hint) console.error(chalk2.dim(` ${se.hint}`));
29854
30030
  process.exit(1);
29855
30031
  }
29856
30032
  });
@@ -29871,7 +30047,7 @@ async function runLogin(serverUrl, opts) {
29871
30047
  if (opts.token) {
29872
30048
  const token = opts.token.trim();
29873
30049
  if (/[^\x21-\x7e]/.test(token)) {
29874
- console.error(chalk.dim("Use the Copy button next to the token in the dashboard and paste the full line."));
30050
+ console.error(chalk2.dim("Use the Copy button next to the token in the dashboard and paste the full line."));
29875
30051
  return failLogin(opts, 'the token contains non-ASCII characters \u2014 this looks like a masked/truncated copy (e.g. "ct_12345678\u2026")');
29876
30052
  }
29877
30053
  const { probeServer: identifyServer, probeOk: identified, probeAdvice: identifyAdvice } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
@@ -29880,7 +30056,7 @@ async function runLogin(serverUrl, opts) {
29880
30056
  const check = await verifyToken(token);
29881
30057
  if (!check.ok) {
29882
30058
  if (check.status === 401 || check.status === 403) {
29883
- console.error(chalk.dim("Tokens are shown once \u2014 mint a fresh one (Account \u2192 Connect your machine) and copy the whole line."));
30059
+ console.error(chalk2.dim("Tokens are shown once \u2014 mint a fresh one (Account \u2192 Connect your machine) and copy the whole line."));
29884
30060
  return failLogin(opts, `${base2} rejected the token (HTTP ${check.status})`);
29885
30061
  }
29886
30062
  if (check.status !== void 0 && check.status >= 500) {
@@ -29892,12 +30068,12 @@ async function runLogin(serverUrl, opts) {
29892
30068
  return failLogin(opts, `could not verify the token against ${base2}: ${check.error || `HTTP ${check.status}`}`);
29893
30069
  }
29894
30070
  saveCredentials2({ serverUrl, token });
29895
- console.log(chalk.green("\u2713 Logged in.") + chalk.dim(` server: ${serverUrl} (token verified)`));
30071
+ console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` server: ${serverUrl} (token verified)`));
29896
30072
  return;
29897
30073
  }
29898
30074
  const existing = loadAllCredentials2().find((t) => t.serverUrl.replace(/\/+$/, "") === base2);
29899
30075
  if (existing && (await verifyToken(existing.token)).ok) {
29900
- console.log(chalk.green("\u2713 Already connected.") + chalk.dim(` server: ${serverUrl} (existing token verified)`));
30076
+ console.log(chalk2.green("\u2713 Already connected.") + chalk2.dim(` server: ${serverUrl} (existing token verified)`));
29901
30077
  return;
29902
30078
  }
29903
30079
  try {
@@ -29907,14 +30083,14 @@ async function runLogin(serverUrl, opts) {
29907
30083
  const probe = await fetch(`${base2}/api/status`, { signal: AbortSignal.timeout(8e3) });
29908
30084
  if (probe.status === 200) {
29909
30085
  saveCredentials2({ serverUrl, token: "" });
29910
- console.log(chalk.green("\u2713 Logged in.") + chalk.dim(` ${serverUrl} (local server \u2014 no auth, no token needed)`));
30086
+ console.log(chalk2.green("\u2713 Logged in.") + chalk2.dim(` ${serverUrl} (local server \u2014 no auth, no token needed)`));
29911
30087
  return;
29912
30088
  }
29913
30089
  }
29914
30090
  } catch {
29915
30091
  }
29916
30092
  if (opts.check) {
29917
- console.error(chalk.dim(`Not connected to ${serverUrl}.`));
30093
+ console.error(chalk2.dim(`Not connected to ${serverUrl}.`));
29918
30094
  process.exit(1);
29919
30095
  }
29920
30096
  try {
@@ -29942,7 +30118,7 @@ async function runLogin(serverUrl, opts) {
29942
30118
  if (!authProvider && !issuer) {
29943
30119
  authProvider = "better-auth";
29944
30120
  if (capsError) {
29945
- console.log(` ${chalk.yellow("Could not read the server's capabilities")} (${capsError}) \u2014 assuming the standard sign-in flow.`);
30121
+ console.log(` ${chalk2.yellow("Could not read the server's capabilities")} (${capsError}) \u2014 assuming the standard sign-in flow.`);
29946
30122
  void Promise.resolve().then(() => (init_install_report(), install_report_exports)).then(({ reportInstallFailure: reportInstallFailure2 }) => reportInstallFailure2(base2, {
29947
30123
  step: "cli_login_probe",
29948
30124
  reason: capsError?.slice(0, 40) ?? null,
@@ -29957,13 +30133,13 @@ async function runLogin(serverUrl, opts) {
29957
30133
  return;
29958
30134
  }
29959
30135
  console.log();
29960
- console.log(chalk.bold("Open this to approve the machine:"));
29961
- console.log(" " + chalk.cyan(p.url));
30136
+ console.log(chalk2.bold("Open this to approve the machine:"));
30137
+ console.log(" " + chalk2.cyan(p.url));
29962
30138
  if (openBrowser(p.url)) {
29963
- console.log(chalk.dim(" (tried to open it for you \u2014 if no window appeared, use the link above)"));
30139
+ console.log(chalk2.dim(" (tried to open it for you \u2014 if no window appeared, use the link above)"));
29964
30140
  }
29965
- console.log(chalk.dim(` If the page asks for a code: ${chalk.bold(p.userCode)}`));
29966
- console.log(chalk.dim("Then come back here \u2014 this finishes on its own. Waiting\u2026"));
30141
+ console.log(chalk2.dim(` If the page asks for a code: ${chalk2.bold(p.userCode)}`));
30142
+ console.log(chalk2.dim("Then come back here \u2014 this finishes on its own. Waiting\u2026"));
29967
30143
  };
29968
30144
  const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base2, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
29969
30145
  const authHdr = { authorization: `Bearer ${tokens.accessToken}`, "content-type": "application/json" };
@@ -29979,9 +30155,9 @@ async function runLogin(serverUrl, opts) {
29979
30155
  const created = await fetch(`${base2}/api/teams`, { method: "POST", headers: authHdr, body: JSON.stringify({ name }) });
29980
30156
  if (!created.ok) throw new Error(`workspace create failed: HTTP ${created.status} ${await created.text().catch(() => "")}`);
29981
30157
  slug = (await readJsonBody(created, base2, "POST /api/teams")).slug;
29982
- console.log(chalk.dim(`Created workspace "${name}" (${slug}) \u2014 you're its owner.`));
30158
+ console.log(chalk2.dim(`Created workspace "${name}" (${slug}) \u2014 you're its owner.`));
29983
30159
  } else {
29984
- teams.forEach((t) => console.error(` ${t.team_slug} ${chalk.dim(t.name)}`));
30160
+ teams.forEach((t) => console.error(` ${t.team_slug} ${chalk2.dim(t.name)}`));
29985
30161
  throw new Error("you belong to multiple teams \u2014 pass --team <slug> (listed above)");
29986
30162
  }
29987
30163
  }
@@ -29994,13 +30170,13 @@ async function runLogin(serverUrl, opts) {
29994
30170
  if (!mint.ok) throw new Error(`token mint failed: HTTP ${mint.status} ${await mint.text().catch(() => "")}`);
29995
30171
  const { token } = await readJsonBody(mint, base2, "POST /api/teams/:slug/tokens");
29996
30172
  saveCredentials2({ serverUrl, token });
29997
- console.log(chalk.green(`\u2713 Logged in to ${chalk.bold(slug)}`) + chalk.dim(` (device: ${deviceId}) server: ${serverUrl}`));
30173
+ console.log(chalk2.green(`\u2713 Logged in to ${chalk2.bold(slug)}`) + chalk2.dim(` (device: ${deviceId}) server: ${serverUrl}`));
29998
30174
  if (!opts.fromInit) {
29999
- console.log(chalk.dim("Run `chat-recall sync` to push redacted conversations."));
30175
+ console.log(chalk2.dim("Run `chat-recall sync` to push redacted conversations."));
30000
30176
  }
30001
30177
  } catch (err) {
30002
30178
  if (opts.fromInit) throw err instanceof Error ? err : new Error(String(err));
30003
- console.error(chalk.red("login failed:"), err instanceof Error ? err.message : err);
30179
+ console.error(chalk2.red("login failed:"), err instanceof Error ? err.message : err);
30004
30180
  process.exit(1);
30005
30181
  }
30006
30182
  }
@@ -30008,15 +30184,15 @@ var exclude = program.command("exclude").description("Control what never leaves
30008
30184
  exclude.command("list", { isDefault: true }).description("Show current exclusions").action(async () => {
30009
30185
  const { loadSettings: loadSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
30010
30186
  const s = loadSettings2();
30011
- console.log(chalk.bold("Sync exclusions") + chalk.dim(" (~/.chat-recall/settings/settings.json)"));
30012
- console.log(` tools: ${s.sync.excludeTools.length ? s.sync.excludeTools.join(", ") : chalk.dim("none")}`);
30013
- console.log(` projects: ${s.sync.excludeProjects.length ? "" : chalk.dim("none")}`);
30187
+ console.log(chalk2.bold("Sync exclusions") + chalk2.dim(" (~/.chat-recall/settings/settings.json)"));
30188
+ console.log(` tools: ${s.sync.excludeTools.length ? s.sync.excludeTools.join(", ") : chalk2.dim("none")}`);
30189
+ console.log(` projects: ${s.sync.excludeProjects.length ? "" : chalk2.dim("none")}`);
30014
30190
  for (const p of s.sync.excludeProjects) console.log(` ${p}`);
30015
30191
  const deny = s.privacy.projectDenylist || [];
30016
- console.log(` index denylist (never even indexed): ${deny.length ? "" : chalk.dim("none")}`);
30192
+ console.log(` index denylist (never even indexed): ${deny.length ? "" : chalk2.dim("none")}`);
30017
30193
  for (const p of deny) console.log(` ${p}`);
30018
- console.log(chalk.dim("\nPersonal folders (Pictures, Music, Documents, Desktop, Downloads, \u2026) are"));
30019
- console.log(chalk.dim("skipped from code indexing by default. Opt one back in: `chat-recall include project <path>`."));
30194
+ console.log(chalk2.dim("\nPersonal folders (Pictures, Music, Documents, Desktop, Downloads, \u2026) are"));
30195
+ console.log(chalk2.dim("skipped from code indexing by default. Opt one back in: `chat-recall include project <path>`."));
30020
30196
  const inc = s.sync.includeProjects || [];
30021
30197
  if (inc.length) {
30022
30198
  console.log(` personal paths opted back in:`);
@@ -30027,8 +30203,8 @@ var include = program.command("include").description("Opt a personal folder (Pic
30027
30203
  include.command("list", { isDefault: true }).description("Show paths opted back into indexing").action(async () => {
30028
30204
  const { loadSettings: loadSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
30029
30205
  const inc = loadSettings2().sync.includeProjects || [];
30030
- console.log(chalk.bold("Personal paths opted back into code indexing"));
30031
- if (!inc.length) console.log(chalk.dim(" none \u2014 personal folders are all skipped"));
30206
+ console.log(chalk2.bold("Personal paths opted back into code indexing"));
30207
+ if (!inc.length) console.log(chalk2.dim(" none \u2014 personal folders are all skipped"));
30032
30208
  for (const p of inc) console.log(` ${p}`);
30033
30209
  });
30034
30210
  include.command("project <path>").description("Re-permit a path under a personal folder (substring match; e.g. ~/Documents/code)").action(async (path) => {
@@ -30039,12 +30215,12 @@ include.command("project <path>").description("Re-permit a path under a personal
30039
30215
  const s = loadSettings2();
30040
30216
  s.sync.includeProjects = s.sync.includeProjects || [];
30041
30217
  if (s.sync.includeProjects.includes(abs)) {
30042
- console.log(chalk.dim(`Already included: ${abs}`));
30218
+ console.log(chalk2.dim(`Already included: ${abs}`));
30043
30219
  return;
30044
30220
  }
30045
30221
  s.sync.includeProjects.push(abs);
30046
30222
  saveSettings2(s);
30047
- console.log(chalk.green(`\u2713 Included ${abs}`) + chalk.dim(" \u2014 code indexing may now walk it (takes effect next discovery tick)."));
30223
+ console.log(chalk2.green(`\u2713 Included ${abs}`) + chalk2.dim(" \u2014 code indexing may now walk it (takes effect next discovery tick)."));
30048
30224
  });
30049
30225
  include.command("remove <path>").description("Undo an opt-in (personal folder goes back to skipped)").action(async (path) => {
30050
30226
  const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
@@ -30055,30 +30231,30 @@ include.command("remove <path>").description("Undo an opt-in (personal folder go
30055
30231
  const before = (s.sync.includeProjects || []).length;
30056
30232
  s.sync.includeProjects = (s.sync.includeProjects || []).filter((p) => p !== path && p !== abs);
30057
30233
  if (s.sync.includeProjects.length === before) {
30058
- console.error(chalk.red(`Not in the include list: ${path}`));
30234
+ console.error(chalk2.red(`Not in the include list: ${path}`));
30059
30235
  process.exit(1);
30060
30236
  }
30061
30237
  saveSettings2(s);
30062
- console.log(chalk.green(`\u2713 Removed ${path}`) + chalk.dim(" \u2014 back to skipped."));
30238
+ console.log(chalk2.green(`\u2713 Removed ${path}`) + chalk2.dim(" \u2014 back to skipped."));
30063
30239
  });
30064
30240
  var retention = program.command("retention").description("How long the server keeps your synced sessions. Bare `retention` shows the current window.");
30065
30241
  retention.command("show", { isDefault: true }).description("Show the window and how many sessions it would delete").action(async () => {
30066
30242
  const t = requireTarget();
30067
30243
  const r = await serverGet("/api/data/retention");
30068
- console.log(chalk.bold("Retention") + chalk.dim(` (server: ${t.base})`));
30244
+ console.log(chalk2.bold("Retention") + chalk2.dim(` (server: ${t.base})`));
30069
30245
  if (!r.days) {
30070
- console.log(` window: ${chalk.green("none")} \u2014 the server keeps everything you sync`);
30246
+ console.log(` window: ${chalk2.green("none")} \u2014 the server keeps everything you sync`);
30071
30247
  } else {
30072
- console.log(` window: ${chalk.yellow(`${r.days} days`)}`);
30248
+ console.log(` window: ${chalk2.yellow(`${r.days} days`)}`);
30073
30249
  console.log(` sessions currently outside it: ${r.wouldDelete}`);
30074
30250
  }
30075
- console.log(chalk.dim(` Set one: chat-recall retention set <${r.min}-${r.max}> \xB7 clear it: chat-recall retention set 0`));
30251
+ console.log(chalk2.dim(` Set one: chat-recall retention set <${r.min}-${r.max}> \xB7 clear it: chat-recall retention set 0`));
30076
30252
  });
30077
30253
  retention.command("set <days>").description("Set the window in days, or 0 to keep everything. Prints what it would delete and asks first.").option("--yes", "Skip the confirmation (for scripts). The warning still prints.", false).action(async (daysArg, opts) => {
30078
30254
  requireTarget();
30079
30255
  const days = Number(daysArg);
30080
30256
  if (!Number.isInteger(days) || days < 0) {
30081
- console.error(chalk.red(`'${daysArg}' is not a whole number of days.`));
30257
+ console.error(chalk2.red(`'${daysArg}' is not a whole number of days.`));
30082
30258
  process.exit(1);
30083
30259
  }
30084
30260
  const preview = await serverGet(
@@ -30086,23 +30262,23 @@ retention.command("set <days>").description("Set the window in days, or 0 to kee
30086
30262
  );
30087
30263
  if (days === 0) {
30088
30264
  await serverPost("/api/data/retention", { days: 0 });
30089
- console.log(chalk.green("\u2713 Window cleared") + chalk.dim(" \u2014 the server keeps everything you sync."));
30265
+ console.log(chalk2.green("\u2713 Window cleared") + chalk2.dim(" \u2014 the server keeps everything you sync."));
30090
30266
  return;
30091
30267
  }
30092
30268
  if (preview.wouldDelete > 0) {
30093
30269
  console.log();
30094
- console.log(chalk.yellow(`This deletes ${preview.wouldDelete} session(s) from the server, and keeps deleting`));
30095
- console.log(chalk.yellow(`anything older than ${days} days from now on.`));
30270
+ console.log(chalk2.yellow(`This deletes ${preview.wouldDelete} session(s) from the server, and keeps deleting`));
30271
+ console.log(chalk2.yellow(`anything older than ${days} days from now on.`));
30096
30272
  console.log();
30097
- console.log(chalk.dim(" Re-syncing can restore a session ONLY if its transcript is still on a"));
30098
- console.log(chalk.dim(" machine you have. For a laptop you no longer own, history your AI tool"));
30099
- console.log(chalk.dim(" has rotated, or files you deleted, our copy is the only copy."));
30100
- console.log(chalk.dim(" Take one first if you want it: chat-recall export"));
30273
+ console.log(chalk2.dim(" Re-syncing can restore a session ONLY if its transcript is still on a"));
30274
+ console.log(chalk2.dim(" machine you have. For a laptop you no longer own, history your AI tool"));
30275
+ console.log(chalk2.dim(" has rotated, or files you deleted, our copy is the only copy."));
30276
+ console.log(chalk2.dim(" Take one first if you want it: chat-recall export"));
30101
30277
  console.log();
30102
30278
  if (!opts.yes) {
30103
30279
  if (!process.stdin.isTTY) {
30104
- console.error(chalk.red("Refusing: this would delete data and there is no terminal to confirm at."));
30105
- console.error(chalk.dim("Pass --yes if you mean it."));
30280
+ console.error(chalk2.red("Refusing: this would delete data and there is no terminal to confirm at."));
30281
+ console.error(chalk2.dim("Pass --yes if you mean it."));
30106
30282
  process.exit(1);
30107
30283
  }
30108
30284
  const { createInterface: createInterface3 } = await import("node:readline/promises");
@@ -30110,7 +30286,7 @@ retention.command("set <days>").description("Set the window in days, or 0 to kee
30110
30286
  const answer = (await rl.question(`Type ${preview.wouldDelete} to confirm: `)).trim();
30111
30287
  rl.close();
30112
30288
  if (answer !== String(preview.wouldDelete)) {
30113
- console.log(chalk.dim("Cancelled \u2014 nothing changed."));
30289
+ console.log(chalk2.dim("Cancelled \u2014 nothing changed."));
30114
30290
  return;
30115
30291
  }
30116
30292
  }
@@ -30119,20 +30295,20 @@ retention.command("set <days>").description("Set the window in days, or 0 to kee
30119
30295
  "/api/data/retention",
30120
30296
  { days, acknowledge: true }
30121
30297
  );
30122
- console.log(chalk.green(`\u2713 Window set to ${res.days} days`) + chalk.dim(` \u2014 ${res.wouldDelete} session(s) will be removed on the next sweep.`));
30298
+ console.log(chalk2.green(`\u2713 Window set to ${res.days} days`) + chalk2.dim(` \u2014 ${res.wouldDelete} session(s) will be removed on the next sweep.`));
30123
30299
  });
30124
30300
  var syncOnly = program.command("sync-only").description("Opt-in sync: ship ONLY chosen projects. Bare `sync-only` shows the mode + allowlist.");
30125
30301
  syncOnly.command("list", { isDefault: true }).description("Show the sync mode and allowlisted projects").action(async () => {
30126
30302
  const { loadSettings: loadSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
30127
30303
  const s = loadSettings2();
30128
30304
  const mode = s.sync.syncMode ?? "all";
30129
- console.log(chalk.bold("Selective sync") + chalk.dim(" (~/.chat-recall/settings/settings.json)"));
30130
- console.log(` mode: ${mode === "only" ? chalk.yellow("only (allowlist)") : chalk.green("all (default)")}`);
30305
+ console.log(chalk2.bold("Selective sync") + chalk2.dim(" (~/.chat-recall/settings/settings.json)"));
30306
+ console.log(` mode: ${mode === "only" ? chalk2.yellow("only (allowlist)") : chalk2.green("all (default)")}`);
30131
30307
  const list = s.sync.syncOnlyProjects ?? [];
30132
- console.log(` projects: ${list.length ? "" : chalk.dim("none")}`);
30308
+ console.log(` projects: ${list.length ? "" : chalk2.dim("none")}`);
30133
30309
  for (const p of list) console.log(` ${p}`);
30134
- if (mode === "only" && list.length === 0) console.log(chalk.red(' \u26A0 mode is "only" but the allowlist is empty \u2192 NOTHING syncs.'));
30135
- console.log(chalk.dim("\nList ids with `chat-recall projects`, then `chat-recall sync-only add <id>`."));
30310
+ if (mode === "only" && list.length === 0) console.log(chalk2.red(' \u26A0 mode is "only" but the allowlist is empty \u2192 NOTHING syncs.'));
30311
+ console.log(chalk2.dim("\nList ids with `chat-recall projects`, then `chat-recall sync-only add <id>`."));
30136
30312
  });
30137
30313
  syncOnly.command("add <project>").description('Add a project id (git:\u2026/ws:\u2026/path:\u2026) or path substring to the allowlist (switches mode to "only")').action(async (project) => {
30138
30314
  const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
@@ -30141,7 +30317,7 @@ syncOnly.command("add <project>").description('Add a project id (git:\u2026/ws:\
30141
30317
  if (!s.sync.syncOnlyProjects.includes(project)) s.sync.syncOnlyProjects.push(project);
30142
30318
  s.sync.syncMode = "only";
30143
30319
  saveSettings2(s);
30144
- console.log(chalk.green(`\u2713 Added ${project}`) + chalk.dim(' \u2014 mode is now "only"; next sync ships only allowlisted projects.'));
30320
+ console.log(chalk2.green(`\u2713 Added ${project}`) + chalk2.dim(' \u2014 mode is now "only"; next sync ships only allowlisted projects.'));
30145
30321
  });
30146
30322
  syncOnly.command("remove <project>").description("Remove a project from the allowlist").action(async (project) => {
30147
30323
  const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
@@ -30149,18 +30325,18 @@ syncOnly.command("remove <project>").description("Remove a project from the allo
30149
30325
  const before = (s.sync.syncOnlyProjects || []).length;
30150
30326
  s.sync.syncOnlyProjects = (s.sync.syncOnlyProjects || []).filter((p) => p !== project);
30151
30327
  if (s.sync.syncOnlyProjects.length === before) {
30152
- console.error(chalk.red(`Not in the allowlist: ${project}`));
30328
+ console.error(chalk2.red(`Not in the allowlist: ${project}`));
30153
30329
  process.exit(1);
30154
30330
  }
30155
30331
  saveSettings2(s);
30156
- console.log(chalk.green(`\u2713 Removed ${project}`));
30332
+ console.log(chalk2.green(`\u2713 Removed ${project}`));
30157
30333
  });
30158
30334
  syncOnly.command("all").description("Switch back to syncing ALL non-excluded projects (keeps the list, just disables opt-in mode)").action(async () => {
30159
30335
  const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
30160
30336
  const s = loadSettings2();
30161
30337
  s.sync.syncMode = "all";
30162
30338
  saveSettings2(s);
30163
- console.log(chalk.green("\u2713 Sync mode: all") + chalk.dim(" \u2014 every non-excluded project syncs again."));
30339
+ console.log(chalk2.green("\u2713 Sync mode: all") + chalk2.dim(" \u2014 every non-excluded project syncs again."));
30164
30340
  });
30165
30341
  program.command("projects").description("List distinct project ids discovered on this machine (for `sync-only add`)").action(async () => {
30166
30342
  const { listAvailableBackends: listAvailableBackends2 } = await Promise.resolve().then(() => (init_tool_backend(), tool_backend_exports));
@@ -30180,11 +30356,11 @@ program.command("projects").description("List distinct project ids discovered on
30180
30356
  }
30181
30357
  const rows = [...counts.entries()].sort((a, b) => b[1] - a[1]);
30182
30358
  if (!rows.length) {
30183
- console.log(chalk.dim("No projects discovered."));
30359
+ console.log(chalk2.dim("No projects discovered."));
30184
30360
  return;
30185
30361
  }
30186
- console.log(chalk.bold("Discovered projects") + chalk.dim(" (id \xB7 sessions)"));
30187
- for (const [id, n] of rows) console.log(` ${chalk.cyan(id)} ${chalk.dim("\xB7 " + n)}`);
30362
+ console.log(chalk2.bold("Discovered projects") + chalk2.dim(" (id \xB7 sessions)"));
30363
+ for (const [id, n] of rows) console.log(` ${chalk2.cyan(id)} ${chalk2.dim("\xB7 " + n)}`);
30188
30364
  });
30189
30365
  exclude.command("project <path>").description("Exclude a project path from syncing (substring match on the project path; e.g. ~/.claude-pr-bot)").action(async (path) => {
30190
30366
  const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
@@ -30193,28 +30369,28 @@ exclude.command("project <path>").description("Exclude a project path from synci
30193
30369
  const abs = resolve3(path.replace(/^~(?=\/|$)/, homedir23()));
30194
30370
  const s = loadSettings2();
30195
30371
  if (s.sync.excludeProjects.includes(abs)) {
30196
- console.log(chalk.dim(`Already excluded: ${abs}`));
30372
+ console.log(chalk2.dim(`Already excluded: ${abs}`));
30197
30373
  return;
30198
30374
  }
30199
30375
  s.sync.excludeProjects.push(abs);
30200
30376
  saveSettings2(s);
30201
- console.log(chalk.green(`\u2713 Excluded ${abs}`) + chalk.dim(" \u2014 takes effect next sync; already-synced rows stay until deleted."));
30377
+ console.log(chalk2.green(`\u2713 Excluded ${abs}`) + chalk2.dim(" \u2014 takes effect next sync; already-synced rows stay until deleted."));
30202
30378
  });
30203
30379
  exclude.command("tool <tool>").description("Exclude an AI tool entirely (claude | gemini | codex | opencode | agy | cursor)").action(async (tool) => {
30204
30380
  const valid = ["claude", "gemini", "codex", "opencode", "agy", "cursor"];
30205
30381
  if (!valid.includes(tool)) {
30206
- console.error(chalk.red(`Unknown tool '${tool}'.`) + chalk.dim(` Use one of: ${valid.join(" | ")}`));
30382
+ console.error(chalk2.red(`Unknown tool '${tool}'.`) + chalk2.dim(` Use one of: ${valid.join(" | ")}`));
30207
30383
  process.exit(1);
30208
30384
  }
30209
30385
  const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
30210
30386
  const s = loadSettings2();
30211
30387
  if (s.sync.excludeTools.includes(tool)) {
30212
- console.log(chalk.dim(`Already excluded: ${tool}`));
30388
+ console.log(chalk2.dim(`Already excluded: ${tool}`));
30213
30389
  return;
30214
30390
  }
30215
30391
  s.sync.excludeTools.push(tool);
30216
30392
  saveSettings2(s);
30217
- console.log(chalk.green(`\u2713 Excluded ${tool}`) + chalk.dim(" \u2014 nothing from it leaves this machine from the next sync on."));
30393
+ console.log(chalk2.green(`\u2713 Excluded ${tool}`) + chalk2.dim(" \u2014 nothing from it leaves this machine from the next sync on."));
30218
30394
  });
30219
30395
  exclude.command("remove <value>").description("Remove an exclusion (tool name or project path)").action(async (value) => {
30220
30396
  const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
@@ -30227,20 +30403,20 @@ exclude.command("remove <value>").description("Remove an exclusion (tool name or
30227
30403
  s.sync.excludeTools = s.sync.excludeTools.filter((t) => t !== value);
30228
30404
  s.sync.excludeProjects = s.sync.excludeProjects.filter((p) => p !== value && p !== abs);
30229
30405
  if (s.sync.excludeTools.length === beforeTools && s.sync.excludeProjects.length === beforeProjects) {
30230
- console.error(chalk.red(`No exclusion matches '${value}'.`) + chalk.dim(" See: chat-recall exclude list"));
30406
+ console.error(chalk2.red(`No exclusion matches '${value}'.`) + chalk2.dim(" See: chat-recall exclude list"));
30231
30407
  process.exit(1);
30232
30408
  }
30233
30409
  saveSettings2(s);
30234
- console.log(chalk.green(`\u2713 Removed ${value}`));
30410
+ console.log(chalk2.green(`\u2713 Removed ${value}`));
30235
30411
  });
30236
30412
  program.command("login [server-url]").description(`Log in (device flow) and mint a sync device token \u2192 ~/.chat-recall/credentials.json (0600). Defaults to ${DEFAULT_SERVER}.`).option("--token <token>", "Self-host: skip OIDC and save this device token directly").option("--check", "Report-only: exit 0 if a working credential for this server exists, 1 otherwise (never interactive)").option("--issuer <url>", "OIDC issuer URL (default: learned from the server via /api/capabilities)").option("--client-id <id>", "OIDC client id (default: chat-recall-web)").option("--team <slug>", "Team to mint the device token for (default: your only team)").option("--device-id <id>", "Device id for this machine (default: hostname)").option("--prompt-json", "Print the device prompt as one JSON line on stdout, then wait for approval", false).action((serverUrl, opts) => runLogin(serverUrl || DEFAULT_SERVER, opts));
30237
30413
  program.command("logout <server-url>").description("Remove one sync target (other targets keep syncing)").action(async (serverUrl) => {
30238
30414
  const { removeCredentials: removeCredentials2, loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30239
30415
  if (removeCredentials2(serverUrl)) {
30240
30416
  const left = loadAllCredentials2();
30241
- console.log(chalk.green(`\u2713 Removed ${serverUrl}`) + chalk.dim(` \u2014 ${left.length} target(s) remain`));
30417
+ console.log(chalk2.green(`\u2713 Removed ${serverUrl}`) + chalk2.dim(` \u2014 ${left.length} target(s) remain`));
30242
30418
  } else {
30243
- console.error(chalk.red(`No such target: ${serverUrl}`));
30419
+ console.error(chalk2.red(`No such target: ${serverUrl}`));
30244
30420
  process.exit(1);
30245
30421
  }
30246
30422
  });
@@ -30248,7 +30424,7 @@ program.command("delete <session-id>").description("Delete a session from chat-r
30248
30424
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30249
30425
  const targets = loadAllCredentials2();
30250
30426
  if (targets.length === 0) {
30251
- console.error(chalk.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30427
+ console.error(chalk2.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30252
30428
  process.exit(1);
30253
30429
  }
30254
30430
  let ok = 0;
@@ -30260,20 +30436,20 @@ program.command("delete <session-id>").description("Delete a session from chat-r
30260
30436
  });
30261
30437
  if (res.ok) {
30262
30438
  ok++;
30263
- console.log(chalk.green(`\u2713 Deleted on ${t.serverUrl}`));
30264
- } else console.error(chalk.red(`\u2717 ${t.serverUrl}: HTTP ${res.status}`));
30439
+ console.log(chalk2.green(`\u2713 Deleted on ${t.serverUrl}`));
30440
+ } else console.error(chalk2.red(`\u2717 ${t.serverUrl}: HTTP ${res.status}`));
30265
30441
  } catch (e) {
30266
- console.error(chalk.red(`\u2717 ${t.serverUrl}: ${e instanceof Error ? e.message : "failed"}`));
30442
+ console.error(chalk2.red(`\u2717 ${t.serverUrl}: ${e instanceof Error ? e.message : "failed"}`));
30267
30443
  }
30268
30444
  }
30269
30445
  if (ok === 0) process.exit(1);
30270
- console.log(chalk.dim(`Tombstoned on ${ok}/${targets.length} server(s) \u2014 re-sync cannot resurrect it.`));
30446
+ console.log(chalk2.dim(`Tombstoned on ${ok}/${targets.length} server(s) \u2014 re-sync cannot resurrect it.`));
30271
30447
  });
30272
30448
  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) => {
30273
30449
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
30274
30450
  const targets = loadAllCredentials2();
30275
30451
  if (targets.length === 0) {
30276
- console.error(chalk.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30452
+ console.error(chalk2.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30277
30453
  process.exit(1);
30278
30454
  }
30279
30455
  let ok = 0;
@@ -30286,14 +30462,14 @@ program.command("rename <session-id> <name>").description('Give a session a memo
30286
30462
  });
30287
30463
  if (res.ok) {
30288
30464
  ok++;
30289
- console.log(chalk.green(`\u2713 ${name.trim() ? "Named" : "Cleared name"} on ${t.serverUrl}`));
30290
- } else console.error(chalk.red(`\u2717 ${t.serverUrl}: HTTP ${res.status}`));
30465
+ console.log(chalk2.green(`\u2713 ${name.trim() ? "Named" : "Cleared name"} on ${t.serverUrl}`));
30466
+ } else console.error(chalk2.red(`\u2717 ${t.serverUrl}: HTTP ${res.status}`));
30291
30467
  } catch (e) {
30292
- console.error(chalk.red(`\u2717 ${t.serverUrl}: ${e instanceof Error ? e.message : "failed"}`));
30468
+ console.error(chalk2.red(`\u2717 ${t.serverUrl}: ${e instanceof Error ? e.message : "failed"}`));
30293
30469
  }
30294
30470
  }
30295
30471
  if (ok === 0) process.exit(1);
30296
- console.log(chalk.dim(name.trim() ? `Named "${name.trim()}" on ${ok}/${targets.length} server(s).` : `Cleared name on ${ok}/${targets.length} server(s).`));
30472
+ console.log(chalk2.dim(name.trim() ? `Named "${name.trim()}" on ${ok}/${targets.length} server(s).` : `Cleared name on ${ok}/${targets.length} server(s).`));
30297
30473
  });
30298
30474
  program.command("sync").description("Push redacted conversations to the configured server (secrets always masked). Bare `sync` is incremental (watermark-based); flags force an explicit window.").option("--since-hours <n>", "Only sync sessions modified in the last N hours").option("--limit <n>", "Max sessions to sync").option("--full", "Ignore the watermark and push everything").option("--paths-cleartext", "Send project paths in cleartext (self-host only; default hashes them)").option("--throttle <ms>", "Pause between upload batches in ms (default: 1000, or 3000 with --full)").option("--prune", "After syncing, drop server-side session rows that have no content (ghost rows)").action(async (opts) => {
30299
30475
  const { syncSessions: syncSessions2, syncIncremental: syncIncremental2, isSyncSkip: isSyncSkip2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
@@ -30302,17 +30478,17 @@ program.command("sync").description("Push redacted conversations to the configur
30302
30478
  const r2 = await syncIncremental2();
30303
30479
  if (isSyncSkip2(r2)) {
30304
30480
  if (r2.skipped === "lock-held") {
30305
- console.log(chalk.dim("Another sync is already running (MCP tick or watch daemon) \u2014 your data is on its way."));
30481
+ console.log(chalk2.dim("Another sync is already running (MCP tick or watch daemon) \u2014 your data is on its way."));
30306
30482
  return;
30307
30483
  }
30308
30484
  const msg = {
30309
30485
  "no-credentials": "Not logged in \u2014 run `chat-recall login <server-url>` first.",
30310
30486
  "paused": "Sync is paused in settings \u2014 re-run `chat-recall login <server-url>` to resume."
30311
30487
  }[r2.skipped];
30312
- console.error(chalk.red(msg));
30488
+ console.error(chalk2.red(msg));
30313
30489
  process.exit(1);
30314
30490
  }
30315
- console.log(chalk.green(`\u2713 Synced ${r2.uploaded} session(s), ${r2.items} item(s)`) + chalk.dim(` \u2014 ${r2.links} links, ${r2.derived} derived rows, ${r2.kgTriples} KG triples, ${r2.skipped} skipped, ${r2.redactions} secrets redacted, ${r2.findings} secret findings${r2.scanned ? ` (scanned ${r2.scanned} in ${r2.scanMs}ms)` : ""} (incremental)`));
30491
+ console.log(chalk2.green(`\u2713 Synced ${r2.uploaded} session(s), ${r2.items} item(s)`) + chalk2.dim(` \u2014 ${r2.links} links, ${r2.derived} derived rows, ${r2.kgTriples} KG triples, ${r2.skipped} skipped, ${r2.redactions} secrets redacted, ${r2.findings} secret findings${r2.scanned ? ` (scanned ${r2.scanned} in ${r2.scanMs}ms)` : ""} (incremental)`));
30316
30492
  return;
30317
30493
  }
30318
30494
  const sinceMs = opts.sinceHours ? Date.now() - Number(opts.sinceHours) * 36e5 : void 0;
@@ -30327,9 +30503,9 @@ program.command("sync").description("Push redacted conversations to the configur
30327
30503
  throttleMs: opts.throttle !== void 0 ? Number(opts.throttle) : opts.full ? 3e3 : void 0,
30328
30504
  prune: !!opts.prune
30329
30505
  });
30330
- console.log(chalk.green(`\u2713 Synced ${r.uploaded} session(s), ${r.items} item(s)`) + chalk.dim(` \u2014 ${r.links} links, ${r.derived} derived rows, ${r.kgTriples} KG triples, ${r.skipped} skipped, ${r.redactions} secrets redacted, ${r.findings} secret findings${r.scanned ? ` (scanned ${r.scanned} in ${r.scanMs}ms)` : ""}`));
30506
+ console.log(chalk2.green(`\u2713 Synced ${r.uploaded} session(s), ${r.items} item(s)`) + chalk2.dim(` \u2014 ${r.links} links, ${r.derived} derived rows, ${r.kgTriples} KG triples, ${r.skipped} skipped, ${r.redactions} secrets redacted, ${r.findings} secret findings${r.scanned ? ` (scanned ${r.scanned} in ${r.scanMs}ms)` : ""}`));
30331
30507
  } catch (err) {
30332
- console.error(chalk.red("sync failed:"), err instanceof Error ? err.message : err);
30508
+ console.error(chalk2.red("sync failed:"), err instanceof Error ? err.message : err);
30333
30509
  process.exit(1);
30334
30510
  }
30335
30511
  });
@@ -30338,11 +30514,11 @@ program.command("reconcile").description("Reconcile DERIVED FIELDS (e.g. native
30338
30514
  try {
30339
30515
  const r = await reconcileFields2({ force: !!opts.force });
30340
30516
  const per = Object.entries(r.perTarget).map(([s, v]) => `${s}: ${v.pushed} pushed`).join(", ");
30341
- console.log(chalk.green(`\u2713 Reconciled derived fields`) + chalk.dim(
30517
+ console.log(chalk2.green(`\u2713 Reconciled derived fields`) + chalk2.dim(
30342
30518
  ` \u2014 ${r.sessions} sessions, ${r.scanned} scanned, ${r.pushed} pushed, ${r.absent} absent${per ? ` (${per})` : ""}`
30343
30519
  ));
30344
30520
  } catch (err) {
30345
- console.error(chalk.red("reconcile failed:"), err instanceof Error ? err.message : err);
30521
+ console.error(chalk2.red("reconcile failed:"), err instanceof Error ? err.message : err);
30346
30522
  process.exit(1);
30347
30523
  }
30348
30524
  });
@@ -30352,7 +30528,7 @@ program.command("update").description("Update this CLI now from the server you s
30352
30528
  const { cliVersion: cliVersion3 } = await Promise.resolve().then(() => (init_http(), http_exports));
30353
30529
  const targets = loadAllCredentials();
30354
30530
  if (!targets.length) {
30355
- console.error(chalk.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30531
+ console.error(chalk2.red("Not logged in \u2014 run `chat-recall login <server-url>` first."));
30356
30532
  process.exit(1);
30357
30533
  }
30358
30534
  let installFailed = false;
@@ -30361,17 +30537,17 @@ program.command("update").description("Update this CLI now from the server you s
30361
30537
  const headers = cred.token ? { authorization: `Bearer ${cred.token}` } : {};
30362
30538
  const r = await runAutoUpdate2(base2, headers, cliVersion3());
30363
30539
  if (r.updated) {
30364
- console.log(chalk.green(`\u2713 ${r.reason}`) + chalk.dim(` (${base2})`));
30540
+ console.log(chalk2.green(`\u2713 ${r.reason}`) + chalk2.dim(` (${base2})`));
30365
30541
  continue;
30366
30542
  }
30367
30543
  const benign = /already current|no CLI release|disabled/.test(r.reason);
30368
30544
  if (/install failed|download failed|checksum/.test(r.reason)) installFailed = true;
30369
- console.log((benign ? chalk.dim("\u2022 ") : chalk.red("\u2717 ")) + r.reason + chalk.dim(` (${base2})`));
30545
+ console.log((benign ? chalk2.dim("\u2022 ") : chalk2.red("\u2717 ")) + r.reason + chalk2.dim(` (${base2})`));
30370
30546
  }
30371
30547
  await refreshUpdateCheck2(true);
30372
30548
  if (installFailed) {
30373
- console.log(chalk.dim("\nThe update was available but did not install \u2014 usually npm cannot write the global prefix. Retry with:"));
30374
- console.log(chalk.dim(` npm install -g chat-recall (or re-run the installer: curl -fsSL ${targets[0].serverUrl.replace(/\/+$/, "")}/install | sh)`));
30549
+ console.log(chalk2.dim("\nThe update was available but did not install \u2014 usually npm cannot write the global prefix. Retry with:"));
30550
+ console.log(chalk2.dim(` npm install -g chat-recall (or re-run the installer: curl -fsSL ${targets[0].serverUrl.replace(/\/+$/, "")}/install | sh)`));
30375
30551
  }
30376
30552
  });
30377
30553
  program.hook("preAction", () => {