chat-recall 0.5.27 → 0.5.29

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
@@ -7134,13 +7134,13 @@ async function parseCodexSubagents(sessionPath) {
7134
7134
  if (meta?.type !== "session_meta")
7135
7135
  continue;
7136
7136
  const payload = meta.payload || {};
7137
- const spawn3 = payload.source?.subagent?.thread_spawn;
7138
- if (!spawn3)
7137
+ const spawn4 = payload.source?.subagent?.thread_spawn;
7138
+ if (!spawn4)
7139
7139
  continue;
7140
- if (spawn3.parent_thread_id !== parentId)
7140
+ if (spawn4.parent_thread_id !== parentId)
7141
7141
  continue;
7142
7142
  const id = fileName.replace(/\.jsonl$/i, "");
7143
- const role = String(spawn3.agent_role || payload.agent_role || "").toLowerCase();
7143
+ const role = String(spawn4.agent_role || payload.agent_role || "").toLowerCase();
7144
7144
  const kind = role.includes("compact") ? "compact" : role.includes("aside") ? "aside" : role.includes("worker") || role.includes("explore") ? "explore" : "other";
7145
7145
  let messages = [];
7146
7146
  try {
@@ -7152,8 +7152,8 @@ async function parseCodexSubagents(sessionPath) {
7152
7152
  subagents.push({
7153
7153
  id,
7154
7154
  kind,
7155
- agentType: spawn3.agent_role || payload.agent_role,
7156
- description: spawn3.agent_nickname || payload.agent_nickname,
7155
+ agentType: spawn4.agent_role || payload.agent_role,
7156
+ description: spawn4.agent_nickname || payload.agent_nickname,
7157
7157
  filePath,
7158
7158
  messageCount: messages.length,
7159
7159
  toolUseCount,
@@ -9781,8 +9781,8 @@ var init_codex2 = __esm({
9781
9781
  } catch {
9782
9782
  continue;
9783
9783
  }
9784
- const spawn3 = meta?.payload?.source?.subagent?.thread_spawn;
9785
- if (!spawn3 || spawn3.parent_thread_id !== parentUuid)
9784
+ const spawn4 = meta?.payload?.source?.subagent?.thread_spawn;
9785
+ if (!spawn4 || spawn4.parent_thread_id !== parentUuid)
9786
9786
  continue;
9787
9787
  events.push(...this.parseRollout(path));
9788
9788
  }
@@ -9975,10 +9975,10 @@ var init_codex2 = __esm({
9975
9975
  const meta = JSON.parse(first);
9976
9976
  const payload = meta?.payload || {};
9977
9977
  projectPath = payload.cwd || "";
9978
- const spawn3 = payload.source?.subagent?.thread_spawn;
9979
- if (spawn3 || payload.agent_role || payload.agent_nickname) {
9978
+ const spawn4 = payload.source?.subagent?.thread_spawn;
9979
+ if (spawn4 || payload.agent_role || payload.agent_nickname) {
9980
9980
  isSubagent = true;
9981
- parentId = spawn3?.parent_thread_id || null;
9981
+ parentId = spawn4?.parent_thread_id || null;
9982
9982
  }
9983
9983
  }
9984
9984
  } catch {
@@ -13266,9 +13266,9 @@ function doResolve(realPath) {
13266
13266
  const bot = resolveBotWorktree(realPath);
13267
13267
  if (bot)
13268
13268
  return bot;
13269
- const git = resolveGit(realPath);
13270
- if (git)
13271
- return git;
13269
+ const git2 = resolveGit(realPath);
13270
+ if (git2)
13271
+ return git2;
13272
13272
  const ws = resolveAutoWorkspace(realPath, cfg.autoWorkspaceMinRepos ?? DEFAULT_AUTO_WORKSPACE_MIN_REPOS);
13273
13273
  if (ws)
13274
13274
  return ws;
@@ -13337,9 +13337,9 @@ function resolveGit(realPath) {
13337
13337
  const toplevel = findGitToplevel(realPath);
13338
13338
  if (!toplevel)
13339
13339
  return null;
13340
- const remote = getGitRemote(toplevel);
13341
- if (remote) {
13342
- const parsed = parseGitRemote(remote);
13340
+ const probe = getGitRemote(toplevel);
13341
+ if (probe.kind === "remote") {
13342
+ const parsed = parseGitRemote(probe.url);
13343
13343
  if (parsed) {
13344
13344
  return {
13345
13345
  id: `git:${parsed.host}/${parsed.owner}/${parsed.repo}`,
@@ -13348,6 +13348,9 @@ function resolveGit(realPath) {
13348
13348
  };
13349
13349
  }
13350
13350
  }
13351
+ if (probe.kind === "failed") {
13352
+ console.error(`[project] could not read git remotes in ${toplevel} \u2014 filing it as a local-only repo for now, which is probably wrong: ${probe.reason}`);
13353
+ }
13351
13354
  const hash = createHash6("sha1").update(toplevel).digest("hex").slice(0, 12);
13352
13355
  return {
13353
13356
  id: `git-local:${hash}`,
@@ -13366,23 +13369,39 @@ function findGitToplevel(realPath) {
13366
13369
  }
13367
13370
  return null;
13368
13371
  }
13372
+ function git(toplevel, args) {
13373
+ return execSync(`git ${args}`, {
13374
+ cwd: toplevel,
13375
+ stdio: ["ignore", "pipe", "ignore"],
13376
+ encoding: "utf-8",
13377
+ timeout: GIT_TIMEOUT_MS
13378
+ });
13379
+ }
13380
+ function probeGitRemote(toplevel) {
13381
+ let names;
13382
+ try {
13383
+ names = git(toplevel, "remote").split("\n").map((n) => n.trim()).filter(Boolean);
13384
+ } catch (err) {
13385
+ return { kind: "failed", reason: err instanceof Error ? err.message : String(err) };
13386
+ }
13387
+ if (names.length === 0)
13388
+ return { kind: "none" };
13389
+ const pick = names.includes("origin") ? "origin" : names[0];
13390
+ try {
13391
+ const url = git(toplevel, `remote get-url ${pick}`).trim();
13392
+ return url ? { kind: "remote", url } : { kind: "none" };
13393
+ } catch (err) {
13394
+ return { kind: "failed", reason: err instanceof Error ? err.message : String(err) };
13395
+ }
13396
+ }
13369
13397
  function getGitRemote(toplevel) {
13370
13398
  const cached = gitCache.get(toplevel);
13371
13399
  if (cached)
13372
- return cached.remote;
13373
- let remote = null;
13374
- try {
13375
- remote = execSync("git remote get-url origin", {
13376
- cwd: toplevel,
13377
- stdio: ["ignore", "pipe", "ignore"],
13378
- encoding: "utf-8",
13379
- timeout: 2e3
13380
- }).trim() || null;
13381
- } catch {
13382
- remote = null;
13383
- }
13384
- gitCache.set(toplevel, { remote });
13385
- return remote;
13400
+ return cached;
13401
+ const probe = probeGitRemote(toplevel);
13402
+ if (probe.kind !== "failed")
13403
+ gitCache.set(toplevel, probe);
13404
+ return probe;
13386
13405
  }
13387
13406
  function parseGitRemote(url) {
13388
13407
  const trimmed = url.trim().replace(/\.git$/, "");
@@ -13512,7 +13531,7 @@ function globToRegex(glob) {
13512
13531
  function escapeRe(s) {
13513
13532
  return s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
13514
13533
  }
13515
- var DEFAULT_AUTO_WORKSPACE_MIN_REPOS, BOT_WORKTREE_MARKERS, resolveCache, gitCache, workspaceCache, wsIdCache, cachedConfig, AUTO_WORKSPACE_MAX_DEPTH;
13534
+ var DEFAULT_AUTO_WORKSPACE_MIN_REPOS, BOT_WORKTREE_MARKERS, resolveCache, gitCache, workspaceCache, wsIdCache, cachedConfig, GIT_TIMEOUT_MS, AUTO_WORKSPACE_MAX_DEPTH;
13516
13535
  var init_project_resolver = __esm({
13517
13536
  "../engine/dist/core/project-resolver.js"() {
13518
13537
  "use strict";
@@ -13524,6 +13543,7 @@ var init_project_resolver = __esm({
13524
13543
  workspaceCache = /* @__PURE__ */ new Map();
13525
13544
  wsIdCache = /* @__PURE__ */ new Map();
13526
13545
  cachedConfig = null;
13546
+ GIT_TIMEOUT_MS = 1e4;
13527
13547
  AUTO_WORKSPACE_MAX_DEPTH = 2;
13528
13548
  }
13529
13549
  });
@@ -19418,7 +19438,7 @@ function pruneLedgerTargets(configuredServers) {
19418
19438
  return 0;
19419
19439
  }
19420
19440
  const rows = names.reduce((n, s) => n + Object.keys(orphans[s]).length, 0);
19421
- console.error(`[sync] ledger: parked ${rows} row(s) for ${names.length} server(s) no longer configured (${names.join(", ")}) in ${side}`);
19441
+ console.error(`[sync] tidied local bookkeeping: ${rows} already-synced marker(s) for ${names.length} server(s) you no longer sync to (${names.join(", ")}) moved to ${side}. Nothing was uploaded there; the file is kept so logging back in re-ships nothing.`);
19422
19442
  persistNow(data);
19423
19443
  return names.length;
19424
19444
  }
@@ -19730,7 +19750,7 @@ var init_http = __esm({
19730
19750
  CLI_VERSION = JSON.parse(readFileSync33(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19731
19751
  } catch {
19732
19752
  try {
19733
- if (true) CLI_VERSION = "0.5.27";
19753
+ if (true) CLI_VERSION = "0.5.29";
19734
19754
  } catch {
19735
19755
  }
19736
19756
  }
@@ -20242,7 +20262,7 @@ var init_telemetry = __esm({
20242
20262
  init_sync_client();
20243
20263
  cliVersion2 = () => {
20244
20264
  try {
20245
- return true ? "0.5.27" : "dev";
20265
+ return true ? "0.5.29" : "dev";
20246
20266
  } catch {
20247
20267
  return "dev";
20248
20268
  }
@@ -20253,6 +20273,18 @@ var init_telemetry = __esm({
20253
20273
  });
20254
20274
 
20255
20275
  // ../engine/dist/core/collector-health.js
20276
+ var collector_health_exports = {};
20277
+ __export(collector_health_exports, {
20278
+ CRASHLOOP_RESTARTS: () => CRASHLOOP_RESTARTS,
20279
+ STALE_AFTER_MS: () => STALE_AFTER_MS,
20280
+ collectorHealthPath: () => collectorHealthPath,
20281
+ judgeHealth: () => judgeHealth,
20282
+ progressLine: () => progressLine,
20283
+ readCollectorHealth: () => readCollectorHealth,
20284
+ reportWalkProgress: () => reportWalkProgress,
20285
+ updateCollectorHealth: () => updateCollectorHealth,
20286
+ writeCollectorHealth: () => writeCollectorHealth
20287
+ });
20256
20288
  import { readFileSync as readFileSync34, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8 } from "node:fs";
20257
20289
  import { join as join42, dirname as dirname17 } from "node:path";
20258
20290
  function progressLine(h) {
@@ -22238,7 +22270,7 @@ var init_sync_client = __esm({
22238
22270
  ownVersion = JSON.parse(readFileSync37(join44(dir, "../package.json"), "utf-8")).version || ownVersion;
22239
22271
  } catch {
22240
22272
  try {
22241
- if (true) ownVersion = "0.5.27";
22273
+ if (true) ownVersion = "0.5.29";
22242
22274
  } catch {
22243
22275
  }
22244
22276
  }
@@ -22410,6 +22442,43 @@ var init_which = __esm({
22410
22442
  }
22411
22443
  });
22412
22444
 
22445
+ // src/init-report.ts
22446
+ var init_report_exports = {};
22447
+ __export(init_report_exports, {
22448
+ createInitReporter: () => createInitReporter
22449
+ });
22450
+ function createInitReporter(opts) {
22451
+ let painted = false;
22452
+ const clear = () => {
22453
+ if (painted && opts.tty) opts.write(`\r${" ".repeat(INSTALLING.length)}\r`);
22454
+ painted = false;
22455
+ };
22456
+ return {
22457
+ start() {
22458
+ if (painted) return;
22459
+ if (opts.tty) {
22460
+ opts.write(INSTALLING);
22461
+ painted = true;
22462
+ } else opts.line(INSTALLING);
22463
+ },
22464
+ warn(text) {
22465
+ clear();
22466
+ opts.line(text);
22467
+ },
22468
+ done(text) {
22469
+ clear();
22470
+ opts.line(text);
22471
+ }
22472
+ };
22473
+ }
22474
+ var INSTALLING;
22475
+ var init_init_report = __esm({
22476
+ "src/init-report.ts"() {
22477
+ "use strict";
22478
+ INSTALLING = "Installing\u2026";
22479
+ }
22480
+ });
22481
+
22413
22482
  // ../engine/dist/core/mcp-clients.js
22414
22483
  var mcp_clients_exports = {};
22415
22484
  __export(mcp_clients_exports, {
@@ -22430,11 +22499,17 @@ function writeFileMkdir(path, body) {
22430
22499
  mkdirSync10(dirname20(path), { recursive: true });
22431
22500
  writeFileSync11(path, body);
22432
22501
  }
22502
+ function readJsonObject(path) {
22503
+ const raw = readFileSync39(path, "utf-8");
22504
+ if (raw.trim() === "")
22505
+ return {};
22506
+ return JSON.parse(raw);
22507
+ }
22433
22508
  function registerMcpServersJson(target, path, spec) {
22434
22509
  let cfg = {};
22435
22510
  if (existsSync44(path)) {
22436
22511
  try {
22437
- cfg = JSON.parse(readFileSync39(path, "utf-8"));
22512
+ cfg = readJsonObject(path);
22438
22513
  } catch (err) {
22439
22514
  return { id: target.id, label: target.label, path, state: "unparseable", error: String(err) };
22440
22515
  }
@@ -22463,7 +22538,7 @@ function registerOpencodeJson(path, spec) {
22463
22538
  let cfg = {};
22464
22539
  if (existsSync44(path)) {
22465
22540
  try {
22466
- cfg = JSON.parse(readFileSync39(path, "utf-8"));
22541
+ cfg = readJsonObject(path);
22467
22542
  } catch (err) {
22468
22543
  return { id, label, path, state: "unparseable", error: String(err) };
22469
22544
  }
@@ -23957,6 +24032,66 @@ var init_toolkit_pull = __esm({
23957
24032
  }
23958
24033
  });
23959
24034
 
24035
+ // src/init-progress.ts
24036
+ var init_progress_exports = {};
24037
+ __export(init_progress_exports, {
24038
+ handOffLine: () => handOffLine,
24039
+ progressLine: () => progressLine2,
24040
+ renderBar: () => renderBar,
24041
+ renderCount: () => renderCount,
24042
+ spawnDetachedSync: () => spawnDetachedSync,
24043
+ watchFirstSync: () => watchFirstSync
24044
+ });
24045
+ import { spawn as spawn2 } from "node:child_process";
24046
+ function renderBar(done, total, width = 12) {
24047
+ if (total <= 0) return "\u2591".repeat(width);
24048
+ const ratio = Math.max(0, Math.min(1, done / total));
24049
+ const filled = Math.round(ratio * width);
24050
+ return "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
24051
+ }
24052
+ function renderCount(done, total) {
24053
+ const n = (x) => x.toLocaleString("en-US");
24054
+ return `${n(done)}/${n(total)}`;
24055
+ }
24056
+ function progressLine2(snap) {
24057
+ if (!snap || snap.total <= 0) return "starting\u2026";
24058
+ if (snap.complete) return `${renderCount(snap.done, snap.total)} indexed`;
24059
+ return `${renderBar(snap.done, snap.total)} ${renderCount(snap.done, snap.total)}`;
24060
+ }
24061
+ function handOffLine(snap) {
24062
+ const where = snap && snap.total > 0 ? `${renderCount(snap.done, snap.total)} so far` : "started";
24063
+ return `${where} \u2014 continuing in the background`;
24064
+ }
24065
+ async function watchFirstSync(deps) {
24066
+ const handOffAfterMs = deps.handOffAfterMs ?? 2e4;
24067
+ const pollMs = deps.pollMs ?? 400;
24068
+ const started = deps.now();
24069
+ deps.startWorker();
24070
+ let snap = null;
24071
+ for (; ; ) {
24072
+ snap = deps.readProgress();
24073
+ if (snap?.complete) return { finished: true, snapshot: snap };
24074
+ if (deps.now() - started >= handOffAfterMs) return { finished: false, snapshot: snap };
24075
+ deps.draw(progressLine2(snap));
24076
+ await deps.sleep(pollMs);
24077
+ }
24078
+ }
24079
+ function spawnDetachedSync(extraArgs = []) {
24080
+ const entry = process.argv[1];
24081
+ if (!entry) return;
24082
+ const child = spawn2(process.execPath, [entry, "sync", ...extraArgs], {
24083
+ detached: true,
24084
+ stdio: "ignore",
24085
+ windowsHide: true
24086
+ });
24087
+ child.unref();
24088
+ }
24089
+ var init_init_progress = __esm({
24090
+ "src/init-progress.ts"() {
24091
+ "use strict";
24092
+ }
24093
+ });
24094
+
23960
24095
  // src/repair.ts
23961
24096
  var repair_exports = {};
23962
24097
  __export(repair_exports, {
@@ -24572,7 +24707,7 @@ __export(collector_exports, {
24572
24707
  isSecretRule: () => isSecretRule,
24573
24708
  resolveCodeindexBin: () => resolveCodeindexBin
24574
24709
  });
24575
- import { spawn as spawn2 } from "node:child_process";
24710
+ import { spawn as spawn3 } from "node:child_process";
24576
24711
  import { execSync as execSync4 } from "node:child_process";
24577
24712
  import { readFileSync as readFileSync44 } from "node:fs";
24578
24713
  import { join as join53, basename as basename28 } from "node:path";
@@ -24587,7 +24722,7 @@ async function resolveCodeindexBin(autoInstall = true) {
24587
24722
  }
24588
24723
  function runMcp(binPath, workspace, calls, timeoutMs = 18e4) {
24589
24724
  return new Promise((resolve3, reject) => {
24590
- const p = spawn2(binPath, ["--mcp", "--workspace", workspace], { stdio: ["pipe", "pipe", "ignore"] });
24725
+ const p = spawn3(binPath, ["--mcp", "--workspace", workspace], { stdio: ["pipe", "pipe", "ignore"] });
24591
24726
  const res = /* @__PURE__ */ new Map();
24592
24727
  let pending = "";
24593
24728
  let failed = false;
@@ -25892,7 +26027,7 @@ var init_intent_drain = __esm({
25892
26027
  ownVersion2 = JSON.parse(readFileSync46(join55(dir, "../package.json"), "utf-8")).version || ownVersion2;
25893
26028
  } catch {
25894
26029
  try {
25895
- if (true) ownVersion2 = "0.5.27";
26030
+ if (true) ownVersion2 = "0.5.29";
25896
26031
  } catch {
25897
26032
  }
25898
26033
  }
@@ -27603,6 +27738,7 @@ var DEFAULT_ALLOW = [
27603
27738
  "recall_task_create",
27604
27739
  "recall_task_update",
27605
27740
  "recall_task_comment",
27741
+ "recall_task_policy",
27606
27742
  "recall_help",
27607
27743
  "recall_smart_resume",
27608
27744
  "recall_project_context",
@@ -27653,7 +27789,6 @@ var DEFAULT_ALLOW = [
27653
27789
  "recall_toolkit_status",
27654
27790
  "recall_toolkit_sync"
27655
27791
  ];
27656
- var SELF_HOST_DOCS = "https://github.com/munhq/chat-recall/blob/main/docs/SELF_HOSTING.md";
27657
27792
  function enforceNodeVersion() {
27658
27793
  const required = Number(/(\d+)/.exec(String(pkgEngines))?.[1] ?? 0);
27659
27794
  const found = Number(process.versions.node.split(".")[0]);
@@ -27669,8 +27804,13 @@ program.name("chat-recall").description("Semantic search for Claude Code session
27669
27804
  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) => {
27670
27805
  try {
27671
27806
  console.log(chalk.bold("chat-recall init") + chalk.dim(` v${pkgVersion}`));
27672
- console.log();
27673
- console.log(chalk.bold("1. Detecting AI tools..."));
27807
+ const { createInitReporter: createInitReporter2 } = await Promise.resolve().then(() => (init_init_report(), init_report_exports));
27808
+ const rep = createInitReporter2({
27809
+ tty: process.stdout.isTTY === true,
27810
+ write: (t) => process.stdout.write(t),
27811
+ line: (t) => console.log(t)
27812
+ });
27813
+ rep.start();
27674
27814
  const clis = [
27675
27815
  { name: "Gemini CLI", cmd: "gemini", available: false },
27676
27816
  { name: "Claude CLI", cmd: "claude", available: false },
@@ -27686,12 +27826,9 @@ program.command("init").description("Set up chat-recall: connect to your server,
27686
27826
  } catch {
27687
27827
  }
27688
27828
  }
27689
- for (const cli of clis) {
27690
- const icon = cli.available ? chalk.green("found") : chalk.dim("not found");
27691
- console.log(` ${cli.name}: ${icon}`);
27692
- }
27693
- console.log();
27694
- console.log(chalk.bold("2. Connecting to your server..."));
27829
+ 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}`);
27695
27832
  let target = firstTarget();
27696
27833
  let deadTarget = false;
27697
27834
  if (options.server) {
@@ -27699,25 +27836,20 @@ program.command("init").description("Set up chat-recall: connect to your server,
27699
27836
  await runLogin(options.server, { token: options.token, fromInit: true, promptJson: options.promptJson });
27700
27837
  target = firstTarget();
27701
27838
  } catch (e) {
27702
- console.log(` ${chalk.yellow("Not connected.")} ${describeProbeError(e)}`);
27703
- console.log(` ${chalk.dim(`Run ${chalk.bold(`chat-recall login ${options.server}`)} when ready, or self-host: ${SELF_HOST_DOCS}`)}`);
27839
+ serverBad(describeProbeError(e));
27704
27840
  }
27705
27841
  } else if (!target) {
27706
- console.log(` Connecting to ${chalk.bold(DEFAULT_SERVER)} \u2014 sign in to approve this machine.`);
27707
- console.log(` ${chalk.dim(`Prefer your own server? ${chalk.bold("--server <url>")}. Self-hosting is free: ${SELF_HOST_DOCS}`)}`);
27708
27842
  try {
27709
27843
  await runLogin(DEFAULT_SERVER, { token: options.token, fromInit: true, promptJson: options.promptJson });
27710
27844
  target = firstTarget();
27711
27845
  } catch (e) {
27712
- console.log(` ${chalk.yellow("Not connected.")} ${describeProbeError(e)}`);
27713
- console.log(` ${chalk.dim(`Run ${chalk.bold("chat-recall login <server-url>")} when ready, or self-host: ${SELF_HOST_DOCS}`)}`);
27846
+ serverBad(describeProbeError(e));
27714
27847
  }
27715
27848
  }
27716
27849
  if (target && !options.server) {
27717
27850
  const { probeServer: probeServer2, probeOk: probeOk2, probeAdvice: probeAdvice2 } = await Promise.resolve().then(() => (init_server_probe(), server_probe_exports));
27718
27851
  const probe = await probeServer2(target.base, { timeoutMs: 1e4 });
27719
27852
  if (probeOk2(probe)) {
27720
- console.log(` ${chalk.green("Connected")} \u2192 ${target.base}`);
27721
27853
  } else if (probe.kind === "moved" || probe.kind === "not_api") {
27722
27854
  const retired = target.base;
27723
27855
  console.log(` ${chalk.yellow("The saved server no longer serves chat-recall")} \u2192 ${retired}`);
@@ -27732,7 +27864,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
27732
27864
  target = firstTarget();
27733
27865
  if (target) console.log(` ${chalk.green("Connected")} \u2192 ${target.base}`);
27734
27866
  } catch (e) {
27735
- console.log(` ${chalk.yellow("Not connected.")} ${describeProbeError(e)}`);
27867
+ serverBad(describeProbeError(e));
27736
27868
  console.log(` ${chalk.dim(`Run ${chalk.bold(`chat-recall login ${DEFAULT_SERVER}`)} when ready, then ${chalk.bold(`chat-recall logout ${retired}`)}.`)}`);
27737
27869
  deadTarget = true;
27738
27870
  }
@@ -27741,11 +27873,8 @@ program.command("init").description("Set up chat-recall: connect to your server,
27741
27873
  console.log(` ${probeAdvice2(probe, target.base)}`);
27742
27874
  }
27743
27875
  } else if (target) {
27744
- console.log(` ${chalk.green("Connected")} \u2192 ${target.base}`);
27745
27876
  }
27746
- console.log();
27747
27877
  if (!options.skipMcp) {
27748
- console.log(chalk.bold("3. Configuring MCP server..."));
27749
27878
  const projectRoot = join60(import.meta.dirname, "..");
27750
27879
  let mcpBinOnPath = false;
27751
27880
  mcpBinOnPath = isOnPath("chat-recall-mcp");
@@ -27764,19 +27893,22 @@ program.command("init").description("Set up chat-recall: connect to your server,
27764
27893
  { ...launch, env: MCP_ENV, alwaysAllow: DEFAULT_ALLOW },
27765
27894
  { extraIds: detectedClients }
27766
27895
  );
27767
- for (const r of results) {
27768
- const word = r.state === "current" ? chalk.green("already configured") : r.state === "created" ? chalk.green("configured") : r.state === "repaired" ? chalk.green("repaired") : chalk.yellow("left alone \u2014 file does not parse");
27769
- console.log(` ${r.label}: ${word} \u2192 ${r.path}`);
27896
+ const ok = results.filter((r) => r.state !== "unparseable");
27897
+ const bad = results.filter((r) => r.state === "unparseable");
27898
+ if (ok.length) {
27899
+ }
27900
+ for (const r of bad) {
27901
+ 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)`)}`);
27904
+ }
27905
+ if (process.env.CHAT_RECALL_VERBOSE) {
27770
27906
  }
27771
- console.log(chalk.dim(` Launch: ${launch.command}${launch.args ? " " + launch.args.join(" ") : ""}`));
27772
27907
  } else {
27773
- console.log(chalk.bold("3. Skipping MCP configuration (--skip-mcp)"));
27774
27908
  }
27775
- console.log();
27776
27909
  const skipCodeindex = options.skipCodeindex === true;
27777
27910
  const forceInstall = options.withCodeindex === true || process.env.CHAT_RECALL_WITH_CODEINDEX === "1";
27778
27911
  if (skipCodeindex) {
27779
- console.log(chalk.bold("4. Skipping codeindex companion (--skip-codeindex)"));
27780
27912
  } else {
27781
27913
  const {
27782
27914
  checkCodeindexStatus: checkCodeindexStatus2,
@@ -27786,64 +27918,46 @@ program.command("init").description("Set up chat-recall: connect to your server,
27786
27918
  } = await Promise.resolve().then(() => (init_companions(), companions_exports));
27787
27919
  const detected = checkCodeindexStatus2();
27788
27920
  if (detected.installed && !forceInstall) {
27789
- console.log(chalk.bold("4. Detected codeindex companion"));
27790
- console.log(` ${chalk.green("codeindex")}: ${detected.path}`);
27791
27921
  if (!options.skipMcp) {
27792
27922
  const mcpJsonPath = join60(homedir22(), ".mcp.json");
27793
27923
  const reg = registerCodeindexMcp2(mcpJsonPath, detected.path);
27794
- if (reg.added) console.log(` ${chalk.green("codeindex MCP server registered")} in ${mcpJsonPath}`);
27795
- else console.log(` codeindex MCP server: ${chalk.green("already registered")}`);
27796
27924
  }
27797
27925
  } else if (forceInstall) {
27798
- console.log(chalk.bold("4. Installing codeindex companion (--with-codeindex)..."));
27799
27926
  try {
27800
27927
  const result = await installCodeindex2({ force: true });
27801
27928
  if (result.installed) {
27802
27929
  const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
27803
- console.log(` ${chalk.green("codeindex installed")} (${sizeMb}) \u2192 ${result.path}`);
27804
27930
  if (!options.skipMcp) {
27805
27931
  const mcpJsonPath = join60(homedir22(), ".mcp.json");
27806
27932
  const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
27807
- if (reg.added) console.log(` ${chalk.green("codeindex MCP server registered")} in ${mcpJsonPath}`);
27808
27933
  }
27809
27934
  } else if (!result.prebuiltAvailable) {
27810
- console.log(` ${chalk.yellow("codeindex")}: ${result.unsupportedReason}`);
27935
+ rep.warn(`${chalk.yellow("codeindex")}: ${result.unsupportedReason}`);
27811
27936
  }
27812
27937
  } catch (err) {
27813
27938
  const msg = String(err);
27814
- console.log(` ${chalk.yellow("codeindex install failed")}: ${msg}`);
27939
+ rep.warn(`${chalk.yellow("codeindex install failed")}: ${msg}`);
27815
27940
  if (msg.includes("404")) {
27816
27941
  console.log(chalk.dim(" The codeindex release returned 404. If the repo is private,"));
27817
27942
  console.log(chalk.dim(" authenticate `gh` to it, or build from source."));
27818
27943
  }
27819
27944
  }
27820
27945
  } else {
27821
- console.log(chalk.bold("4. codeindex companion: not installed"));
27822
- console.log(chalk.dim(" codeindex is a separate MCP server for code-level lookup \u2014"));
27823
- console.log(chalk.dim(" find_symbol, plan_change, get_change_impact, etc."));
27824
- console.log(chalk.dim(" To install: `chat-recall init --with-codeindex`"));
27825
- console.log(chalk.dim(" Or grab the binary from https://github.com/munhq/codeindex"));
27826
27946
  }
27827
27947
  }
27828
- console.log();
27829
- console.log(chalk.bold("Installing chat-recall skills into your AI tools..."));
27830
27948
  try {
27831
27949
  const { installSkills: installSkills2 } = await Promise.resolve().then(() => (init_install_skills(), install_skills_exports));
27832
27950
  const r = installSkills2();
27833
27951
  const hits = r.perTarget.filter((t) => t.installed.length > 0);
27834
27952
  if (hits.length) {
27835
- for (const t of hits) console.log(` ${chalk.green(t.label)}: ${t.installed.length} skill(s) \u2192 ${t.dir}`);
27953
+ const skills = Math.max(...hits.map((t) => t.installed.length));
27836
27954
  } else {
27837
- console.log(chalk.dim(" No supported AI tools detected \u2014 skills not installed."));
27838
27955
  }
27839
27956
  const userOwned = [...new Set(r.perTarget.flatMap((t) => t.skippedUserOwned))];
27840
- if (userOwned.length) console.log(chalk.yellow(` Kept your existing skill(s) of the same name: ${userOwned.join(", ")}`));
27841
27957
  } catch (err) {
27842
- console.log(` ${chalk.yellow("Skill install failed")} \u2014 ${err instanceof Error ? err.message : err}`);
27958
+ rep.warn(`${chalk.yellow("Skill install failed")} \u2014 ${err instanceof Error ? err.message : err}`);
27843
27959
  }
27844
- console.log();
27845
27960
  if (!options.skipMcp && firstTarget()) {
27846
- console.log(chalk.bold("Installing your other MCP servers from your account..."));
27847
27961
  try {
27848
27962
  const { loadAllCredentials: loadAllCredentials2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
27849
27963
  const { executePull: executePull2 } = await Promise.resolve().then(() => (init_toolkit_pull(), toolkit_pull_exports));
@@ -27879,66 +27993,59 @@ program.command("init").description("Set up chat-recall: connect to your server,
27879
27993
  const written = report.outcomes.filter((o) => o.status === "written");
27880
27994
  const present = report.outcomes.filter((o) => o.status === "present").length;
27881
27995
  if (answered === 0) {
27882
- console.log(` ${chalk.yellow("Could not ask your server what MCP servers are on your account")}`);
27996
+ rep.warn(chalk.yellow("Could not ask your server what MCP servers are on your account"));
27883
27997
  for (const f of askFailed) console.log(` ${chalk.dim(f)}`);
27884
27998
  console.log(` ${chalk.dim("Re-run `chat-recall toolkit pull` once the server answers.")}`);
27885
27999
  } else if (written.length === 0 && present === 0) {
27886
- console.log(chalk.dim(" Nothing on your account to install yet."));
27887
28000
  } else {
27888
- console.log(` ${chalk.green(`Installed ${written.length}`)} ${chalk.dim(`(${present} already present)`)}`);
27889
28001
  const envVars = [...new Set(written.flatMap((o) => o.needsEnv || []))].sort();
27890
28002
  if (envVars.length) {
27891
- console.log(` ${chalk.yellow("Set these env vars \u2014 their values were never uploaded:")} ${envVars.join(", ")}`);
28003
+ rep.warn(`${chalk.yellow("Set these env vars \u2014 their values were never uploaded:")} ${envVars.join(", ")}`);
27892
28004
  }
27893
28005
  }
27894
28006
  } catch (err) {
27895
- console.log(` ${chalk.yellow("Could not install account MCPs")} \u2014 ${err instanceof Error ? err.message : err}`);
28007
+ rep.warn(`${chalk.yellow("Could not install account MCPs")} \u2014 ${err instanceof Error ? err.message : err}`);
27896
28008
  console.log(` ${chalk.dim("Re-run `chat-recall toolkit pull` later.")}`);
27897
28009
  }
27898
- console.log();
27899
28010
  }
27900
28011
  if (deadTarget) {
27901
- console.log(chalk.bold("5. Skipping first sync \u2014 the saved server is not serving chat-recall"));
28012
+ console.log(chalk.bold("Skipping first sync \u2014 the saved server is not serving chat-recall"));
27902
28013
  console.log(` ${chalk.dim(`Re-point with the two commands above, then run ${chalk.bold("chat-recall sync")}.`)}`);
27903
28014
  } else if (!options.skipSync && firstTarget()) {
27904
- console.log(chalk.bold("5. Indexing and uploading your sessions"));
27905
- try {
27906
- const { syncSessions: syncSessions2 } = await Promise.resolve().then(() => (init_sync_client(), sync_client_exports));
27907
- const r = await syncSessions2();
27908
- console.log(` ${chalk.green(`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)` : ""}`)}`);
27909
- } catch (err) {
27910
- console.log(` ${chalk.yellow("Sync failed")} \u2014 ${err instanceof Error ? err.message : err}`);
27911
- console.log(` ${chalk.dim("Re-run `chat-recall sync` once your server is reachable.")}`);
28015
+ const { watchFirstSync: watchFirstSync2, spawnDetachedSync: spawnDetachedSync2, handOffLine: handOffLine2, progressLine: renderProgress } = await Promise.resolve().then(() => (init_init_progress(), init_progress_exports));
28016
+ const { readCollectorHealth: readCollectorHealth2 } = await Promise.resolve().then(() => (init_collector_health(), collector_health_exports));
28017
+ const label = (text) => `${chalk.bold("Indexing")} ${text}`;
28018
+ const tty = process.stdout.isTTY === true;
28019
+ const outcome = await watchFirstSync2({
28020
+ startWorker: () => spawnDetachedSync2(),
28021
+ readProgress: () => {
28022
+ const p = readCollectorHealth2()?.progress;
28023
+ return p ? { done: p.done, total: p.total, complete: p.complete } : null;
28024
+ },
28025
+ // In place on a terminal; a single final line when piped, so a log or
28026
+ // a CI transcript does not collect one line per poll.
28027
+ draw: (text) => {
28028
+ if (tty) process.stdout.write(`\r${label(text)} `);
28029
+ },
28030
+ now: () => Date.now(),
28031
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms))
28032
+ });
28033
+ if (tty) process.stdout.write("\r");
28034
+ if (outcome.finished) {
28035
+ console.log(label(chalk.green(renderProgress(outcome.snapshot))));
28036
+ } else {
28037
+ console.log(label(chalk.dim(handOffLine2(outcome.snapshot))));
28038
+ console.log(` ${chalk.dim("watch it with: " + chalk.bold("chat-recall status"))}`);
28039
+ }
28040
+ const failed = Object.entries(readCollectorHealth2()?.targets ?? {}).filter(([, t]) => (t.failures ?? 0) > 0 && t.lastError);
28041
+ for (const [server, t] of failed) {
28042
+ console.log(` ${chalk.yellow("sync error")} ${chalk.dim(server)}: ${t.lastError}`);
27912
28043
  }
27913
28044
  } else if (!options.skipSync) {
27914
- console.log(chalk.bold("5. Skipping first sync (not logged in)"));
27915
28045
  } else {
27916
- console.log(chalk.bold("5. Skipping first sync (--skip-sync)"));
27917
28046
  }
27918
- console.log();
27919
- console.log(chalk.bold("6. Background sync"));
27920
- console.log(chalk.dim(" Runs via the MCP server while Claude Code is open (no extra service to install)."));
27921
- console.log(chalk.dim(" Always-on / headless? Opt in with `chat-recall watch --install-service`."));
27922
28047
  void options.skipService;
27923
- console.log();
27924
- console.log(chalk.green.bold("Setup complete!"));
27925
- console.log();
27926
- console.log(`chat-recall MCP tools (${DEFAULT_ALLOW.length}):`);
27927
- for (let i = 0; i < DEFAULT_ALLOW.length; i += 3) {
27928
- console.log(" " + DEFAULT_ALLOW.slice(i, i + 3).join(", "));
27929
- }
27930
- if (!skipCodeindex) {
27931
- const { checkCodeindexStatus: checkCodeindexStatus2 } = await Promise.resolve().then(() => (init_companions(), companions_exports));
27932
- if (checkCodeindexStatus2().installed) {
27933
- console.log();
27934
- console.log("codeindex MCP tools (16): status, search, find_symbol, find_word, get_outline,");
27935
- console.log(" get_tree, get_imports, get_imported_by, find_callers, plan_change,");
27936
- console.log(" get_hot_files, index_workspace, analyze, read_file, read_symbol,");
27937
- console.log(" get_change_impact");
27938
- }
27939
- }
27940
- console.log();
27941
- console.log(`${chalk.dim("Background sync is running \u2014 new conversations ship automatically. Manage it with `chat-recall service status|uninstall`.")}`);
28048
+ rep.done(chalk.green.bold("\u2713 Done"));
27942
28049
  } catch (err) {
27943
28050
  console.error(chalk.red("Error:"), err);
27944
28051
  process.exit(1);
@@ -29850,13 +29957,13 @@ async function runLogin(serverUrl, opts) {
29850
29957
  return;
29851
29958
  }
29852
29959
  console.log();
29853
- console.log(chalk.bold("To log in, open:"));
29960
+ console.log(chalk.bold("Open this to approve the machine:"));
29854
29961
  console.log(" " + chalk.cyan(p.url));
29855
- console.log(chalk.dim(` (if prompted, enter code: ${chalk.bold(p.userCode)} at ${p.verificationUri})`));
29856
- const opened = openBrowser(p.url);
29857
- if (opened) console.log(chalk.dim(" (opening your browser\u2026 if nothing happens, use the link above)"));
29858
- console.log(chalk.dim("After approving, come back to THIS terminal \u2014 it finishes the login by itself."));
29859
- console.log(chalk.dim("Waiting for approval\u2026"));
29962
+ if (openBrowser(p.url)) {
29963
+ console.log(chalk.dim(" (tried to open it for you \u2014 if no window appeared, use the link above)"));
29964
+ }
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"));
29860
29967
  };
29861
29968
  const tokens = authProvider === "better-auth" ? await betterAuthDeviceLogin2(base2, onPrompt) : await deviceLogin2({ issuer, clientId: opts.clientId }, onPrompt);
29862
29969
  const authHdr = { authorization: `Bearer ${tokens.accessToken}`, "content-type": "application/json" };