chat-recall 0.7.16 → 0.7.18

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
@@ -2034,7 +2034,7 @@ var require_thread_stream = __commonJS({
2034
2034
  var { version: version2 } = require_package();
2035
2035
  var { EventEmitter } = __require("events");
2036
2036
  var { Worker: Worker2 } = __require("worker_threads");
2037
- var { join: join62 } = __require("path");
2037
+ var { join: join63 } = __require("path");
2038
2038
  var { pathToFileURL } = __require("url");
2039
2039
  var { wait } = require_wait();
2040
2040
  var {
@@ -2077,7 +2077,7 @@ var require_thread_stream = __commonJS({
2077
2077
  function createWorker(stream, opts) {
2078
2078
  const { filename, workerData } = opts;
2079
2079
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
2080
- const toExecute = bundlerOverrides["thread-stream-worker"] || join62(__dirname, "lib", "worker.js");
2080
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join63(__dirname, "lib", "worker.js");
2081
2081
  const worker = new Worker2(toExecute, {
2082
2082
  ...opts.workerOpts,
2083
2083
  trackUnmanagedFds: false,
@@ -2480,7 +2480,7 @@ var require_transport = __commonJS({
2480
2480
  "use strict";
2481
2481
  var { createRequire: createRequire3 } = __require("module");
2482
2482
  var getCallers = require_caller();
2483
- var { join: join62, isAbsolute: isAbsolute4, sep: sep4 } = __require("node:path");
2483
+ var { join: join63, isAbsolute: isAbsolute4, sep: sep4 } = __require("node:path");
2484
2484
  var sleep2 = require_atomic_sleep();
2485
2485
  var onExit = require_on_exit_leak_free();
2486
2486
  var ThreadStream = require_thread_stream();
@@ -2543,7 +2543,7 @@ var require_transport = __commonJS({
2543
2543
  throw new Error("only one of target or targets can be specified");
2544
2544
  }
2545
2545
  if (targets) {
2546
- target = bundlerOverrides["pino-worker"] || join62(__dirname, "worker.js");
2546
+ target = bundlerOverrides["pino-worker"] || join63(__dirname, "worker.js");
2547
2547
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
2548
2548
  return {
2549
2549
  ...dest,
@@ -2561,7 +2561,7 @@ var require_transport = __commonJS({
2561
2561
  });
2562
2562
  });
2563
2563
  } else if (pipeline) {
2564
- target = bundlerOverrides["pino-worker"] || join62(__dirname, "worker.js");
2564
+ target = bundlerOverrides["pino-worker"] || join63(__dirname, "worker.js");
2565
2565
  options.pipelines = [pipeline.map((dest) => {
2566
2566
  return {
2567
2567
  ...dest,
@@ -2583,7 +2583,7 @@ var require_transport = __commonJS({
2583
2583
  return origin;
2584
2584
  }
2585
2585
  if (origin === "pino/file") {
2586
- return join62(__dirname, "..", "file.js");
2586
+ return join63(__dirname, "..", "file.js");
2587
2587
  }
2588
2588
  let fixTarget2;
2589
2589
  for (const filePath of callers) {
@@ -3572,7 +3572,7 @@ var require_safe_stable_stringify = __commonJS({
3572
3572
  return circularValue;
3573
3573
  }
3574
3574
  let res = "";
3575
- let join62 = ",";
3575
+ let join63 = ",";
3576
3576
  const originalIndentation = indentation;
3577
3577
  if (Array.isArray(value)) {
3578
3578
  if (value.length === 0) {
@@ -3586,7 +3586,7 @@ var require_safe_stable_stringify = __commonJS({
3586
3586
  indentation += spacer;
3587
3587
  res += `
3588
3588
  ${indentation}`;
3589
- join62 = `,
3589
+ join63 = `,
3590
3590
  ${indentation}`;
3591
3591
  }
3592
3592
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3594,13 +3594,13 @@ ${indentation}`;
3594
3594
  for (; i < maximumValuesToStringify - 1; i++) {
3595
3595
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3596
3596
  res += tmp2 !== void 0 ? tmp2 : "null";
3597
- res += join62;
3597
+ res += join63;
3598
3598
  }
3599
3599
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
3600
3600
  res += tmp !== void 0 ? tmp : "null";
3601
3601
  if (value.length - 1 > maximumBreadth) {
3602
3602
  const removedKeys = value.length - maximumBreadth - 1;
3603
- res += `${join62}"... ${getItemCount(removedKeys)} not stringified"`;
3603
+ res += `${join63}"... ${getItemCount(removedKeys)} not stringified"`;
3604
3604
  }
3605
3605
  if (spacer !== "") {
3606
3606
  res += `
@@ -3621,7 +3621,7 @@ ${originalIndentation}`;
3621
3621
  let separator = "";
3622
3622
  if (spacer !== "") {
3623
3623
  indentation += spacer;
3624
- join62 = `,
3624
+ join63 = `,
3625
3625
  ${indentation}`;
3626
3626
  whitespace = " ";
3627
3627
  }
@@ -3635,13 +3635,13 @@ ${indentation}`;
3635
3635
  const tmp = stringifyFnReplacer(key3, value, stack, replacer, spacer, indentation);
3636
3636
  if (tmp !== void 0) {
3637
3637
  res += `${separator}${strEscape(key3)}:${whitespace}${tmp}`;
3638
- separator = join62;
3638
+ separator = join63;
3639
3639
  }
3640
3640
  }
3641
3641
  if (keyLength > maximumBreadth) {
3642
3642
  const removedKeys = keyLength - maximumBreadth;
3643
3643
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
3644
- separator = join62;
3644
+ separator = join63;
3645
3645
  }
3646
3646
  if (spacer !== "" && separator.length > 1) {
3647
3647
  res = `
@@ -3682,7 +3682,7 @@ ${originalIndentation}`;
3682
3682
  }
3683
3683
  const originalIndentation = indentation;
3684
3684
  let res = "";
3685
- let join62 = ",";
3685
+ let join63 = ",";
3686
3686
  if (Array.isArray(value)) {
3687
3687
  if (value.length === 0) {
3688
3688
  return "[]";
@@ -3695,7 +3695,7 @@ ${originalIndentation}`;
3695
3695
  indentation += spacer;
3696
3696
  res += `
3697
3697
  ${indentation}`;
3698
- join62 = `,
3698
+ join63 = `,
3699
3699
  ${indentation}`;
3700
3700
  }
3701
3701
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -3703,13 +3703,13 @@ ${indentation}`;
3703
3703
  for (; i < maximumValuesToStringify - 1; i++) {
3704
3704
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3705
3705
  res += tmp2 !== void 0 ? tmp2 : "null";
3706
- res += join62;
3706
+ res += join63;
3707
3707
  }
3708
3708
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
3709
3709
  res += tmp !== void 0 ? tmp : "null";
3710
3710
  if (value.length - 1 > maximumBreadth) {
3711
3711
  const removedKeys = value.length - maximumBreadth - 1;
3712
- res += `${join62}"... ${getItemCount(removedKeys)} not stringified"`;
3712
+ res += `${join63}"... ${getItemCount(removedKeys)} not stringified"`;
3713
3713
  }
3714
3714
  if (spacer !== "") {
3715
3715
  res += `
@@ -3722,7 +3722,7 @@ ${originalIndentation}`;
3722
3722
  let whitespace = "";
3723
3723
  if (spacer !== "") {
3724
3724
  indentation += spacer;
3725
- join62 = `,
3725
+ join63 = `,
3726
3726
  ${indentation}`;
3727
3727
  whitespace = " ";
3728
3728
  }
@@ -3731,7 +3731,7 @@ ${indentation}`;
3731
3731
  const tmp = stringifyArrayReplacer(key3, value[key3], stack, replacer, spacer, indentation);
3732
3732
  if (tmp !== void 0) {
3733
3733
  res += `${separator}${strEscape(key3)}:${whitespace}${tmp}`;
3734
- separator = join62;
3734
+ separator = join63;
3735
3735
  }
3736
3736
  }
3737
3737
  if (spacer !== "" && separator.length > 1) {
@@ -3789,20 +3789,20 @@ ${originalIndentation}`;
3789
3789
  indentation += spacer;
3790
3790
  let res2 = `
3791
3791
  ${indentation}`;
3792
- const join63 = `,
3792
+ const join64 = `,
3793
3793
  ${indentation}`;
3794
3794
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
3795
3795
  let i = 0;
3796
3796
  for (; i < maximumValuesToStringify - 1; i++) {
3797
3797
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3798
3798
  res2 += tmp2 !== void 0 ? tmp2 : "null";
3799
- res2 += join63;
3799
+ res2 += join64;
3800
3800
  }
3801
3801
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
3802
3802
  res2 += tmp !== void 0 ? tmp : "null";
3803
3803
  if (value.length - 1 > maximumBreadth) {
3804
3804
  const removedKeys = value.length - maximumBreadth - 1;
3805
- res2 += `${join63}"... ${getItemCount(removedKeys)} not stringified"`;
3805
+ res2 += `${join64}"... ${getItemCount(removedKeys)} not stringified"`;
3806
3806
  }
3807
3807
  res2 += `
3808
3808
  ${originalIndentation}`;
@@ -3818,16 +3818,16 @@ ${originalIndentation}`;
3818
3818
  return '"[Object]"';
3819
3819
  }
3820
3820
  indentation += spacer;
3821
- const join62 = `,
3821
+ const join63 = `,
3822
3822
  ${indentation}`;
3823
3823
  let res = "";
3824
3824
  let separator = "";
3825
3825
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
3826
3826
  if (isTypedArrayWithEntries(value)) {
3827
- res += stringifyTypedArray(value, join62, maximumBreadth);
3827
+ res += stringifyTypedArray(value, join63, maximumBreadth);
3828
3828
  keys = keys.slice(value.length);
3829
3829
  maximumPropertiesToStringify -= value.length;
3830
- separator = join62;
3830
+ separator = join63;
3831
3831
  }
3832
3832
  if (deterministic) {
3833
3833
  keys = sort(keys, comparator);
@@ -3838,13 +3838,13 @@ ${indentation}`;
3838
3838
  const tmp = stringifyIndent(key3, value[key3], stack, spacer, indentation);
3839
3839
  if (tmp !== void 0) {
3840
3840
  res += `${separator}${strEscape(key3)}: ${tmp}`;
3841
- separator = join62;
3841
+ separator = join63;
3842
3842
  }
3843
3843
  }
3844
3844
  if (keyLength > maximumBreadth) {
3845
3845
  const removedKeys = keyLength - maximumBreadth;
3846
3846
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
3847
- separator = join62;
3847
+ separator = join63;
3848
3848
  }
3849
3849
  if (separator !== "") {
3850
3850
  res = `
@@ -6617,6 +6617,7 @@ var init_from_events = __esm({
6617
6617
  // ../engine/dist/transcript/raw.js
6618
6618
  var raw_exports = {};
6619
6619
  __export(raw_exports, {
6620
+ RAW_PARSE_MAX_BYTES: () => RAW_PARSE_MAX_BYTES,
6620
6621
  archiveRawSession: () => archiveRawSession,
6621
6622
  buildRawContainer: () => buildRawContainer,
6622
6623
  containerSrcHash: () => containerSrcHash,
@@ -6916,11 +6917,11 @@ async function archiveRawSession(store, sessionId) {
6916
6917
  return await store.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
6917
6918
  }
6918
6919
  function detectForkPredecessor(sessionPath) {
6919
- const { readFileSync: readFileSync50, readdirSync: readdirSync34 } = requireFs();
6920
- const { dirname: dirname30, join: join62, basename: basename28 } = requirePath();
6920
+ const { readFileSync: readFileSync51, readdirSync: readdirSync34 } = requireFs();
6921
+ const { dirname: dirname30, join: join63, basename: basename28 } = requirePath();
6921
6922
  let text;
6922
6923
  try {
6923
- text = readFileSync50(sessionPath, "utf-8");
6924
+ text = readFileSync51(sessionPath, "utf-8");
6924
6925
  } catch {
6925
6926
  return null;
6926
6927
  }
@@ -6954,7 +6955,7 @@ function detectForkPredecessor(sessionPath) {
6954
6955
  if (!f.endsWith(".jsonl") || f === self || f === "sessions-index.json")
6955
6956
  continue;
6956
6957
  try {
6957
- if (readFileSync50(join62(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
6958
+ if (readFileSync51(join63(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
6958
6959
  return basename28(f, ".jsonl");
6959
6960
  }
6960
6961
  } catch {
@@ -6968,7 +6969,7 @@ function requireFs() {
6968
6969
  function requirePath() {
6969
6970
  return pathMod;
6970
6971
  }
6971
- var MARKER_QUOTE;
6972
+ var RAW_PARSE_MAX_BYTES, MARKER_QUOTE;
6972
6973
  var init_raw = __esm({
6973
6974
  "../engine/dist/transcript/raw.js"() {
6974
6975
  "use strict";
@@ -6976,6 +6977,7 @@ var init_raw = __esm({
6976
6977
  init_claude();
6977
6978
  init_codex();
6978
6979
  init_from_events();
6980
+ RAW_PARSE_MAX_BYTES = 40 * 1024 * 1024;
6979
6981
  MARKER_QUOTE = /\[REDACTED:[A-Za-z0-9_-]+\]"/g;
6980
6982
  }
6981
6983
  });
@@ -11129,18 +11131,20 @@ function updateShadow(sessionId, exp, prebuilt) {
11129
11131
  const curHash = containerSrcHash(current);
11130
11132
  if (!prior) {
11131
11133
  current.srcHash = curHash;
11134
+ current.mergeVersion = SHADOW_MERGE_VERSION;
11132
11135
  try {
11133
11136
  writeShadowContainer(tool, rawId, current);
11134
11137
  } catch {
11135
11138
  }
11136
11139
  return { status: "created", sessionId, tool, container: current, recovered: 0, path };
11137
11140
  }
11138
- if (prior.srcHash && prior.srcHash === curHash) {
11141
+ if (prior.srcHash && prior.srcHash === curHash && prior.mergeVersion === SHADOW_MERGE_VERSION) {
11139
11142
  return { status: "unchanged", sessionId, tool, container: prior, recovered: 0, path };
11140
11143
  }
11141
11144
  const merged = mergeContainer(prior, current);
11142
- if (merged.status !== "unchanged" || prior.srcHash !== curHash) {
11145
+ if (merged.status !== "unchanged" || prior.srcHash !== curHash || prior.mergeVersion !== SHADOW_MERGE_VERSION) {
11143
11146
  merged.container.srcHash = curHash;
11147
+ merged.container.mergeVersion = SHADOW_MERGE_VERSION;
11144
11148
  try {
11145
11149
  writeShadowContainer(tool, rawId, merged.container);
11146
11150
  } catch {
@@ -11233,7 +11237,7 @@ function shadowSizeOnDisk(tool, rawId) {
11233
11237
  return 0;
11234
11238
  }
11235
11239
  }
11236
- var WHOLE_FILE_TOOLS, STATE_META, lastLiveFingerprint;
11240
+ var WHOLE_FILE_TOOLS, STATE_META, SHADOW_MERGE_VERSION, lastLiveFingerprint;
11237
11241
  var init_shadow = __esm({
11238
11242
  "../engine/dist/transcript/shadow.js"() {
11239
11243
  "use strict";
@@ -11241,6 +11245,7 @@ var init_shadow = __esm({
11241
11245
  init_raw();
11242
11246
  WHOLE_FILE_TOOLS = /* @__PURE__ */ new Set(["cursor"]);
11243
11247
  STATE_META = /* @__PURE__ */ new Set(["mode", "permission-mode", "ai-title", "last-prompt", "pr-link", "summary"]);
11248
+ SHADOW_MERGE_VERSION = 2;
11244
11249
  lastLiveFingerprint = /* @__PURE__ */ new Map();
11245
11250
  }
11246
11251
  });
@@ -14357,6 +14362,7 @@ var init_opencode2 = __esm({
14357
14362
  // ../engine/dist/transcript/index.js
14358
14363
  var transcript_exports = {};
14359
14364
  __export(transcript_exports, {
14365
+ RAW_PARSE_MAX_BYTES: () => RAW_PARSE_MAX_BYTES,
14360
14366
  TRANSCRIPT_VERSION: () => TRANSCRIPT_VERSION,
14361
14367
  archiveRawSession: () => archiveRawSession,
14362
14368
  buildRawContainer: () => buildRawContainer,
@@ -18817,6 +18823,11 @@ function markSynced(server, rows) {
18817
18823
  base4.s = s;
18818
18824
  }
18819
18825
  if (r.hash !== void 0) base4.h = r.hash;
18826
+ const completesChunked = cursorFromCaller && r.chunkFinal === true && prev?.k === 1;
18827
+ if (cursorFromCaller && (r.full || completesChunked) && o > 0) base4.F = o;
18828
+ else if (prev?.F !== void 0) base4.F = prev.F;
18829
+ if (cursorFromCaller && r.chunkedHead) base4.k = 1;
18830
+ else if (prev?.k && !completesChunked && !(cursorFromCaller && r.full)) base4.k = 1;
18820
18831
  forServer[r.id] = base4;
18821
18832
  }
18822
18833
  persist(data);
@@ -18873,6 +18884,14 @@ function markFieldFullPassDone(server, field) {
18873
18884
  delete st.force[field.name];
18874
18885
  persistReconcile(data);
18875
18886
  }
18887
+ function forgetFullCoverage(server, sessionId) {
18888
+ const data = load();
18889
+ const row = data[server]?.[sessionId];
18890
+ if (!row || typeof row !== "object") return;
18891
+ delete row.F;
18892
+ delete row.h;
18893
+ persist(data);
18894
+ }
18876
18895
  function markFullResync(server, sessionId) {
18877
18896
  const data = load();
18878
18897
  const forServer = data[server];
@@ -18881,7 +18900,11 @@ function markFullResync(server, sessionId) {
18881
18900
  delete forServer[sessionId];
18882
18901
  persist(data);
18883
18902
  }
18884
- function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
18903
+ function fullCoveredOffset(row) {
18904
+ if (row.F !== void 0) return row.F;
18905
+ return row.h ? row.s ?? 0 : 0;
18906
+ }
18907
+ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly, nowMs = Date.now()) {
18885
18908
  if (!row) return "full";
18886
18909
  if (row.v < extractorVersion) return "full";
18887
18910
  const offset = row.o ?? 0;
@@ -18889,10 +18912,13 @@ function syncMode(row, mtime, fileSize, extractorVersion, isAppendOnly) {
18889
18912
  if (process.env.CHAT_RECALL_TAIL_APPEND !== "0" && isAppendOnly && fileSize > 0 && offset > 0 && fileSize > (row.s ?? 0)) {
18890
18913
  return "append";
18891
18914
  }
18915
+ if (isAppendOnly && offset > 0 && fileSize > 0 && fileSize === (row.s ?? 0) && !row.k && fullCoveredOffset(row) < offset && nowMs - Math.floor(mtime) >= SETTLE_QUIET_MS) {
18916
+ return "full";
18917
+ }
18892
18918
  if (row.m >= Math.floor(mtime)) return "skip";
18893
18919
  return "full";
18894
18920
  }
18895
- var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache;
18921
+ var ledgerPath, cache3, base, diskStamp, rowKey, LEDGER_FLUSH_MS, pendingWrite, flushTimer, itemVersionsPath, itemVersionsCache, inventoryPath, unackedCursorAttempts, reconcilePath, reconcileCache, SETTLE_QUIET_MS;
18896
18922
  var init_sync_ledger = __esm({
18897
18923
  "src/sync-ledger.ts"() {
18898
18924
  "use strict";
@@ -18912,6 +18938,7 @@ var init_sync_ledger = __esm({
18912
18938
  unackedCursorAttempts = 0;
18913
18939
  reconcilePath = () => join38(getDataDir(), "sync-field-reconcile.json");
18914
18940
  reconcileCache = null;
18941
+ SETTLE_QUIET_MS = 2 * 60 * 60 * 1e3;
18915
18942
  }
18916
18943
  });
18917
18944
 
@@ -19063,7 +19090,7 @@ var init_http = __esm({
19063
19090
  CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
19064
19091
  } catch {
19065
19092
  try {
19066
- if (true) CLI_VERSION = "0.7.16";
19093
+ if (true) CLI_VERSION = "0.7.18";
19067
19094
  } catch {
19068
19095
  }
19069
19096
  }
@@ -19561,7 +19588,7 @@ var init_telemetry = __esm({
19561
19588
  init_sync_client();
19562
19589
  cliVersion2 = () => {
19563
19590
  try {
19564
- return true ? "0.7.16" : "dev";
19591
+ return true ? "0.7.18" : "dev";
19565
19592
  } catch {
19566
19593
  return "dev";
19567
19594
  }
@@ -19724,6 +19751,150 @@ var init_collector_health = __esm({
19724
19751
  }
19725
19752
  });
19726
19753
 
19754
+ // src/raw-archive-parts.ts
19755
+ import { createReadStream as createReadStream4, createWriteStream, openSync as openSync9, readSync as readSync8, closeSync as closeSync9, rmSync as rmSync5, statSync as statSync37 } from "node:fs";
19756
+ import { tmpdir as tmpdir2 } from "node:os";
19757
+ import { join as join41 } from "node:path";
19758
+ import { randomBytes } from "node:crypto";
19759
+ import { createGzip } from "node:zlib";
19760
+ import { once } from "node:events";
19761
+ import { readFileSync as readFileSync33 } from "node:fs";
19762
+ async function* fileLines(path) {
19763
+ let rest = "";
19764
+ const decoder = new TextDecoder("utf-8");
19765
+ for await (const chunk of createReadStream4(path, { highWaterMark: 1024 * 1024 })) {
19766
+ rest += decoder.decode(chunk, { stream: true });
19767
+ let nl = rest.indexOf("\n");
19768
+ while (nl >= 0) {
19769
+ yield rest.slice(0, nl + 1);
19770
+ rest = rest.slice(nl + 1);
19771
+ nl = rest.indexOf("\n");
19772
+ }
19773
+ }
19774
+ rest += decoder.decode();
19775
+ if (rest) yield rest;
19776
+ }
19777
+ function* textLines(text) {
19778
+ let at = 0;
19779
+ while (at < text.length) {
19780
+ const nl = text.indexOf("\n", at);
19781
+ const end = nl < 0 ? text.length : nl + 1;
19782
+ yield text.slice(at, end);
19783
+ at = end;
19784
+ }
19785
+ }
19786
+ async function writeArchiveFile(source, meta, redact, dir = tmpdir2()) {
19787
+ const path = join41(dir, `cr-archive-${randomBytes(8).toString("hex")}.json.gz`);
19788
+ const gzip = createGzip({ level: 6 });
19789
+ const out = createWriteStream(path, { mode: 384 });
19790
+ const done = once(out, "finish");
19791
+ gzip.pipe(out);
19792
+ let size = 0;
19793
+ const write = async (s) => {
19794
+ size += Buffer.byteLength(s);
19795
+ if (!gzip.write(s)) await once(gzip, "drain");
19796
+ };
19797
+ try {
19798
+ await write(`{"v":1,"tool":${JSON.stringify(meta.tool)},"mtime":${Math.floor(meta.mtime)},"files":[`);
19799
+ for (const [i, f] of source.files.entries()) {
19800
+ await write(`${i ? "," : ""}{"name":${JSON.stringify(f.name)},"text":"`);
19801
+ const whole = () => source.kind === "memory" ? f.text : readFileSync33(f.path, "utf-8");
19802
+ if (!f.name.endsWith(".jsonl")) {
19803
+ await write(escapeJson(redact(whole())));
19804
+ } else {
19805
+ const lines = source.kind === "memory" ? textLines(f.text) : fileLines(f.path);
19806
+ for await (const line of lines) {
19807
+ const nl = line.endsWith("\n");
19808
+ const body2 = nl ? line.slice(0, -1) : line;
19809
+ await write(escapeJson((body2 ? redactJsonLine(body2, redact) : body2) + (nl ? "\n" : "")));
19810
+ }
19811
+ }
19812
+ await write('"}');
19813
+ }
19814
+ await write("]}");
19815
+ gzip.end();
19816
+ await done;
19817
+ } catch (err) {
19818
+ gzip.destroy();
19819
+ out.destroy();
19820
+ rmSync5(path, { force: true });
19821
+ throw err;
19822
+ }
19823
+ return { path, gzSize: statSync37(path).size, size };
19824
+ }
19825
+ async function putPart(url, body2) {
19826
+ let last;
19827
+ for (let attempt = 1; attempt <= PART_ATTEMPTS; attempt++) {
19828
+ const ac = new AbortController();
19829
+ const timer = setTimeout(() => ac.abort(), PART_TIMEOUT_MS);
19830
+ try {
19831
+ const r = await fetch(url, { method: "PUT", body: body2, signal: ac.signal });
19832
+ const etag = r.headers.get("etag");
19833
+ if (r.ok && etag) return etag;
19834
+ last = new Error(`part PUT answered ${r.status}`);
19835
+ if (r.status === 403 || r.status === 404) break;
19836
+ } catch (err) {
19837
+ last = err;
19838
+ } finally {
19839
+ clearTimeout(timer);
19840
+ }
19841
+ }
19842
+ throw last instanceof Error ? last : new Error(String(last));
19843
+ }
19844
+ async function uploadArchiveFile(base4, token, args) {
19845
+ const headers = { "content-type": "application/json" };
19846
+ if (token) headers.authorization = `Bearer ${token}`;
19847
+ const post = async (path, body2) => {
19848
+ const r = await fetch(`${base4}/api/sync/raw-upload/${path}`, { method: "POST", headers, body: JSON.stringify(body2) });
19849
+ const json = await r.json().catch(() => ({}));
19850
+ return { status: r.status, json };
19851
+ };
19852
+ const start = await post("start", {
19853
+ session_id: args.sessionId,
19854
+ tool: args.tool,
19855
+ mtime: Math.floor(args.mtime),
19856
+ size: args.file.size,
19857
+ gz_size: args.file.gzSize,
19858
+ project_id: args.projectId,
19859
+ project_path: args.projectPath
19860
+ });
19861
+ if (start.status === 501 || start.status === 404) return "unsupported";
19862
+ if (start.status !== 200) throw new Error(`raw upload start answered ${start.status}: ${start.json.error ?? ""}`);
19863
+ if (start.json.result === "unchanged" || start.json.result === "shrink-protected") return start.json.result;
19864
+ const urls = start.json.urls;
19865
+ const partBytes = Number(start.json.part_bytes);
19866
+ const parts = [];
19867
+ const fd = openSync9(args.file.path, "r");
19868
+ try {
19869
+ for (const [i, url] of urls.entries()) {
19870
+ const len = Math.min(partBytes, args.file.gzSize - i * partBytes);
19871
+ const buf = Buffer.allocUnsafe(len);
19872
+ let got = 0;
19873
+ while (got < len) {
19874
+ const n = readSync8(fd, buf, got, len - got, i * partBytes + got);
19875
+ if (n <= 0) break;
19876
+ got += n;
19877
+ }
19878
+ parts.push({ part_number: i + 1, etag: await putPart(url, buf.subarray(0, got)) });
19879
+ }
19880
+ } finally {
19881
+ closeSync9(fd);
19882
+ }
19883
+ const done = await post("complete", { session_id: args.sessionId, upload_id: start.json.upload_id, parts });
19884
+ if (done.status !== 200) throw new Error(`raw upload complete answered ${done.status}: ${done.json.error ?? ""}`);
19885
+ return done.json.result;
19886
+ }
19887
+ var escapeJson, PART_TIMEOUT_MS, PART_ATTEMPTS;
19888
+ var init_raw_archive_parts = __esm({
19889
+ "src/raw-archive-parts.ts"() {
19890
+ "use strict";
19891
+ init_transcript();
19892
+ escapeJson = (s) => JSON.stringify(s).slice(1, -1);
19893
+ PART_TIMEOUT_MS = 12e4;
19894
+ PART_ATTEMPTS = 3;
19895
+ }
19896
+ });
19897
+
19727
19898
  // src/server-probe.ts
19728
19899
  var server_probe_exports = {};
19729
19900
  __export(server_probe_exports, {
@@ -19867,7 +20038,7 @@ __export(client_events_exports, {
19867
20038
  isTelemetryEnabled: () => isTelemetryEnabled,
19868
20039
  reportClientEvent: () => reportClientEvent
19869
20040
  });
19870
- import { readFileSync as readFileSync33 } from "node:fs";
20041
+ import { readFileSync as readFileSync34 } from "node:fs";
19871
20042
  function scrub(msg) {
19872
20043
  if (!msg) return "";
19873
20044
  const noHome = msg.replace(/\/(?:home|Users)\/[^/\s]+/g, "~");
@@ -19920,7 +20091,7 @@ var init_client_events = __esm({
19920
20091
  init_secret_redactor();
19921
20092
  CLI_VERSION2 = "0.0.0";
19922
20093
  try {
19923
- CLI_VERSION2 = JSON.parse(readFileSync33(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
20094
+ CLI_VERSION2 = JSON.parse(readFileSync34(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION2;
19924
20095
  } catch {
19925
20096
  }
19926
20097
  }
@@ -19950,11 +20121,11 @@ __export(auto_update_exports, {
19950
20121
  tarballUrl: () => tarballUrl
19951
20122
  });
19952
20123
  import { execSync as execSync2, execFileSync as execFileSync3 } from "node:child_process";
19953
- import { readFileSync as readFileSync34, existsSync as existsSync41 } from "node:fs";
20124
+ import { readFileSync as readFileSync35, existsSync as existsSync41 } from "node:fs";
19954
20125
  import { fileURLToPath as fileURLToPath2 } from "node:url";
19955
- import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync30, statSync as statSync37, mkdirSync as mkdirSync9 } from "node:fs";
19956
- import { join as join41, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
19957
- import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
20126
+ import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync6, readdirSync as readdirSync30, statSync as statSync38, mkdirSync as mkdirSync9 } from "node:fs";
20127
+ import { join as join42, dirname as dirname18, sep as sep2, posix as pathPosix, win32 as pathWin32 } from "node:path";
20128
+ import { tmpdir as tmpdir3, homedir as homedir15 } from "node:os";
19958
20129
  import { createHash as createHash8 } from "node:crypto";
19959
20130
  function compareVersions(a, b) {
19960
20131
  const pa = String(a).split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
@@ -19978,7 +20149,7 @@ function newerOf(a, b) {
19978
20149
  function installedVersion() {
19979
20150
  try {
19980
20151
  const here = dirname18(fileURLToPath2(import.meta.url));
19981
- const raw = readFileSync34(join41(here, "..", "package.json"), "utf-8");
20152
+ const raw = readFileSync35(join42(here, "..", "package.json"), "utf-8");
19982
20153
  return JSON.parse(raw).version || null;
19983
20154
  } catch {
19984
20155
  return null;
@@ -19991,7 +20162,7 @@ function planAutoUpdate(base4, caps, ownVersion3, flag) {
19991
20162
  if (compareVersions(rel.version, ownVersion3) <= 0) return { update: false, reason: `already current (own ${ownVersion3} >= server ${rel.version})` };
19992
20163
  return { update: true, reason: `server ${rel.version} > own ${ownVersion3}`, url: tarballUrl(base4, rel.version), fallbackUrl: tarballUrl(base4), sha256: rel.sha256, from: ownVersion3, to: rel.version };
19993
20164
  }
19994
- function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
20165
+ function sweepStaleStaging(root = tmpdir3(), now = Date.now()) {
19995
20166
  const MKDTEMP_SUFFIX = /^cr-update-.{6}$/s;
19996
20167
  const COLD_MS = 60 * 60 * 1e3;
19997
20168
  let removed = 0;
@@ -20003,13 +20174,13 @@ function sweepStaleStaging(root = tmpdir2(), now = Date.now()) {
20003
20174
  }
20004
20175
  for (const name of entries) {
20005
20176
  if (!MKDTEMP_SUFFIX.test(name)) continue;
20006
- const dir = join41(root, name);
20177
+ const dir = join42(root, name);
20007
20178
  try {
20008
- const st = statSync37(dir);
20179
+ const st = statSync38(dir);
20009
20180
  if (!st.isDirectory() || now - st.mtimeMs < COLD_MS) continue;
20010
20181
  const inside = readdirSync30(dir);
20011
20182
  if (inside.length > 1 || inside.length === 1 && inside[0] !== "chat-recall.tgz") continue;
20012
- rmSync5(dir, { recursive: true, force: true });
20183
+ rmSync6(dir, { recursive: true, force: true });
20013
20184
  removed++;
20014
20185
  } catch {
20015
20186
  }
@@ -20032,14 +20203,14 @@ async function executeAutoUpdate(plan, deps) {
20032
20203
  }
20033
20204
  const got = createHash8("sha256").update(buf).digest("hex");
20034
20205
  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` };
20035
- const dir = mkdtempSync3(join41(tmpdir2(), "cr-update-"));
20206
+ const dir = mkdtempSync3(join42(tmpdir3(), "cr-update-"));
20036
20207
  const discardStaging = () => {
20037
20208
  try {
20038
- rmSync5(dir, { recursive: true, force: true });
20209
+ rmSync6(dir, { recursive: true, force: true });
20039
20210
  } catch {
20040
20211
  }
20041
20212
  };
20042
- const tgz = join41(dir, "chat-recall.tgz");
20213
+ const tgz = join42(dir, "chat-recall.tgz");
20043
20214
  writeFileSync8(tgz, buf);
20044
20215
  if (deps.stopping?.()) {
20045
20216
  discardStaging();
@@ -20119,7 +20290,7 @@ function realInstall(tgz) {
20119
20290
  try {
20120
20291
  npmInstall(npm, ["install", "-g", tgz]);
20121
20292
  } catch {
20122
- npmInstall(npm, ["install", "-g", "--prefix", join41(homedir15(), ".local"), tgz]);
20293
+ npmInstall(npm, ["install", "-g", "--prefix", join42(homedir15(), ".local"), tgz]);
20123
20294
  }
20124
20295
  }
20125
20296
  function acquireUpdateLock() {
@@ -20136,12 +20307,12 @@ function realRestart(platform2) {
20136
20307
  if (c) execSync2(c.cmd, { stdio: "ignore", ...c.shell ? { shell: c.shell } : {} });
20137
20308
  }
20138
20309
  function updateStatePath() {
20139
- const dir = process.env.CHAT_RECALL_DATA_DIR || join41(homedir15(), ".chat-recall");
20140
- return join41(dir, "auto-update-state.json");
20310
+ const dir = process.env.CHAT_RECALL_DATA_DIR || join42(homedir15(), ".chat-recall");
20311
+ return join42(dir, "auto-update-state.json");
20141
20312
  }
20142
20313
  function readUpdateState() {
20143
20314
  try {
20144
- return JSON.parse(readFileSync34(updateStatePath(), "utf8"));
20315
+ return JSON.parse(readFileSync35(updateStatePath(), "utf8"));
20145
20316
  } catch {
20146
20317
  return {};
20147
20318
  }
@@ -20282,9 +20453,9 @@ __export(sync_client_exports, {
20282
20453
  syncIncremental: () => syncIncremental,
20283
20454
  syncSessions: () => syncSessions
20284
20455
  });
20285
- import { join as join42, dirname as dirname19, basename as basename20 } from "node:path";
20286
- import { readFileSync as readFileSync35, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync31, statSync as statSync38 } from "node:fs";
20287
- import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
20456
+ import { join as join43, dirname as dirname19, basename as basename20 } from "node:path";
20457
+ import { readFileSync as readFileSync36, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync7, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync31, statSync as statSync39 } from "node:fs";
20458
+ import { tmpdir as tmpdir4, hostname as hostname2 } from "node:os";
20288
20459
  import { createHash as createHash9 } from "node:crypto";
20289
20460
  import { fileURLToPath as fileURLToPath3 } from "node:url";
20290
20461
  import { gzipSync as gzipSync3 } from "zlib";
@@ -20338,7 +20509,7 @@ function loadAllCredentials() {
20338
20509
  const fromEnv = envCredentials();
20339
20510
  if (fromEnv) return [{ serverUrl: fromEnv.serverUrl, token: fromEnv.token }];
20340
20511
  try {
20341
- const parsed = JSON.parse(readFileSync35(credPath(), "utf-8"));
20512
+ const parsed = JSON.parse(readFileSync36(credPath(), "utf-8"));
20342
20513
  if (Array.isArray(parsed?.targets)) return parsed.targets.filter((t) => t?.serverUrl).map((t) => ({ serverUrl: t.serverUrl, token: t.token || "" }));
20343
20514
  if (parsed?.serverUrl) return [{ serverUrl: parsed.serverUrl, token: parsed.token || "" }];
20344
20515
  } catch {
@@ -20400,11 +20571,11 @@ function _resetTenantSecurityConfigCache() {
20400
20571
  _securityConfigCache.clear();
20401
20572
  }
20402
20573
  function syncConfigCachePath() {
20403
- return join42(getDataDir(), "sync-config-cache.json");
20574
+ return join43(getDataDir(), "sync-config-cache.json");
20404
20575
  }
20405
20576
  function readPersistedSyncConfigs() {
20406
20577
  try {
20407
- return JSON.parse(readFileSync35(syncConfigCachePath(), "utf-8"));
20578
+ return JSON.parse(readFileSync36(syncConfigCachePath(), "utf-8"));
20408
20579
  } catch {
20409
20580
  return {};
20410
20581
  }
@@ -20682,7 +20853,7 @@ async function reconcileFields(opts = {}) {
20682
20853
  }
20683
20854
  return { sessions: fullRefs().length, scanned, pushed, absent, perTarget };
20684
20855
  }
20685
- function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now()) {
20856
+ function reapStaleScanDirs(dir = tmpdir4(), maxAgeMs = 6 * 36e5, now = Date.now()) {
20686
20857
  let reaped = 0;
20687
20858
  let names;
20688
20859
  try {
@@ -20692,10 +20863,10 @@ function reapStaleScanDirs(dir = tmpdir3(), maxAgeMs = 6 * 36e5, now = Date.now(
20692
20863
  }
20693
20864
  for (const name of names) {
20694
20865
  if (!name.startsWith("cr-batchscan-")) continue;
20695
- const p = join42(dir, name);
20866
+ const p = join43(dir, name);
20696
20867
  try {
20697
- if (now - statSync38(p).mtimeMs < maxAgeMs) continue;
20698
- rmSync6(p, { recursive: true, force: true });
20868
+ if (now - statSync39(p).mtimeMs < maxAgeMs) continue;
20869
+ rmSync7(p, { recursive: true, force: true });
20699
20870
  reaped++;
20700
20871
  } catch {
20701
20872
  }
@@ -20709,7 +20880,7 @@ function batchScanExternal(items, opts = {}) {
20709
20880
  let scanned = 0, slices = 0, scanMs = 0;
20710
20881
  if (items.length === 0) return { findings, scanned, slices, scanMs };
20711
20882
  reapStaleScanDirs();
20712
- let scanDir = mkdtempSync4(join42(tmpdir3(), "cr-batchscan-"));
20883
+ let scanDir = mkdtempSync4(join43(tmpdir4(), "cr-batchscan-"));
20713
20884
  let safeToId = /* @__PURE__ */ new Map();
20714
20885
  let bytesInDir = 0;
20715
20886
  const flushSlice = () => {
@@ -20727,7 +20898,7 @@ function batchScanExternal(items, opts = {}) {
20727
20898
  slices++;
20728
20899
  }
20729
20900
  try {
20730
- rmSync6(scanDir, { recursive: true, force: true });
20901
+ rmSync7(scanDir, { recursive: true, force: true });
20731
20902
  } catch {
20732
20903
  }
20733
20904
  safeToId = /* @__PURE__ */ new Map();
@@ -20740,19 +20911,19 @@ function batchScanExternal(items, opts = {}) {
20740
20911
  if (raw === null) continue;
20741
20912
  const safe = item.id.replace(/[^A-Za-z0-9_-]/g, "");
20742
20913
  safeToId.set(safe, item.id);
20743
- writeFileSync9(join42(scanDir, `${safe}.txt`), raw);
20914
+ writeFileSync9(join43(scanDir, `${safe}.txt`), raw);
20744
20915
  bytesInDir += Buffer.byteLength(raw);
20745
20916
  } catch {
20746
20917
  }
20747
20918
  if (bytesInDir >= maxBytes) {
20748
20919
  flushSlice();
20749
- scanDir = mkdtempSync4(join42(tmpdir3(), "cr-batchscan-"));
20920
+ scanDir = mkdtempSync4(join43(tmpdir4(), "cr-batchscan-"));
20750
20921
  }
20751
20922
  }
20752
20923
  flushSlice();
20753
20924
  } finally {
20754
20925
  try {
20755
- rmSync6(scanDir, { recursive: true, force: true });
20926
+ rmSync7(scanDir, { recursive: true, force: true });
20756
20927
  } catch {
20757
20928
  }
20758
20929
  }
@@ -20996,12 +21167,13 @@ async function syncToTargetInScope(cred, opts = {}) {
20996
21167
  markSynced(base4, rows.map((r) => {
20997
21168
  const off = typeof r.from_offset === "number" ? r.from_offset : 0;
20998
21169
  const hash = shippedHash.get(r.session_id);
20999
- return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true } : { id: r.session_id, mtime: r.mtime, hash };
21170
+ return off > 0 ? { id: r.session_id, mtime: r.mtime, offset: off, size: off, hash, acked: true, full: true } : { id: r.session_id, mtime: r.mtime, hash };
21000
21171
  }));
21001
21172
  } catch {
21002
21173
  }
21003
21174
  });
21004
21175
  const appendResults = { uploaded: 0, fullResyncNeeded: /* @__PURE__ */ new Set() };
21176
+ const pendingArchives = [];
21005
21177
  const postAppend = async (convs) => {
21006
21178
  const headers = { "content-type": "application/json" };
21007
21179
  if (cred.token) headers.authorization = `Bearer ${cred.token}`;
@@ -21036,8 +21208,10 @@ async function syncToTargetInScope(cred, opts = {}) {
21036
21208
  offset: c.from_offset,
21037
21209
  size: c.from_offset,
21038
21210
  // size = new offset (file grew to here)
21039
- acked: true
21211
+ acked: true,
21040
21212
  // server merged this tail
21213
+ chunkFinal: c.final === true
21214
+ // completes a chunked full sync
21041
21215
  })));
21042
21216
  } catch {
21043
21217
  }
@@ -21069,9 +21243,9 @@ async function syncToTargetInScope(cred, opts = {}) {
21069
21243
  const mtime = conv.mtime;
21070
21244
  try {
21071
21245
  if (staged || !guarded) {
21072
- markSynced(base4, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true }]);
21246
+ markSynced(base4, [{ id, mtime, offset: conv.from_offset, size: conv.from_offset, acked: true, chunkedHead: true }]);
21073
21247
  } else if (typeof guarded.o === "number" && guarded.o > 0 && guarded.o <= fileSize) {
21074
- markSynced(base4, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true }]);
21248
+ markSynced(base4, [{ id, mtime, offset: guarded.o, size: guarded.o, acked: true, chunkedHead: true }]);
21075
21249
  } else {
21076
21250
  markSynced(base4, [{ id, mtime }]);
21077
21251
  }
@@ -21280,6 +21454,14 @@ async function syncToTargetInScope(cred, opts = {}) {
21280
21454
  if (built.srcHash) shippedHash.set(ref.prefixedId, built.srcHash);
21281
21455
  if (built.conv.chunked) await postChunkedHead(built.conv, sessionFileBytes(ref));
21282
21456
  else await convBatch.add(built.conv);
21457
+ if (built.archive) {
21458
+ pendingArchives.push({
21459
+ id: ref.prefixedId,
21460
+ ...built.archive,
21461
+ projectId: built.conv.project_id,
21462
+ projectPath: built.conv.project_path
21463
+ });
21464
+ }
21283
21465
  if (built.findings.length > 0) await findingsBatch.add(built.findings);
21284
21466
  if (upload.sessionMeta) {
21285
21467
  await accumulateKg(extractEntities(built.kgText, { projectPath: built.projectPath, sourceType: "session", sessionId: ref.prefixedId, validFrom: ref.mtime ? new Date(ref.mtime).toISOString().slice(0, 10) : void 0 }), ref.prefixedId);
@@ -21432,6 +21614,30 @@ async function syncToTargetInScope(cred, opts = {}) {
21432
21614
  await kgTripleBatch.drain();
21433
21615
  await findingsBatch.drain();
21434
21616
  await drainInflight();
21617
+ let archivesUnsupported = false;
21618
+ for (const a of pendingArchives) {
21619
+ try {
21620
+ if (!archivesUnsupported) {
21621
+ const r = await uploadArchiveFile(base4, cred.token, {
21622
+ sessionId: a.id,
21623
+ tool: a.tool,
21624
+ mtime: a.mtime,
21625
+ projectId: a.projectId,
21626
+ projectPath: a.projectPath,
21627
+ file: a.file
21628
+ });
21629
+ if (r === "unsupported") archivesUnsupported = true;
21630
+ }
21631
+ } catch (err) {
21632
+ console.error(`[sync] raw archive for ${a.id} not uploaded (tried again when the session is next quiet): ${err instanceof Error ? err.message : err}`);
21633
+ try {
21634
+ forgetFullCoverage(base4, a.id);
21635
+ } catch {
21636
+ }
21637
+ } finally {
21638
+ rmSync7(a.file.path, { force: true });
21639
+ }
21640
+ }
21435
21641
  {
21436
21642
  const sentHashes = loadInventoryHashes(base4);
21437
21643
  const entries = [];
@@ -21537,6 +21743,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21537
21743
  let includeRaw = opts.includeRaw !== false;
21538
21744
  let includeMeta = opts.includeMeta !== false;
21539
21745
  let srcHash;
21746
+ const wantRaw = opts.includeRaw !== false;
21540
21747
  const oversized = sessionBuildBytes(ref) > FULL_BUILD_MAX_BYTES;
21541
21748
  let headOffsetEnd = 0;
21542
21749
  let oversizedFindings = null;
@@ -21705,7 +21912,7 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21705
21912
  } else if (opts.scanSecrets) {
21706
21913
  let tmp = null;
21707
21914
  try {
21708
- tmp = join42(tmpdir3(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
21915
+ tmp = join43(tmpdir4(), `cr-scan-${ref.prefixedId.replace(/[^A-Za-z0-9_-]/g, "")}-${mtime}.txt`);
21709
21916
  writeFileSync9(tmp, rawText);
21710
21917
  for (const f of scanFileForSecrets(tmp, { verifyOnly: opts.verifySecrets, tenantRules: opts.tenantRules })) {
21711
21918
  const verifiedAt = f.verified ? (/* @__PURE__ */ new Date()).toISOString() : null;
@@ -21757,6 +21964,25 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21757
21964
  }
21758
21965
  const envTexts = envelope.messages.filter((m) => m.content?.trim());
21759
21966
  const redactedText = envTexts.map((m) => m.content).join("\n");
21967
+ let archive;
21968
+ if (wantRaw && !raw_b64) {
21969
+ try {
21970
+ const redact = (t) => redactSecrets(t, { force: true, count });
21971
+ let source = null;
21972
+ let archiveMtime = Math.floor(ref.mtime) || 0;
21973
+ let archiveTool = ref.toolId;
21974
+ if (container) {
21975
+ source = { kind: "memory", files: container.files };
21976
+ archiveMtime = container.mtime;
21977
+ archiveTool = container.tool;
21978
+ } else if (oversized) {
21979
+ source = diskArchiveSource(ref);
21980
+ }
21981
+ if (source) archive = { file: await writeArchiveFile(source, { tool: archiveTool, mtime: archiveMtime }, redact), tool: archiveTool, mtime: archiveMtime };
21982
+ } catch (err) {
21983
+ console.error(`[sync] raw archive for ${ref.prefixedId} not written: ${err instanceof Error ? err.message : err}`);
21984
+ }
21985
+ }
21760
21986
  return {
21761
21987
  conv: {
21762
21988
  session_id: ref.prefixedId,
@@ -21788,7 +22014,17 @@ async function buildConversationSync(ref, mtime, opts = {}) {
21788
22014
  redactions: count.redactions,
21789
22015
  findings,
21790
22016
  scanMs,
21791
- srcHash
22017
+ srcHash,
22018
+ archive
22019
+ };
22020
+ }
22021
+ function diskArchiveSource(ref) {
22022
+ if (ref.toolId !== "claude") return null;
22023
+ const groups = resolveSessionContentGroups(ref.rawId);
22024
+ if (groups.length === 0 || groups.some((g) => g.paths.length !== 1)) return null;
22025
+ return {
22026
+ kind: "disk",
22027
+ files: groups.map((g) => ({ name: g.name === "main" ? `${ref.rawId}.jsonl` : g.name, path: g.paths[0] }))
21792
22028
  };
21793
22029
  }
21794
22030
  function parseTailMessages(tool, text) {
@@ -21944,14 +22180,15 @@ var init_sync_client = __esm({
21944
22180
  init_telemetry();
21945
22181
  init_collector_health();
21946
22182
  init_live_session_scan();
22183
+ init_raw_archive_parts();
21947
22184
  init_backends();
21948
22185
  ownVersion = "0.0.0";
21949
22186
  try {
21950
22187
  const dir = dirname19(fileURLToPath3(import.meta.url));
21951
- ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
22188
+ ownVersion = JSON.parse(readFileSync36(join43(dir, "../package.json"), "utf-8")).version || ownVersion;
21952
22189
  } catch {
21953
22190
  try {
21954
- if (true) ownVersion = "0.7.16";
22191
+ if (true) ownVersion = "0.7.18";
21955
22192
  } catch {
21956
22193
  }
21957
22194
  }
@@ -21963,7 +22200,7 @@ var init_sync_client = __esm({
21963
22200
  ]);
21964
22201
  walkProgressForUpload = null;
21965
22202
  lastServerPackSpec = null;
21966
- credPath = () => join42(getDataDir(), "credentials.json");
22203
+ credPath = () => join43(getDataDir(), "credentials.json");
21967
22204
  _securityConfigCache = /* @__PURE__ */ new Map();
21968
22205
  SECURITY_CONFIG_TTL_MS = 5 * 60 * 1e3;
21969
22206
  _syncConfigCache = /* @__PURE__ */ new Map();
@@ -21994,15 +22231,15 @@ __export(update_notice_exports, {
21994
22231
  refreshUpdateCheck: () => refreshUpdateCheck,
21995
22232
  updateNotice: () => updateNotice
21996
22233
  });
21997
- import { readFileSync as readFileSync36, writeFileSync as writeFileSync10 } from "node:fs";
21998
- import { join as join43 } from "node:path";
22234
+ import { readFileSync as readFileSync37, writeFileSync as writeFileSync10 } from "node:fs";
22235
+ import { join as join44 } from "node:path";
21999
22236
  function newestKnown(c) {
22000
22237
  const npm = c.npmVersion ?? "0.0.0";
22001
22238
  return compareVersions(npm, c.serverVersion) > 0 ? { version: npm, source: "npm" } : { version: c.serverVersion, source: "server" };
22002
22239
  }
22003
22240
  function readCheck() {
22004
22241
  try {
22005
- const c = JSON.parse(readFileSync36(checkPath(), "utf-8"));
22242
+ const c = JSON.parse(readFileSync37(checkPath(), "utf-8"));
22006
22243
  return c && typeof c.serverVersion === "string" ? c : null;
22007
22244
  } catch {
22008
22245
  return null;
@@ -22096,17 +22333,17 @@ var init_update_notice = __esm({
22096
22333
  init_http();
22097
22334
  CHECK_TTL_MS = 6 * 3600 * 1e3;
22098
22335
  NPM_LATEST_URL = "https://registry.npmjs.org/chat-recall";
22099
- checkPath = () => join43(getDataDir(), "update-check.json");
22336
+ checkPath = () => join44(getDataDir(), "update-check.json");
22100
22337
  printed = false;
22101
22338
  }
22102
22339
  });
22103
22340
 
22104
22341
  // ../engine/dist/core/which.js
22105
- import { existsSync as existsSync42, statSync as statSync39, accessSync, constants } from "node:fs";
22106
- import { join as join44, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
22342
+ import { existsSync as existsSync42, statSync as statSync40, accessSync, constants } from "node:fs";
22343
+ import { join as join45, isAbsolute as isAbsolute2, delimiter as delimiter2, sep as sep3 } from "node:path";
22107
22344
  function executable(p) {
22108
22345
  try {
22109
- if (!statSync39(p).isFile())
22346
+ if (!statSync40(p).isFile())
22110
22347
  return false;
22111
22348
  } catch {
22112
22349
  return false;
@@ -22142,7 +22379,7 @@ function resolveOnPath(name) {
22142
22379
  if (!dir)
22143
22380
  continue;
22144
22381
  for (const c of candidates(name)) {
22145
- const full = join44(dir, c);
22382
+ const full = join45(dir, c);
22146
22383
  if (existsSync42(full) && executable(full))
22147
22384
  return full;
22148
22385
  }
@@ -22206,9 +22443,9 @@ __export(mcp_clients_exports, {
22206
22443
  inspectMcpClients: () => inspectMcpClients,
22207
22444
  registerMcpEverywhere: () => registerMcpEverywhere
22208
22445
  });
22209
- import { existsSync as existsSync44, mkdirSync as mkdirSync12, readFileSync as readFileSync37, writeFileSync as writeFileSync12 } from "fs";
22446
+ import { existsSync as existsSync44, mkdirSync as mkdirSync12, readFileSync as readFileSync38, writeFileSync as writeFileSync12 } from "fs";
22210
22447
  import { homedir as homedir16 } from "os";
22211
- import { dirname as dirname21, join as join46 } from "path";
22448
+ import { dirname as dirname21, join as join47 } from "path";
22212
22449
  function envHome(varName, fallback) {
22213
22450
  const v = process.env[varName];
22214
22451
  return v && v.trim() ? v.trim() : fallback;
@@ -22218,7 +22455,7 @@ function writeFileMkdir(path, body2) {
22218
22455
  writeFileSync12(path, body2);
22219
22456
  }
22220
22457
  function readJsonObject(path) {
22221
- const raw = readFileSync37(path, "utf-8");
22458
+ const raw = readFileSync38(path, "utf-8");
22222
22459
  if (raw.trim() === "")
22223
22460
  return {};
22224
22461
  return JSON.parse(raw);
@@ -22293,7 +22530,7 @@ function registerCodexToml(path, spec) {
22293
22530
  const id = "codex";
22294
22531
  const label = "Codex";
22295
22532
  const block = codexBlock(spec);
22296
- const prev = existsSync44(path) ? readFileSync37(path, "utf-8") : "";
22533
+ const prev = existsSync44(path) ? readFileSync38(path, "utf-8") : "";
22297
22534
  const lines = prev.length ? prev.split("\n") : [];
22298
22535
  const start = lines.findIndex((l) => l.trim() === CODEX_HEADER);
22299
22536
  if (start === -1) {
@@ -22336,7 +22573,7 @@ function inspectMcpClients(opts = {}) {
22336
22573
  let registered = false;
22337
22574
  if (existsSync44(path)) {
22338
22575
  try {
22339
- const raw = readFileSync37(path, "utf-8");
22576
+ const raw = readFileSync38(path, "utf-8");
22340
22577
  registered = c.id === "codex" ? raw.includes(CODEX_HEADER) : !!(JSON.parse(raw).mcpServers?.[MCP_SERVER_NAME] ?? JSON.parse(raw).mcp?.[MCP_SERVER_NAME]);
22341
22578
  } catch {
22342
22579
  registered = false;
@@ -22356,7 +22593,7 @@ var init_mcp_clients = __esm({
22356
22593
  id: "claude",
22357
22594
  label: "Claude Code",
22358
22595
  bin: "claude",
22359
- configPath: (home) => join46(home, ".mcp.json"),
22596
+ configPath: (home) => join47(home, ".mcp.json"),
22360
22597
  // Claude Code is the reference client and `init`'s historical target, so we
22361
22598
  // always write it — a fresh machine has no ~/.claude yet on first install.
22362
22599
  isPresent: () => true,
@@ -22368,7 +22605,7 @@ var init_mcp_clients = __esm({
22368
22605
  id: "codex",
22369
22606
  label: "Codex",
22370
22607
  bin: "codex",
22371
- configPath: (home) => join46(envHome("CHAT_RECALL_CODEX_HOME", join46(home, ".codex")), "config.toml"),
22608
+ configPath: (home) => join47(envHome("CHAT_RECALL_CODEX_HOME", join47(home, ".codex")), "config.toml"),
22372
22609
  isPresent(home) {
22373
22610
  const p = this.configPath(home);
22374
22611
  return existsSync44(p) || existsSync44(dirname21(p));
@@ -22381,7 +22618,7 @@ var init_mcp_clients = __esm({
22381
22618
  id: "opencode",
22382
22619
  label: "OpenCode",
22383
22620
  bin: "opencode",
22384
- configPath: (home) => join46(envHome("XDG_CONFIG_HOME", join46(home, ".config")), "opencode", "opencode.json"),
22621
+ configPath: (home) => join47(envHome("XDG_CONFIG_HOME", join47(home, ".config")), "opencode", "opencode.json"),
22385
22622
  isPresent(home) {
22386
22623
  const p = this.configPath(home);
22387
22624
  return existsSync44(p) || existsSync44(dirname21(p));
@@ -22396,9 +22633,9 @@ var init_mcp_clients = __esm({
22396
22633
  id: "agy",
22397
22634
  label: "Antigravity",
22398
22635
  bin: "agy",
22399
- configPath: (home) => join46(home, ".gemini", "config", "mcp_config.json"),
22636
+ configPath: (home) => join47(home, ".gemini", "config", "mcp_config.json"),
22400
22637
  isPresent(home) {
22401
- return existsSync44(join46(home, ".gemini", "antigravity-cli")) || existsSync44(this.configPath(home));
22638
+ return existsSync44(join47(home, ".gemini", "antigravity-cli")) || existsSync44(this.configPath(home));
22402
22639
  },
22403
22640
  register(spec, home) {
22404
22641
  return registerMcpServersJson(this, this.configPath(home), spec);
@@ -22408,7 +22645,7 @@ var init_mcp_clients = __esm({
22408
22645
  id: "cursor",
22409
22646
  label: "Cursor",
22410
22647
  bin: "cursor",
22411
- configPath: (home) => join46(home, ".cursor", "mcp.json"),
22648
+ configPath: (home) => join47(home, ".cursor", "mcp.json"),
22412
22649
  isPresent(home) {
22413
22650
  const p = this.configPath(home);
22414
22651
  return existsSync44(p) || existsSync44(dirname21(p));
@@ -22435,9 +22672,9 @@ __export(companions_exports, {
22435
22672
  uninstallCodeindex: () => uninstallCodeindex,
22436
22673
  unregisterCodeindexMcp: () => unregisterCodeindexMcp
22437
22674
  });
22438
- import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync40, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync38, writeFileSync as writeFileSync13 } from "node:fs";
22675
+ import { existsSync as existsSync45, mkdirSync as mkdirSync13, statSync as statSync41, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream as createWriteStream2, readFileSync as readFileSync39, writeFileSync as writeFileSync13 } from "node:fs";
22439
22676
  import { homedir as homedir17 } from "node:os";
22440
- import { join as join47 } from "node:path";
22677
+ import { join as join48 } from "node:path";
22441
22678
  import { execSync as execSync3 } from "node:child_process";
22442
22679
  import { createHash as createHash10 } from "node:crypto";
22443
22680
  function expectedCodeindexSha256(artifact) {
@@ -22486,7 +22723,7 @@ function checkCodeindexStatus() {
22486
22723
  status.installed = true;
22487
22724
  status.path = foundPath;
22488
22725
  try {
22489
- status.size = statSync40(foundPath).size;
22726
+ status.size = statSync41(foundPath).size;
22490
22727
  } catch {
22491
22728
  }
22492
22729
  try {
@@ -22512,7 +22749,7 @@ async function downloadArtifact(artifact, dest) {
22512
22749
  throw new Error(`Download failed: ${res.status} ${res.statusText}`);
22513
22750
  if (!res.body)
22514
22751
  throw new Error("Download returned empty body");
22515
- const file = createWriteStream(dest);
22752
+ const file = createWriteStream2(dest);
22516
22753
  await new Promise((resolve3, reject) => {
22517
22754
  const reader = res.body.getReader();
22518
22755
  const pump = async () => {
@@ -22547,7 +22784,7 @@ async function installCodeindex(opts = {}) {
22547
22784
  if (!expected) {
22548
22785
  throw new Error(`No pinned checksum for ${status.artifactName} \u2014 refusing to install an unverified binary. Add its sha256 to CODEINDEX_SHA256 in companions.ts.`);
22549
22786
  }
22550
- const actual = createHash10("sha256").update(readFileSync38(tmpPath)).digest("hex");
22787
+ const actual = createHash10("sha256").update(readFileSync39(tmpPath)).digest("hex");
22551
22788
  if (actual !== expected) {
22552
22789
  throw new Error(`Checksum mismatch for ${status.artifactName}.
22553
22790
  expected ${expected}
@@ -22577,7 +22814,7 @@ function registerCodeindexMcp(mcpJsonPath, binaryPath) {
22577
22814
  let cfg = {};
22578
22815
  if (existsSync45(mcpJsonPath)) {
22579
22816
  try {
22580
- cfg = JSON.parse(readFileSync38(mcpJsonPath, "utf-8"));
22817
+ cfg = JSON.parse(readFileSync39(mcpJsonPath, "utf-8"));
22581
22818
  } catch {
22582
22819
  cfg = {};
22583
22820
  }
@@ -22594,7 +22831,7 @@ function unregisterCodeindexMcp(mcpJsonPath) {
22594
22831
  return { removed: false };
22595
22832
  let cfg;
22596
22833
  try {
22597
- cfg = JSON.parse(readFileSync38(mcpJsonPath, "utf-8"));
22834
+ cfg = JSON.parse(readFileSync39(mcpJsonPath, "utf-8"));
22598
22835
  } catch {
22599
22836
  return { removed: false };
22600
22837
  }
@@ -22619,9 +22856,9 @@ var init_companions = __esm({
22619
22856
  "x86_64-windows": "a96fc8c8949bb399a44cc0615b12e458460a529c69f01e8829cc39a20f45ab29",
22620
22857
  "aarch64-windows": "42620d56272cd9ba9d785569d8d754d6b4a3e80f36f28db3e0c483fd600ed117"
22621
22858
  };
22622
- CODEINDEX_INSTALL_DIR = join47(homedir17(), ".local", "bin");
22859
+ CODEINDEX_INSTALL_DIR = join48(homedir17(), ".local", "bin");
22623
22860
  CODEINDEX_BIN_NAME = process.platform === "win32" ? "codeindex.exe" : "codeindex";
22624
- CODEINDEX_BIN_PATH = join47(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
22861
+ CODEINDEX_BIN_PATH = join48(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
22625
22862
  }
22626
22863
  });
22627
22864
 
@@ -22636,15 +22873,15 @@ __export(install_skills_exports, {
22636
22873
  skillsSourceDir: () => skillsSourceDir,
22637
22874
  uninstallSkills: () => uninstallSkills
22638
22875
  });
22639
- import { readdirSync as readdirSync32, existsSync as existsSync46, mkdirSync as mkdirSync14, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync39, statSync as statSync41, realpathSync as realpathSync4 } from "fs";
22876
+ import { readdirSync as readdirSync32, existsSync as existsSync46, mkdirSync as mkdirSync14, rmSync as rmSync8, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync40, statSync as statSync42, realpathSync as realpathSync4 } from "fs";
22640
22877
  import { createHash as createHash11 } from "crypto";
22641
- import { join as join48, basename as basename21 } from "path";
22878
+ import { join as join49, basename as basename21 } from "path";
22642
22879
  function skillsSourceDir() {
22643
- return join48(import.meta.dirname, "..", "skills");
22880
+ return join49(import.meta.dirname, "..", "skills");
22644
22881
  }
22645
22882
  function version() {
22646
22883
  try {
22647
- return JSON.parse(readFileSync39(new URL("../package.json", import.meta.url), "utf-8")).version || "0.0.0";
22884
+ return JSON.parse(readFileSync40(new URL("../package.json", import.meta.url), "utf-8")).version || "0.0.0";
22648
22885
  } catch {
22649
22886
  return "0.0.0";
22650
22887
  }
@@ -22653,9 +22890,9 @@ function skillsFingerprint() {
22653
22890
  const src = skillsSourceDir();
22654
22891
  const h = createHash11("sha256");
22655
22892
  for (const name of bundledSkillNames()) {
22656
- for (const rel of filesUnder(join48(src, name)).sort()) {
22893
+ for (const rel of filesUnder(join49(src, name)).sort()) {
22657
22894
  h.update(name + "/" + rel);
22658
- h.update(readFileSync39(join48(src, name, rel)));
22895
+ h.update(readFileSync40(join49(src, name, rel)));
22659
22896
  }
22660
22897
  }
22661
22898
  return h.digest("hex").slice(0, 12);
@@ -22665,7 +22902,7 @@ function filesUnder(dir, prefix = "") {
22665
22902
  for (const e of readdirSync32(dir, { withFileTypes: true })) {
22666
22903
  if (e.name === MARKER) continue;
22667
22904
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
22668
- if (e.isDirectory()) out.push(...filesUnder(join48(dir, e.name), rel));
22905
+ if (e.isDirectory()) out.push(...filesUnder(join49(dir, e.name), rel));
22669
22906
  else out.push(rel);
22670
22907
  }
22671
22908
  return out;
@@ -22676,7 +22913,7 @@ function skillStamp() {
22676
22913
  function bundledSkillNames() {
22677
22914
  const src = skillsSourceDir();
22678
22915
  if (!existsSync46(src)) return [];
22679
- return readdirSync32(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join48(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
22916
+ return readdirSync32(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync46(join49(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
22680
22917
  }
22681
22918
  function realPathOrSelf(p) {
22682
22919
  try {
@@ -22689,7 +22926,7 @@ function claudeSkillTargets() {
22689
22926
  const targets = [];
22690
22927
  const seen = /* @__PURE__ */ new Set();
22691
22928
  for (const home of claudeHomeDirs()) {
22692
- const dir = join48(home, "skills");
22929
+ const dir = join49(home, "skills");
22693
22930
  const key2 = realPathOrSelf(dir);
22694
22931
  if (seen.has(key2)) continue;
22695
22932
  seen.add(key2);
@@ -22700,7 +22937,7 @@ function claudeSkillTargets() {
22700
22937
  dir,
22701
22938
  // Judge each profile on ITS OWN transcripts, not the primary's: a home
22702
22939
  // with no projects/ is a directory someone made, not a Claude install.
22703
- available: existsSync46(join48(home, "projects"))
22940
+ available: existsSync46(join49(home, "projects"))
22704
22941
  });
22705
22942
  }
22706
22943
  if (targets.length === 0) {
@@ -22715,16 +22952,16 @@ function skillTargets() {
22715
22952
  // both produces "skill conflict" duplicate warnings. Every tool we support
22716
22953
  // has its own dir, so per-tool coverage is complete.
22717
22954
  ...claudeSkillTargets(),
22718
- { id: "agy", label: "Antigravity", dir: join48(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
22955
+ { id: "agy", label: "Antigravity", dir: join49(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
22719
22956
  { id: "codex", label: "Codex", dir: codexBackend.skillsDir(), available: codexBackend.isAvailable() },
22720
22957
  { id: "opencode", label: "OpenCode", dir: opencodeBackend.skillsDir(), available: opencodeBackend.isAvailable() },
22721
22958
  // Cursor DOES have its own dir (~/.cursor/skills). `skills-cursor` next to
22722
22959
  // it is Cursor's bundled set — read-only, never an install target.
22723
- { id: "cursor", label: "Cursor", dir: join48(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
22960
+ { id: "cursor", label: "Cursor", dir: join49(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
22724
22961
  ];
22725
22962
  }
22726
22963
  function isManaged(dir) {
22727
- return existsSync46(join48(dir, MARKER));
22964
+ return existsSync46(join49(dir, MARKER));
22728
22965
  }
22729
22966
  function installSkills(opts = {}) {
22730
22967
  const onlyAvailable = opts.onlyAvailable ?? true;
@@ -22741,15 +22978,15 @@ function installSkills(opts = {}) {
22741
22978
  continue;
22742
22979
  }
22743
22980
  for (const name of names) {
22744
- const dest = join48(t.dir, name);
22981
+ const dest = join49(t.dir, name);
22745
22982
  if (existsSync46(dest) && !isManaged(dest)) {
22746
22983
  skippedUserOwned.push(name);
22747
22984
  continue;
22748
22985
  }
22749
22986
  mkdirSync14(t.dir, { recursive: true });
22750
- rmSync7(dest, { recursive: true, force: true });
22751
- cpSync(join48(src, name), dest, { recursive: true });
22752
- writeFileSync14(join48(dest, MARKER), `${stamp}
22987
+ rmSync8(dest, { recursive: true, force: true });
22988
+ cpSync(join49(src, name), dest, { recursive: true });
22989
+ writeFileSync14(join49(dest, MARKER), `${stamp}
22753
22990
  `);
22754
22991
  installed.push(name);
22755
22992
  }
@@ -22768,9 +23005,9 @@ function uninstallSkills() {
22768
23005
  }
22769
23006
  for (const entry of readdirSync32(t.dir, { withFileTypes: true })) {
22770
23007
  if (!entry.isDirectory()) continue;
22771
- const dir = join48(t.dir, entry.name);
23008
+ const dir = join49(t.dir, entry.name);
22772
23009
  if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
22773
- rmSync7(dir, { recursive: true, force: true });
23010
+ rmSync8(dir, { recursive: true, force: true });
22774
23011
  removed.push(entry.name);
22775
23012
  }
22776
23013
  }
@@ -22785,10 +23022,10 @@ function skillsNeedRefresh() {
22785
23022
  for (const t of skillTargets()) {
22786
23023
  if (!t.available) continue;
22787
23024
  for (const name of names) {
22788
- const marker = join48(t.dir, name, MARKER);
23025
+ const marker = join49(t.dir, name, MARKER);
22789
23026
  if (!existsSync46(marker)) return true;
22790
23027
  try {
22791
- if (readFileSync39(marker, "utf-8").trim() !== cur) return true;
23028
+ if (readFileSync40(marker, "utf-8").trim() !== cur) return true;
22792
23029
  } catch {
22793
23030
  return true;
22794
23031
  }
@@ -22803,11 +23040,11 @@ function skillStatus() {
22803
23040
  let installed = 0;
22804
23041
  let ver = null;
22805
23042
  for (const name of names) {
22806
- const marker = join48(t.dir, name, MARKER);
23043
+ const marker = join49(t.dir, name, MARKER);
22807
23044
  if (existsSync46(marker)) {
22808
23045
  installed++;
22809
23046
  try {
22810
- ver = readFileSync39(marker, "utf-8").trim();
23047
+ ver = readFileSync40(marker, "utf-8").trim();
22811
23048
  } catch {
22812
23049
  }
22813
23050
  }
@@ -22831,8 +23068,8 @@ var init_install_skills = __esm({
22831
23068
  });
22832
23069
 
22833
23070
  // ../engine/dist/core/artifact-codec.js
22834
- import { readFileSync as readFileSync40 } from "fs";
22835
- import { basename as basename22, join as join49 } from "path";
23071
+ import { readFileSync as readFileSync41 } from "fs";
23072
+ import { basename as basename22, join as join50 } from "path";
22836
23073
  import { homedir as homedir18 } from "os";
22837
23074
  function parseCommandText(content, format, fallbackName) {
22838
23075
  if (format === "toml") {
@@ -22855,41 +23092,41 @@ function parseAgentText(content, format, fallbackName) {
22855
23092
  return { name: fm.name || fallbackName, description: fm.description || "", tools: fm.tools, body: body2.trim() };
22856
23093
  }
22857
23094
  function readCommand(filePath, format) {
22858
- return parseCommandText(readFileSync40(filePath, "utf-8"), format, base2(filePath));
23095
+ return parseCommandText(readFileSync41(filePath, "utf-8"), format, base2(filePath));
22859
23096
  }
22860
23097
  function readAgent(filePath, format) {
22861
- return parseAgentText(readFileSync40(filePath, "utf-8"), format, base2(filePath));
23098
+ return parseAgentText(readFileSync41(filePath, "utf-8"), format, base2(filePath));
22862
23099
  }
22863
23100
  function readInstructions(filePath, name) {
22864
- const content = readFileSync40(filePath, "utf-8");
23101
+ const content = readFileSync41(filePath, "utf-8");
22865
23102
  return { name, description: "", body: content };
22866
23103
  }
22867
23104
  function commandPath(tool, name) {
22868
23105
  switch (tool) {
22869
23106
  case "claude":
22870
- return join49(claudeBackend.commandsDir(), `${name}.md`);
23107
+ return join50(claudeBackend.commandsDir(), `${name}.md`);
22871
23108
  case "agy":
22872
- return join49(join49(agyBackend.homeDir(), "commands"), `${name}.toml`);
23109
+ return join50(join50(agyBackend.homeDir(), "commands"), `${name}.toml`);
22873
23110
  case "opencode":
22874
- return join49(opencodeBackend.commandsDir(), `${name}.md`);
23111
+ return join50(opencodeBackend.commandsDir(), `${name}.md`);
22875
23112
  case "codex":
22876
- return join49(codexBackend.promptsDir(), `${name}.md`);
23113
+ return join50(codexBackend.promptsDir(), `${name}.md`);
22877
23114
  case "cursor":
22878
- return join49(cursorHomeDir(), "commands", `${name}.md`);
23115
+ return join50(cursorHomeDir(), "commands", `${name}.md`);
22879
23116
  }
22880
23117
  }
22881
23118
  function agentPath(tool, name) {
22882
23119
  switch (tool) {
22883
23120
  case "claude":
22884
- return join49(claudeBackend.agentsDir(), `${name}.md`);
23121
+ return join50(claudeBackend.agentsDir(), `${name}.md`);
22885
23122
  case "agy":
22886
- return join49(join49(agyBackend.homeDir(), "agents"), `${name}.md`);
23123
+ return join50(join50(agyBackend.homeDir(), "agents"), `${name}.md`);
22887
23124
  case "opencode":
22888
- return join49(opencodeBackend.agentsDir(), `${name}.md`);
23125
+ return join50(opencodeBackend.agentsDir(), `${name}.md`);
22889
23126
  case "codex":
22890
- return join49(codexBackend.agentsDir(), `${name}.toml`);
23127
+ return join50(codexBackend.agentsDir(), `${name}.toml`);
22891
23128
  case "cursor":
22892
- return join49(cursorHomeDir(), "agents", `${name}.md`);
23129
+ return join50(cursorHomeDir(), "agents", `${name}.md`);
22893
23130
  }
22894
23131
  }
22895
23132
  function instructionsFilename(tool) {
@@ -22914,21 +23151,21 @@ function instructionsFilename(tool) {
22914
23151
  function instructionsPath(tool, projectPath) {
22915
23152
  const file = instructionsFilename(tool);
22916
23153
  if (projectPath)
22917
- return join49(projectPath, file);
23154
+ return join50(projectPath, file);
22918
23155
  switch (tool) {
22919
23156
  case "claude":
22920
- return join49(claudeBackend.homeDir(), file);
23157
+ return join50(claudeBackend.homeDir(), file);
22921
23158
  case "agy":
22922
- return join49(agyBackend.homeDir(), file);
23159
+ return join50(agyBackend.homeDir(), file);
22923
23160
  case "codex":
22924
- return join49(codexBackend.homeDir(), file);
23161
+ return join50(codexBackend.homeDir(), file);
22925
23162
  case "opencode":
22926
- return join49(homedir18(), ".config", "opencode", file);
23163
+ return join50(homedir18(), ".config", "opencode", file);
22927
23164
  // Cursor has no single user-scope instructions FILE — user-level guidance
22928
23165
  // is User Rules plus ~/.cursor/rules/*.mdc. AGENTS.md at the home root is
22929
23166
  // the closest equivalent and is what a mirror can actually write.
22930
23167
  case "cursor":
22931
- return join49(cursorHomeDir(), file);
23168
+ return join50(cursorHomeDir(), file);
22932
23169
  }
22933
23170
  }
22934
23171
  function emit(type, art, toTool, projectPath) {
@@ -22992,8 +23229,8 @@ __export(toolkit_sync_exports, {
22992
23229
  supportedTargetsFor: () => supportedTargetsFor,
22993
23230
  writeMcpEntry: () => writeMcpEntry
22994
23231
  });
22995
- import { existsSync as existsSync47, mkdirSync as mkdirSync15, readFileSync as readFileSync41, writeFileSync as writeFileSync15, cpSync as cpSync2 } from "fs";
22996
- import { dirname as dirname22, join as join50, basename as basename23 } from "path";
23232
+ import { existsSync as existsSync47, mkdirSync as mkdirSync15, readFileSync as readFileSync42, writeFileSync as writeFileSync15, cpSync as cpSync2 } from "fs";
23233
+ import { dirname as dirname22, join as join51, basename as basename23 } from "path";
22997
23234
  import { homedir as homedir19 } from "os";
22998
23235
  function supportedTargetsFor(type) {
22999
23236
  return SUPPORTED_TARGETS[type];
@@ -23035,14 +23272,14 @@ function skillsDirFor(tool) {
23035
23272
  case "claude":
23036
23273
  return claudeBackend.skillsDir();
23037
23274
  case "agy":
23038
- return join50(agyBackend.homeDir(), "skills");
23275
+ return join51(agyBackend.homeDir(), "skills");
23039
23276
  case "opencode":
23040
23277
  return opencodeBackend.skillsDir();
23041
23278
  case "codex":
23042
23279
  return codexBackend.skillsDir();
23043
23280
  // user skills, NOT .system
23044
23281
  case "cursor":
23045
- return join50(cursorHomeDir(), "skills");
23282
+ return join51(cursorHomeDir(), "skills");
23046
23283
  }
23047
23284
  }
23048
23285
  function copySkill(row, toTool) {
@@ -23050,7 +23287,7 @@ function copySkill(row, toTool) {
23050
23287
  if (!existsSync47(skillDir))
23051
23288
  return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
23052
23289
  const name = row.extra.skillName || basename23(skillDir);
23053
- const targetDir = join50(skillsDirFor(toTool), name);
23290
+ const targetDir = join51(skillsDirFor(toTool), name);
23054
23291
  if (existsSync47(targetDir))
23055
23292
  return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
23056
23293
  try {
@@ -23065,20 +23302,20 @@ function readMcpEntry(tool, name) {
23065
23302
  const home = homedir19();
23066
23303
  const tries = [];
23067
23304
  if (tool === "claude") {
23068
- tries.push({ path: join50(home, ".mcp.json"), key: "mcpServers" });
23069
- tries.push({ path: join50(home, ".claude.json"), key: "mcpServers" });
23305
+ tries.push({ path: join51(home, ".mcp.json"), key: "mcpServers" });
23306
+ tries.push({ path: join51(home, ".claude.json"), key: "mcpServers" });
23070
23307
  } else if (tool === "gemini") {
23071
- tries.push({ path: join50(home, ".gemini", "settings.json"), key: "mcpServers" });
23308
+ tries.push({ path: join51(home, ".gemini", "settings.json"), key: "mcpServers" });
23072
23309
  } else if (tool === "agy") {
23073
- tries.push({ path: join50(home, ".gemini", "config", "mcp_config.json"), key: "mcpServers" });
23310
+ tries.push({ path: join51(home, ".gemini", "config", "mcp_config.json"), key: "mcpServers" });
23074
23311
  } else if (tool === "cursor") {
23075
- tries.push({ path: join50(cursorHomeDir(), "mcp.json"), key: "mcpServers" });
23312
+ tries.push({ path: join51(cursorHomeDir(), "mcp.json"), key: "mcpServers" });
23076
23313
  } else if (tool === "opencode") {
23077
- tries.push({ path: join50(home, ".config", "opencode", "opencode.json"), key: "mcp" });
23078
- tries.push({ path: join50(home, ".config", "opencode", "config.json"), key: "mcp" });
23079
- tries.push({ path: join50(home, ".opencode", "config.json"), key: "mcp" });
23314
+ tries.push({ path: join51(home, ".config", "opencode", "opencode.json"), key: "mcp" });
23315
+ tries.push({ path: join51(home, ".config", "opencode", "config.json"), key: "mcp" });
23316
+ tries.push({ path: join51(home, ".opencode", "config.json"), key: "mcp" });
23080
23317
  } else if (tool === "codex") {
23081
- const entry = readCodexMcpEntry(join50(home, ".codex", "config.toml"), name);
23318
+ const entry = readCodexMcpEntry(join51(home, ".codex", "config.toml"), name);
23082
23319
  if (entry)
23083
23320
  return entry;
23084
23321
  }
@@ -23086,7 +23323,7 @@ function readMcpEntry(tool, name) {
23086
23323
  if (!existsSync47(t.path))
23087
23324
  continue;
23088
23325
  try {
23089
- const cfg = JSON.parse(readFileSync41(t.path, "utf-8"));
23326
+ const cfg = JSON.parse(readFileSync42(t.path, "utf-8"));
23090
23327
  const block = cfg[t.key] || {};
23091
23328
  if (block[name])
23092
23329
  return block[name];
@@ -23100,7 +23337,7 @@ function readCodexMcpEntry(path, target) {
23100
23337
  return null;
23101
23338
  let content;
23102
23339
  try {
23103
- content = readFileSync41(path, "utf-8");
23340
+ content = readFileSync42(path, "utf-8");
23104
23341
  } catch {
23105
23342
  return null;
23106
23343
  }
@@ -23151,7 +23388,7 @@ function readCodexMcpEntry(path, target) {
23151
23388
  function writeCodexMcpEntry(path, name, entry) {
23152
23389
  let content = "";
23153
23390
  if (existsSync47(path))
23154
- content = readFileSync41(path, "utf-8");
23391
+ content = readFileSync42(path, "utf-8");
23155
23392
  const header = `[mcp_servers.${name}]`;
23156
23393
  if (content.includes(header))
23157
23394
  return false;
@@ -23187,7 +23424,7 @@ function writeCodexMcpEntry(path, name, entry) {
23187
23424
  function writeMcpEntry(toTool, name, entry) {
23188
23425
  const home = homedir19();
23189
23426
  if (toTool === "codex") {
23190
- const path2 = join50(home, ".codex", "config.toml");
23427
+ const path2 = join51(home, ".codex", "config.toml");
23191
23428
  if (!writeCodexMcpEntry(path2, name, entry))
23192
23429
  return { ok: false, status: 409, error: `MCP "${name}" already exists in ${path2}.` };
23193
23430
  return { ok: true, targetPath: path2 };
@@ -23195,22 +23432,22 @@ function writeMcpEntry(toTool, name, entry) {
23195
23432
  let path;
23196
23433
  let key2;
23197
23434
  if (toTool === "claude") {
23198
- path = join50(home, ".mcp.json");
23435
+ path = join51(home, ".mcp.json");
23199
23436
  key2 = "mcpServers";
23200
23437
  } else if (toTool === "agy") {
23201
- path = join50(home, ".gemini", "config", "mcp_config.json");
23438
+ path = join51(home, ".gemini", "config", "mcp_config.json");
23202
23439
  key2 = "mcpServers";
23203
23440
  } else if (toTool === "cursor") {
23204
- path = join50(cursorHomeDir(), "mcp.json");
23441
+ path = join51(cursorHomeDir(), "mcp.json");
23205
23442
  key2 = "mcpServers";
23206
23443
  } else {
23207
- path = join50(home, ".config", "opencode", "opencode.json");
23444
+ path = join51(home, ".config", "opencode", "opencode.json");
23208
23445
  key2 = "mcp";
23209
23446
  }
23210
23447
  let cfg = {};
23211
23448
  if (existsSync47(path)) {
23212
23449
  try {
23213
- cfg = JSON.parse(readFileSync41(path, "utf-8"));
23450
+ cfg = JSON.parse(readFileSync42(path, "utf-8"));
23214
23451
  } catch {
23215
23452
  return { ok: false, status: 500, error: `Target config is malformed: ${path}` };
23216
23453
  }
@@ -23443,11 +23680,11 @@ __export(toolkit_pull_exports, {
23443
23680
  platformRefusal: () => platformRefusal,
23444
23681
  portableCommand: () => portableCommand
23445
23682
  });
23446
- import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync42, writeFileSync as writeFileSync16 } from "node:fs";
23447
- import { dirname as dirname23, join as join51, basename as basename24, isAbsolute as isAbsolute3 } from "node:path";
23683
+ import { existsSync as existsSync48, mkdirSync as mkdirSync16, statSync as statSync43, writeFileSync as writeFileSync16 } from "node:fs";
23684
+ import { dirname as dirname23, join as join52, basename as basename24, isAbsolute as isAbsolute3 } from "node:path";
23448
23685
  function isExecutableFile(p) {
23449
23686
  try {
23450
- return statSync42(p).isFile();
23687
+ return statSync43(p).isFile();
23451
23688
  } catch {
23452
23689
  return false;
23453
23690
  }
@@ -23614,8 +23851,8 @@ function executePull(rows, opts = {}) {
23614
23851
  const outcomes = [];
23615
23852
  for (const { name, body: body2, truncated, redacted, tools } of skills) {
23616
23853
  for (const tool of tools) {
23617
- const dir = join51(skillsDirFor(tool), name);
23618
- const file = join51(dir, "SKILL.md");
23854
+ const dir = join52(skillsDirFor(tool), name);
23855
+ const file = join52(dir, "SKILL.md");
23619
23856
  if (existsSync48(file)) {
23620
23857
  outcomes.push({ type: "skill", name, tool, status: "present" });
23621
23858
  continue;
@@ -23824,8 +24061,8 @@ __export(board_hooks_exports, {
23824
24061
  removeBoardHooks: () => removeBoardHooks,
23825
24062
  resolveCliEntry: () => resolveCliEntry
23826
24063
  });
23827
- import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync42, writeFileSync as writeFileSync17 } from "node:fs";
23828
- import { dirname as dirname24, join as join52 } from "node:path";
24064
+ import { existsSync as existsSync49, mkdirSync as mkdirSync17, readFileSync as readFileSync43, writeFileSync as writeFileSync17 } from "node:fs";
24065
+ import { dirname as dirname24, join as join53 } from "node:path";
23829
24066
  function boardHookCommand(execPath, cliEntry, mode) {
23830
24067
  return `"${execPath}" "${cliEntry}" task-hook --${mode}`;
23831
24068
  }
@@ -23866,7 +24103,7 @@ function removeBoardHooks(config2) {
23866
24103
  function readConfig(file) {
23867
24104
  if (!existsSync49(file)) return { hooks: {} };
23868
24105
  try {
23869
- const parsed = JSON.parse(readFileSync42(file, "utf-8"));
24106
+ const parsed = JSON.parse(readFileSync43(file, "utf-8"));
23870
24107
  if (!parsed.hooks || typeof parsed.hooks !== "object") parsed.hooks = {};
23871
24108
  return parsed;
23872
24109
  } catch {
@@ -23878,7 +24115,7 @@ function registerBoardHooks(files, execPath, cliEntry) {
23878
24115
  for (const file of files) {
23879
24116
  if (existsSync49(file)) {
23880
24117
  try {
23881
- JSON.parse(readFileSync42(file, "utf-8"));
24118
+ JSON.parse(readFileSync43(file, "utf-8"));
23882
24119
  } catch {
23883
24120
  continue;
23884
24121
  }
@@ -23895,8 +24132,8 @@ function registerBoardHooks(files, execPath, cliEntry) {
23895
24132
  return written;
23896
24133
  }
23897
24134
  function resolveCliEntry(distDir) {
23898
- const bundled = join52(distDir, "cli.js");
23899
- return existsSync49(bundled) ? bundled : join52(distDir, "cli.ts");
24135
+ const bundled = join53(distDir, "cli.js");
24136
+ return existsSync49(bundled) ? bundled : join53(distDir, "cli.ts");
23900
24137
  }
23901
24138
  var BOARD_HOOKS;
23902
24139
  var init_board_hooks = __esm({
@@ -24105,11 +24342,16 @@ async function repairAll(opts = {}) {
24105
24342
  scanned++;
24106
24343
  if (opts.verbose && scanned % 50 === 0) log6(`checked ${scanned}/${ids.length}\u2026`);
24107
24344
  const arch = await fetchJson(`${primary.url}/api/conversations/${id}/raw-archive?count=1`, primary.token);
24108
- const archiveMsgs = typeof arch?.messages === "number" ? arch.messages : 0;
24109
- if (archiveMsgs < minMessages) continue;
24110
- const meta = await fetchJson(`${primary.url}/api/conversations/${id}/metadata`, primary.token);
24111
- const current = typeof meta?.messageCount === "number" ? meta.messageCount : 0;
24112
- if (archiveMsgs <= current) continue;
24345
+ const counted = arch && arch.messages !== null;
24346
+ if (counted) {
24347
+ const archiveMsgs = typeof arch?.messages === "number" ? arch.messages : 0;
24348
+ if (archiveMsgs < minMessages) continue;
24349
+ const meta = await fetchJson(`${primary.url}/api/conversations/${id}/metadata`, primary.token);
24350
+ const current = typeof meta?.messageCount === "number" ? meta.messageCount : 0;
24351
+ if (archiveMsgs <= current) continue;
24352
+ } else if (!arch) {
24353
+ continue;
24354
+ }
24113
24355
  const r = await repairSession(id, { dryRun: !opts.apply, verbose: opts.verbose, server: opts.server });
24114
24356
  if (r.status === "already-full") continue;
24115
24357
  candidates2.push(r);
@@ -24294,21 +24536,21 @@ __export(guard_registration_exports, {
24294
24536
  unregisterGuard: () => unregisterGuard
24295
24537
  });
24296
24538
  import { homedir as homedir20 } from "os";
24297
- import { join as join53 } from "path";
24539
+ import { join as join54 } from "path";
24298
24540
  function opencodeConfigDir() {
24299
24541
  const xdg = process.env.XDG_CONFIG_HOME;
24300
- const base4 = xdg && xdg.trim() ? xdg : join53(homedir20(), ".config");
24301
- return join53(base4, "opencode");
24542
+ const base4 = xdg && xdg.trim() ? xdg : join54(homedir20(), ".config");
24543
+ return join54(base4, "opencode");
24302
24544
  }
24303
24545
  function opencodePluginDir() {
24304
- return join53(opencodeConfigDir(), "plugins");
24546
+ return join54(opencodeConfigDir(), "plugins");
24305
24547
  }
24306
24548
  function guardTargets() {
24307
24549
  return [
24308
24550
  {
24309
24551
  tool: "codex",
24310
24552
  label: "Codex",
24311
- configPath: join53(codexBackend.homeDir(), "hooks.json"),
24553
+ configPath: join54(codexBackend.homeDir(), "hooks.json"),
24312
24554
  homeDir: codexBackend.homeDir(),
24313
24555
  // Codex will not run a hook until a person has reviewed it, and the
24314
24556
  // approval is bound to the hook's hash. There is no way to grant it from
@@ -24321,20 +24563,20 @@ function guardTargets() {
24321
24563
  {
24322
24564
  tool: "agy",
24323
24565
  label: "Antigravity",
24324
- configPath: join53(agyBackend.homeDir(), "hooks.json"),
24566
+ configPath: join54(agyBackend.homeDir(), "hooks.json"),
24325
24567
  homeDir: agyBackend.homeDir()
24326
24568
  },
24327
24569
  {
24328
24570
  tool: "cursor",
24329
24571
  label: "Cursor",
24330
- configPath: join53(cursorBackend.homeDir(), "hooks.json"),
24572
+ configPath: join54(cursorBackend.homeDir(), "hooks.json"),
24331
24573
  homeDir: cursorBackend.homeDir(),
24332
24574
  note: "Cursor checks a shell command before it runs and a file edit after it is written."
24333
24575
  },
24334
24576
  {
24335
24577
  tool: "opencode",
24336
24578
  label: "OpenCode",
24337
- configPath: join53(opencodePluginDir(), "chat-recall-guard.js"),
24579
+ configPath: join54(opencodePluginDir(), "chat-recall-guard.js"),
24338
24580
  homeDir: opencodeConfigDir(),
24339
24581
  note: "OpenCode plugin hooks do not fire for subagent tool calls, so a delegating agent bypasses this."
24340
24582
  }
@@ -24757,8 +24999,8 @@ __export(collector_exports, {
24757
24999
  });
24758
25000
  import { spawn as spawn3 } from "node:child_process";
24759
25001
  import { execSync as execSync4 } from "node:child_process";
24760
- import { readFileSync as readFileSync43 } from "node:fs";
24761
- import { join as join54, basename as basename26 } from "node:path";
25002
+ import { readFileSync as readFileSync44 } from "node:fs";
25003
+ import { join as join55, basename as basename26 } from "node:path";
24762
25004
  async function resolveCodeindexBin(autoInstall = true) {
24763
25005
  let st = checkCodeindexStatus();
24764
25006
  if (!st.installed && autoInstall)
@@ -24940,7 +25182,7 @@ async function collectCode(opts) {
24940
25182
  }
24941
25183
  const complexity = (r) => {
24942
25184
  try {
24943
- const txt = readFileSync43(join54(ws, r), "utf8");
25185
+ const txt = readFileSync44(join55(ws, r), "utf8");
24944
25186
  if (txt.length > 2e6)
24945
25187
  return 1;
24946
25188
  const m = txt.match(CF_RE);
@@ -25724,8 +25966,8 @@ __export(project_tasks_exports, {
25724
25966
  pushProjectTaskStatuses: () => pushProjectTaskStatuses,
25725
25967
  renderTeamTasksMd: () => renderTeamTasksMd
25726
25968
  });
25727
- import { existsSync as existsSync51, readFileSync as readFileSync44, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as statSync43 } from "node:fs";
25728
- import { join as join55, dirname as dirname25 } from "node:path";
25969
+ import { existsSync as existsSync51, readFileSync as readFileSync45, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as statSync44 } from "node:fs";
25970
+ import { join as join56, dirname as dirname25 } from "node:path";
25729
25971
  import { homedir as homedir21 } from "node:os";
25730
25972
  function isTeamTaskStatus(v) {
25731
25973
  return !!v && TEAM_STATUSES.has(v);
@@ -25776,11 +26018,11 @@ function parseTaskFile(content) {
25776
26018
  return out;
25777
26019
  }
25778
26020
  function ledgerPath2() {
25779
- return join55(homedir21(), ".chat-recall", "project-tasks-ledger.json");
26021
+ return join56(homedir21(), ".chat-recall", "project-tasks-ledger.json");
25780
26022
  }
25781
26023
  function readLedger() {
25782
26024
  try {
25783
- return JSON.parse(readFileSync44(ledgerPath2(), "utf8"));
26025
+ return JSON.parse(readFileSync45(ledgerPath2(), "utf8"));
25784
26026
  } catch {
25785
26027
  return {};
25786
26028
  }
@@ -25809,18 +26051,18 @@ async function pushProjectTaskStatuses(base4, authHeaders, opts = {}) {
25809
26051
  continue;
25810
26052
  }
25811
26053
  for (const project of projects) {
25812
- const file = join55(project, fk.filename);
26054
+ const file = join56(project, fk.filename);
25813
26055
  if (!existsSync51(file)) continue;
25814
26056
  let mtime;
25815
26057
  try {
25816
- mtime = statSync43(file).mtimeMs;
26058
+ mtime = statSync44(file).mtimeMs;
25817
26059
  } catch {
25818
26060
  continue;
25819
26061
  }
25820
26062
  if (ledger[file] === mtime) continue;
25821
26063
  let changed;
25822
26064
  try {
25823
- changed = parseTaskFile(readFileSync44(file, "utf8")).filter((t) => t.kind === fk.kind);
26065
+ changed = parseTaskFile(readFileSync45(file, "utf8")).filter((t) => t.kind === fk.kind);
25824
26066
  } catch {
25825
26067
  continue;
25826
26068
  }
@@ -25892,8 +26134,8 @@ __export(intent_drain_exports, {
25892
26134
  drainSyncIntents: () => drainSyncIntents,
25893
26135
  instructionTargets: () => instructionTargets
25894
26136
  });
25895
- import { existsSync as existsSync52, readFileSync as readFileSync45, writeFileSync as writeFileSync19, appendFileSync, mkdirSync as mkdirSync19 } from "node:fs";
25896
- import { join as join56 } from "node:path";
26137
+ import { existsSync as existsSync52, readFileSync as readFileSync46, writeFileSync as writeFileSync19, appendFileSync, mkdirSync as mkdirSync19 } from "node:fs";
26138
+ import { join as join57 } from "node:path";
25897
26139
  import { fileURLToPath as fileURLToPath4 } from "node:url";
25898
26140
  import { dirname as dirname26 } from "node:path";
25899
26141
  function instructionTargets(rootPath, isGlobal) {
@@ -25907,16 +26149,16 @@ function instructionTargets(rootPath, isGlobal) {
25907
26149
  const tools = installed.length ? installed : [INSTRUCTION_TOOLS[0]];
25908
26150
  if (isGlobal) return [...new Set(tools.map((t) => instructionsPath(t.id)))];
25909
26151
  if (!rootPath) return [];
25910
- const present = INSTRUCTION_FILENAMES.map((f) => join56(rootPath, f)).filter((p) => existsSync52(p));
26152
+ const present = INSTRUCTION_FILENAMES.map((f) => join57(rootPath, f)).filter((p) => existsSync52(p));
25911
26153
  if (present.length) return present;
25912
- return [...new Set(tools.map((t) => join56(rootPath, instructionsFilename(t.id))))];
26154
+ return [...new Set(tools.map((t) => join57(rootPath, instructionsFilename(t.id))))];
25913
26155
  }
25914
26156
  function applyCodeRecommendation(intent) {
25915
26157
  try {
25916
26158
  const meta = JSON.parse(intent.name || "{}");
25917
26159
  if (intent.artifact_type === "write_tasks_file") {
25918
26160
  if (!meta.rootPath || !meta.content) return { status: "error", result: JSON.stringify({ error: "missing rootPath or content" }) };
25919
- const file = join56(meta.rootPath, meta.filename || "CODE_TASKS.md");
26161
+ const file = join57(meta.rootPath, meta.filename || "CODE_TASKS.md");
25920
26162
  writeFileSync19(file, meta.content);
25921
26163
  return { status: "done", result: JSON.stringify({ wrote: file }) };
25922
26164
  }
@@ -25935,7 +26177,7 @@ function applyCodeRecommendation(intent) {
25935
26177
  mkdirSync19(dir, { recursive: true });
25936
26178
  } catch {
25937
26179
  }
25938
- const existing = existsSync52(file) ? readFileSync45(file, "utf8") : "";
26180
+ const existing = existsSync52(file) ? readFileSync46(file, "utf8") : "";
25939
26181
  if (existing.includes(text)) {
25940
26182
  skipped.push(file);
25941
26183
  continue;
@@ -26110,10 +26352,10 @@ var init_intent_drain = __esm({
26110
26352
  ownVersion2 = "0.0.0";
26111
26353
  try {
26112
26354
  const dir = dirname26(fileURLToPath4(import.meta.url));
26113
- ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
26355
+ ownVersion2 = JSON.parse(readFileSync46(join57(dir, "../package.json"), "utf-8")).version || ownVersion2;
26114
26356
  } catch {
26115
26357
  try {
26116
- if (true) ownVersion2 = "0.7.16";
26358
+ if (true) ownVersion2 = "0.7.18";
26117
26359
  } catch {
26118
26360
  }
26119
26361
  }
@@ -26314,48 +26556,48 @@ var team_merge_exports = {};
26314
26556
  __export(team_merge_exports, {
26315
26557
  mergePullResult: () => mergePullResult
26316
26558
  });
26317
- import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync46, rmSync as rmSync8, writeFileSync as writeFileSync20, statSync as statSync44 } from "fs";
26318
- import { dirname as dirname27, join as join57 } from "path";
26559
+ import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync47, rmSync as rmSync9, writeFileSync as writeFileSync20, statSync as statSync45 } from "fs";
26560
+ import { dirname as dirname27, join as join58 } from "path";
26319
26561
  import { createHash as createHash14 } from "crypto";
26320
26562
  function installPathFor(art, tool) {
26321
26563
  switch (art.type) {
26322
26564
  case "skill":
26323
26565
  switch (tool) {
26324
26566
  case "claude":
26325
- return join57(claudeBackend.skillsDir(), art.name, "SKILL.md");
26567
+ return join58(claudeBackend.skillsDir(), art.name, "SKILL.md");
26326
26568
  // Antigravity's OWN skills dir. It used to resolve to the shared
26327
26569
  // ~/.gemini/skills while toolkit-sync installed to
26328
26570
  // ~/.gemini/antigravity-cli/skills, so a team skill landed in one place
26329
26571
  // and a synced skill in the other.
26330
26572
  case "agy":
26331
- return join57(agyBackend.homeDir(), "skills", art.name, "SKILL.md");
26573
+ return join58(agyBackend.homeDir(), "skills", art.name, "SKILL.md");
26332
26574
  case "opencode":
26333
- return join57(opencodeBackend.skillsDir(), art.name, "SKILL.md");
26575
+ return join58(opencodeBackend.skillsDir(), art.name, "SKILL.md");
26334
26576
  // User skills go to ~/.codex/skills (NOT .system, which is OpenAI's bundle).
26335
26577
  case "codex":
26336
- return join57(codexBackend.skillsDir(), art.name, "SKILL.md");
26578
+ return join58(codexBackend.skillsDir(), art.name, "SKILL.md");
26337
26579
  case "cursor":
26338
- return join57(cursorHomeDir(), "skills", art.name, "SKILL.md");
26580
+ return join58(cursorHomeDir(), "skills", art.name, "SKILL.md");
26339
26581
  }
26340
26582
  break;
26341
26583
  case "agent":
26342
26584
  if (tool === "claude")
26343
- return join57(claudeBackend.agentsDir(), `${art.name}.md`);
26585
+ return join58(claudeBackend.agentsDir(), `${art.name}.md`);
26344
26586
  return null;
26345
26587
  case "command":
26346
26588
  if (tool === "claude")
26347
- return join57(claudeBackend.commandsDir(), `${art.name}.md`);
26589
+ return join58(claudeBackend.commandsDir(), `${art.name}.md`);
26348
26590
  return null;
26349
26591
  case "hook":
26350
26592
  if (tool === "claude")
26351
- return join57(claudeBackend.homeDir(), "chat-recall-hooks", `${art.name}.json`);
26593
+ return join58(claudeBackend.homeDir(), "chat-recall-hooks", `${art.name}.json`);
26352
26594
  return null;
26353
26595
  case "plan":
26354
26596
  switch (tool) {
26355
26597
  case "claude":
26356
- return join57(claudeBackend.plansDir(), `${art.name}.md`);
26598
+ return join58(claudeBackend.plansDir(), `${art.name}.md`);
26357
26599
  case "opencode":
26358
- return join57(opencodeBackend.plansDir(), `${art.name}.md`);
26600
+ return join58(opencodeBackend.plansDir(), `${art.name}.md`);
26359
26601
  case "agy":
26360
26602
  return null;
26361
26603
  // Antigravity plans live per-session
@@ -26366,11 +26608,11 @@ function installPathFor(art, tool) {
26366
26608
  }
26367
26609
  break;
26368
26610
  case "instructions":
26369
- return join57(getDataDir(), "team-instructions", tool, `${art.name}.md`);
26611
+ return join58(getDataDir(), "team-instructions", tool, `${art.name}.md`);
26370
26612
  case "mcp":
26371
- return join57(getDataDir(), "team-mcps", `${art.name}.json`);
26613
+ return join58(getDataDir(), "team-mcps", `${art.name}.json`);
26372
26614
  case "plugin":
26373
- return join57(getDataDir(), "team-plugins", tool, art.name, "manifest.json");
26615
+ return join58(getDataDir(), "team-plugins", tool, art.name, "manifest.json");
26374
26616
  }
26375
26617
  return null;
26376
26618
  }
@@ -26432,7 +26674,7 @@ async function mergePullResult(opts) {
26432
26674
  continue;
26433
26675
  try {
26434
26676
  if (existsSync53(path))
26435
- rmSync8(path, { force: true });
26677
+ rmSync9(path, { force: true });
26436
26678
  await teamInstallsForget(id, row.tool);
26437
26679
  report.removed.push({ artifactId: id, path });
26438
26680
  } catch (err) {
@@ -26451,10 +26693,10 @@ function readFileIfExists(path) {
26451
26693
  try {
26452
26694
  if (!existsSync53(path))
26453
26695
  return null;
26454
- const s = statSync44(path);
26696
+ const s = statSync45(path);
26455
26697
  if (!s.isFile())
26456
26698
  return null;
26457
- return readFileSync46(path);
26699
+ return readFileSync47(path);
26458
26700
  } catch {
26459
26701
  return null;
26460
26702
  }
@@ -26535,8 +26777,8 @@ var init_vault_crypto = __esm({
26535
26777
  });
26536
26778
 
26537
26779
  // ../engine/dist/core/vault-source.js
26538
- import { existsSync as existsSync54, readdirSync as readdirSync33, statSync as statSync45 } from "fs";
26539
- import { extname, join as join58 } from "path";
26780
+ import { existsSync as existsSync54, readdirSync as readdirSync33, statSync as statSync46 } from "fs";
26781
+ import { extname, join as join59 } from "path";
26540
26782
  function* walkVaultSources() {
26541
26783
  const t = loadSettings().team.vault;
26542
26784
  const include2 = new Set(t.syncTools);
@@ -26551,7 +26793,7 @@ function* walkVaultSources() {
26551
26793
  }
26552
26794
  function safeStat(path) {
26553
26795
  try {
26554
- const s = statSync45(path);
26796
+ const s = statSync46(path);
26555
26797
  if (!s.isFile())
26556
26798
  return null;
26557
26799
  return { mtimeMs: s.mtimeMs, size: s.size };
@@ -26573,7 +26815,7 @@ function* walkClaude() {
26573
26815
  if (proj.startsWith("."))
26574
26816
  continue;
26575
26817
  const projectPath = resolveProjectDirName(proj);
26576
- const projDir = join58(root, proj);
26818
+ const projDir = join59(root, proj);
26577
26819
  let entries = [];
26578
26820
  try {
26579
26821
  entries = readdirSync33(projDir);
@@ -26583,7 +26825,7 @@ function* walkClaude() {
26583
26825
  for (const f of entries) {
26584
26826
  if (extname(f) !== ".jsonl")
26585
26827
  continue;
26586
- const path = join58(projDir, f);
26828
+ const path = join59(projDir, f);
26587
26829
  const s = safeStat(path);
26588
26830
  if (!s)
26589
26831
  continue;
@@ -26613,7 +26855,7 @@ function* walkCursor() {
26613
26855
  }
26614
26856
  }
26615
26857
  function* walkCodex() {
26616
- const sessionsRoot = join58(codexBackend.homeDir(), "sessions");
26858
+ const sessionsRoot = join59(codexBackend.homeDir(), "sessions");
26617
26859
  if (!existsSync54(sessionsRoot))
26618
26860
  return;
26619
26861
  const stack = [sessionsRoot];
@@ -26626,7 +26868,7 @@ function* walkCodex() {
26626
26868
  continue;
26627
26869
  }
26628
26870
  for (const e of entries) {
26629
- const path = join58(dir, e.name);
26871
+ const path = join59(dir, e.name);
26630
26872
  if (e.isDirectory()) {
26631
26873
  stack.push(path);
26632
26874
  continue;
@@ -26642,7 +26884,7 @@ function* walkCodex() {
26642
26884
  }
26643
26885
  }
26644
26886
  function* walkAgy() {
26645
- const brainDir = join58(agyBackend.homeDir(), "brain");
26887
+ const brainDir = join59(agyBackend.homeDir(), "brain");
26646
26888
  if (!existsSync54(brainDir))
26647
26889
  return;
26648
26890
  let sessionDirs = [];
@@ -26652,16 +26894,16 @@ function* walkAgy() {
26652
26894
  return;
26653
26895
  }
26654
26896
  for (const rawId of sessionDirs) {
26655
- const sessionPath = join58(brainDir, rawId);
26897
+ const sessionPath = join59(brainDir, rawId);
26656
26898
  try {
26657
- if (!statSync45(sessionPath).isDirectory())
26899
+ if (!statSync46(sessionPath).isDirectory())
26658
26900
  continue;
26659
26901
  } catch {
26660
26902
  continue;
26661
26903
  }
26662
- let filePath = join58(sessionPath, ".system_generated", "logs", "transcript.jsonl");
26904
+ let filePath = join59(sessionPath, ".system_generated", "logs", "transcript.jsonl");
26663
26905
  if (!existsSync54(filePath)) {
26664
- filePath = join58(sessionPath, ".system_generated", "logs", "transcript_full.jsonl");
26906
+ filePath = join59(sessionPath, ".system_generated", "logs", "transcript_full.jsonl");
26665
26907
  }
26666
26908
  if (!existsSync54(filePath))
26667
26909
  continue;
@@ -26699,8 +26941,8 @@ __export(vault_client_exports, {
26699
26941
  vaultStatus: () => vaultStatus,
26700
26942
  vaultSync: () => vaultSync
26701
26943
  });
26702
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync47, writeFileSync as writeFileSync21, existsSync as existsSync55 } from "fs";
26703
- import { dirname as dirname28, join as join59 } from "path";
26944
+ import { mkdirSync as mkdirSync21, readFileSync as readFileSync48, writeFileSync as writeFileSync21, existsSync as existsSync55 } from "fs";
26945
+ import { dirname as dirname28, join as join60 } from "path";
26704
26946
  function context2() {
26705
26947
  const s = loadSettings().team;
26706
26948
  if (!s.enabled)
@@ -26839,7 +27081,7 @@ async function vaultSync(passphrase) {
26839
27081
  continue;
26840
27082
  }
26841
27083
  try {
26842
- const plaintext = readFileSync47(src.path);
27084
+ const plaintext = readFileSync48(src.path);
26843
27085
  const sourceSha = sha256Hex(plaintext);
26844
27086
  const priorSha = uploaded.get(uploadKey(src.sessionId, src.tool));
26845
27087
  if (priorSha === sourceSha) {
@@ -26891,7 +27133,7 @@ async function vaultSync(passphrase) {
26891
27133
  }
26892
27134
  function importedPathFor(tool, sessionId) {
26893
27135
  const base4 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
26894
- return join59(IMPORTED_ROOT(), tool, `${base4}.jsonl`);
27136
+ return join60(IMPORTED_ROOT(), tool, `${base4}.jsonl`);
26895
27137
  }
26896
27138
  async function vaultRestore(passphrase, opts = {}) {
26897
27139
  const ctx = context2();
@@ -26980,7 +27222,7 @@ var init_vault_client = __esm({
26980
27222
  uploadKey = (sessionId, tool) => `${sessionId}\0${tool}`;
26981
27223
  VaultPassphraseMismatchError = class extends Error {
26982
27224
  };
26983
- IMPORTED_ROOT = () => join59(getDataDir(), "imported");
27225
+ IMPORTED_ROOT = () => join60(getDataDir(), "imported");
26984
27226
  }
26985
27227
  });
26986
27228
 
@@ -27542,15 +27784,15 @@ __export(service_installer_exports, {
27542
27784
  uninstallService: () => uninstallService
27543
27785
  });
27544
27786
  import { execSync as execSync5 } from "child_process";
27545
- import { existsSync as existsSync56, mkdirSync as mkdirSync22, readFileSync as readFileSync48, rmSync as rmSync9, writeFileSync as writeFileSync22 } from "fs";
27787
+ import { existsSync as existsSync56, mkdirSync as mkdirSync22, readFileSync as readFileSync49, rmSync as rmSync10, writeFileSync as writeFileSync22 } from "fs";
27546
27788
  import { homedir as homedir22 } from "os";
27547
- import { join as join60 } from "path";
27789
+ import { join as join61 } from "path";
27548
27790
  import { fileURLToPath as fileURLToPath5 } from "url";
27549
27791
  function resolveDaemonPaths() {
27550
27792
  const watchJs = fileURLToPath5(new URL("./watch.js", import.meta.url));
27551
27793
  const node = process.execPath;
27552
- const dataDir = join60(homedir22(), ".chat-recall");
27553
- const logFile = join60(dataDir, "watch.log");
27794
+ const dataDir = join61(homedir22(), ".chat-recall");
27795
+ const logFile = join61(dataDir, "watch.log");
27554
27796
  return { watchJs, node, logFile, dataDir };
27555
27797
  }
27556
27798
  function tryCmd(cmd) {
@@ -27577,12 +27819,12 @@ function refreshServiceDefinition() {
27577
27819
  if (process.platform !== "linux") return false;
27578
27820
  try {
27579
27821
  const { watchJs, node, logFile } = resolveDaemonPaths();
27580
- const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
27822
+ const unitPath = join61(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
27581
27823
  if (!existsSync56(unitPath)) return false;
27582
- const current = readFileSync48(unitPath, "utf8");
27824
+ const current = readFileSync49(unitPath, "utf8");
27583
27825
  if (!current.includes(watchJs)) return false;
27584
27826
  const wanted = renderSystemdUnit(watchJs, node, logFile);
27585
- if (readFileSync48(unitPath, "utf8") === wanted) return false;
27827
+ if (readFileSync49(unitPath, "utf8") === wanted) return false;
27586
27828
  writeFileSync22(unitPath, wanted);
27587
27829
  execSync5("systemctl --user daemon-reload", { stdio: "ignore" });
27588
27830
  return true;
@@ -27608,8 +27850,8 @@ function installService() {
27608
27850
  );
27609
27851
  }
27610
27852
  function installSystemd(watchJs, node, logFile) {
27611
- const unitDir = join60(homedir22(), ".config", "systemd", "user");
27612
- const unitPath = join60(unitDir, "chat-recall-watch.service");
27853
+ const unitDir = join61(homedir22(), ".config", "systemd", "user");
27854
+ const unitPath = join61(unitDir, "chat-recall-watch.service");
27613
27855
  const unit = renderSystemdUnit(watchJs, node, logFile);
27614
27856
  mkdirSync22(unitDir, { recursive: true });
27615
27857
  writeFileSync22(unitPath, unit);
@@ -27688,8 +27930,8 @@ function renderSystemdUnit(watchJs, node, logFile) {
27688
27930
  }
27689
27931
  function installLaunchd(watchJs, node, logFile) {
27690
27932
  const label = "com.chat-recall.watch";
27691
- const plistDir = join60(homedir22(), "Library", "LaunchAgents");
27692
- const plistPath = join60(plistDir, `${label}.plist`);
27933
+ const plistDir = join61(homedir22(), "Library", "LaunchAgents");
27934
+ const plistPath = join61(plistDir, `${label}.plist`);
27693
27935
  const plist = renderLaunchdPlist(watchJs, node, logFile, label);
27694
27936
  mkdirSync22(plistDir, { recursive: true });
27695
27937
  writeFileSync22(plistPath, plist);
@@ -27745,9 +27987,9 @@ function uninstallService() {
27745
27987
  removed = true;
27746
27988
  } catch {
27747
27989
  }
27748
- const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
27990
+ const unitPath = join61(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
27749
27991
  if (existsSync56(unitPath)) {
27750
- rmSync9(unitPath, { force: true });
27992
+ rmSync10(unitPath, { force: true });
27751
27993
  removed = true;
27752
27994
  }
27753
27995
  try {
@@ -27757,14 +27999,14 @@ function uninstallService() {
27757
27999
  return removed;
27758
28000
  }
27759
28001
  if (process.platform === "darwin") {
27760
- const plistPath = join60(homedir22(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
28002
+ const plistPath = join61(homedir22(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
27761
28003
  try {
27762
28004
  execSync5(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
27763
28005
  } catch {
27764
28006
  }
27765
28007
  let removed = false;
27766
28008
  if (existsSync56(plistPath)) {
27767
- rmSync9(plistPath, { force: true });
28009
+ rmSync10(plistPath, { force: true });
27768
28010
  removed = true;
27769
28011
  }
27770
28012
  return removed;
@@ -27808,7 +28050,7 @@ __export(device_auth_exports, {
27808
28050
  betterAuthDeviceLogin: () => betterAuthDeviceLogin,
27809
28051
  deviceLogin: () => deviceLogin
27810
28052
  });
27811
- import { createHash as createHash15, randomBytes } from "node:crypto";
28053
+ import { createHash as createHash15, randomBytes as randomBytes2 } from "node:crypto";
27812
28054
  async function readDeviceCode(res, where) {
27813
28055
  const text = await res.text().catch(() => "");
27814
28056
  let parsed;
@@ -27836,7 +28078,7 @@ async function deviceLogin(opts, onPrompt) {
27836
28078
  }
27837
28079
  const clientId = opts.clientId || DEFAULT_CLIENT_ID;
27838
28080
  const scope = opts.scope || "openid email profile";
27839
- const codeVerifier = b64url(randomBytes(32));
28081
+ const codeVerifier = b64url(randomBytes2(32));
27840
28082
  const codeChallenge = b64url(createHash15("sha256").update(codeVerifier).digest());
27841
28083
  const startRes = await fetch(`${issuer}/protocol/openid-connect/auth/device`, {
27842
28084
  method: "POST",
@@ -28286,9 +28528,9 @@ init_project_resolver();
28286
28528
  import { config } from "dotenv";
28287
28529
  import { Command } from "commander";
28288
28530
  import chalk2 from "chalk";
28289
- import { existsSync as existsSync57, readFileSync as readFileSync49, writeFileSync as writeFileSync23, realpathSync as realpathSync5 } from "fs";
28531
+ import { existsSync as existsSync57, readFileSync as readFileSync50, writeFileSync as writeFileSync23, realpathSync as realpathSync5 } from "fs";
28290
28532
  import { homedir as homedir23 } from "os";
28291
- import { join as join61, resolve as resolve2, dirname as dirname29, basename as basename27 } from "path";
28533
+ import { join as join62, resolve as resolve2, dirname as dirname29, basename as basename27 } from "path";
28292
28534
 
28293
28535
  // ../engine/dist/core/score-tier.js
28294
28536
  var STRONG_RATIO = 0.85;
@@ -28396,10 +28638,10 @@ function renderEnvNeeds(needs, installedCount) {
28396
28638
  // src/star-nudge.ts
28397
28639
  init_paths();
28398
28640
  import { existsSync as existsSync43, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "node:fs";
28399
- import { dirname as dirname20, join as join45 } from "node:path";
28641
+ import { dirname as dirname20, join as join46 } from "node:path";
28400
28642
  var REPO = "https://github.com/munhq/chat-recall";
28401
28643
  function markerPath() {
28402
- return join45(getDataDir(), ".star-asked");
28644
+ return join46(getDataDir(), ".star-asked");
28403
28645
  }
28404
28646
  function suppressed() {
28405
28647
  const env = process.env;
@@ -28452,8 +28694,8 @@ function claudeHookConfigFiles() {
28452
28694
  const out = [];
28453
28695
  const seen = /* @__PURE__ */ new Set();
28454
28696
  for (const home of claudeHomeDirs()) {
28455
- if (!existsSync57(join61(home, "projects"))) continue;
28456
- const file = join61(home, "hooks.json");
28697
+ if (!existsSync57(join62(home, "projects"))) continue;
28698
+ const file = join62(home, "hooks.json");
28457
28699
  let key2;
28458
28700
  try {
28459
28701
  key2 = realpathSync5(file);
@@ -28548,13 +28790,13 @@ async function serverDelete(path, body2) {
28548
28790
  }
28549
28791
  var pkgEngines = (() => {
28550
28792
  try {
28551
- return JSON.parse(readFileSync49(new URL("../package.json", import.meta.url), "utf-8")).engines?.node ?? "";
28793
+ return JSON.parse(readFileSync50(new URL("../package.json", import.meta.url), "utf-8")).engines?.node ?? "";
28552
28794
  } catch {
28553
28795
  return "";
28554
28796
  }
28555
28797
  })();
28556
28798
  var pkgVersion = JSON.parse(
28557
- readFileSync49(new URL("../package.json", import.meta.url), "utf-8")
28799
+ readFileSync50(new URL("../package.json", import.meta.url), "utf-8")
28558
28800
  ).version;
28559
28801
  var DEFAULT_SERVER = (process.env.CHAT_RECALL_DEFAULT_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
28560
28802
  var DEFAULT_ALLOW = [
@@ -28728,10 +28970,10 @@ program.command("init").description("Set up chat-recall: connect to your server,
28728
28970
  } else if (target) {
28729
28971
  }
28730
28972
  if (!options.skipMcp) {
28731
- const projectRoot = join61(import.meta.dirname, "..");
28973
+ const projectRoot = join62(import.meta.dirname, "..");
28732
28974
  let mcpBinOnPath = false;
28733
28975
  mcpBinOnPath = isOnPath("chat-recall-mcp");
28734
- const launch = mcpBinOnPath ? { command: "chat-recall-mcp" } : { command: "node", args: [join61(projectRoot, "dist", "mcp-relay.js")] };
28976
+ const launch = mcpBinOnPath ? { command: "chat-recall-mcp" } : { command: "node", args: [join62(projectRoot, "dist", "mcp-relay.js")] };
28735
28977
  const MCP_ENV = { NODE_OPTIONS: "--max-old-space-size=1024" };
28736
28978
  const { registerMcpEverywhere: registerMcpEverywhere2 } = await Promise.resolve().then(() => (init_mcp_clients(), mcp_clients_exports));
28737
28979
  const MCP_CLIENT_FOR_BIN = {
@@ -28771,7 +29013,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
28771
29013
  const detected = checkCodeindexStatus2();
28772
29014
  if (detected.installed && !forceInstall) {
28773
29015
  if (!options.skipMcp) {
28774
- const mcpJsonPath = join61(homedir23(), ".mcp.json");
29016
+ const mcpJsonPath = join62(homedir23(), ".mcp.json");
28775
29017
  const reg = registerCodeindexMcp2(mcpJsonPath, detected.path);
28776
29018
  }
28777
29019
  } else if (forceInstall) {
@@ -28780,7 +29022,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
28780
29022
  if (result.installed) {
28781
29023
  const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
28782
29024
  if (!options.skipMcp) {
28783
- const mcpJsonPath = join61(homedir23(), ".mcp.json");
29025
+ const mcpJsonPath = join62(homedir23(), ".mcp.json");
28784
29026
  const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
28785
29027
  }
28786
29028
  } else if (!result.prebuiltAvailable) {
@@ -29238,7 +29480,7 @@ memory.command("wake-up").description("Generate wake-up context (high-importance
29238
29480
  const identityFile = getIdentityFilePath();
29239
29481
  let identity = "AI coding assistant";
29240
29482
  if (existsSync57(identityFile)) {
29241
- identity = readFileSync49(identityFile, "utf-8").trim();
29483
+ identity = readFileSync50(identityFile, "utf-8").trim();
29242
29484
  }
29243
29485
  const lines = [
29244
29486
  chalk2.bold("# Wake-Up Context"),
@@ -29338,7 +29580,7 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
29338
29580
  }
29339
29581
  });
29340
29582
  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) => {
29341
- const { existsSync: existsSync58, readFileSync: readFileSync50, statSync: statSync46 } = await import("fs");
29583
+ const { existsSync: existsSync58, readFileSync: readFileSync51, statSync: statSync47 } = await import("fs");
29342
29584
  const { execSync: execSync6 } = await import("child_process");
29343
29585
  const rows = [];
29344
29586
  const note = (ok, label, detail) => rows.push({ ok, label, detail });
@@ -29401,12 +29643,12 @@ program.command("doctor").description("Quick health check across login, server,
29401
29643
  }
29402
29644
  }
29403
29645
  }
29404
- const credFile = join61(getDataDir(), "credentials.json");
29646
+ const credFile = join62(getDataDir(), "credentials.json");
29405
29647
  if (!existsSync58(credFile)) {
29406
29648
  note(false, "Credentials file", `missing \u2014 run \`chat-recall login <server-url>\``);
29407
29649
  } else {
29408
29650
  try {
29409
- const mode = statSync46(credFile).mode & 511;
29651
+ const mode = statSync47(credFile).mode & 511;
29410
29652
  const isWin = process.platform === "win32";
29411
29653
  const safe = isWin || (mode & 63) === 0;
29412
29654
  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"})`);
@@ -29425,13 +29667,13 @@ program.command("doctor").description("Quick health check across login, server,
29425
29667
  const readHooks = (file) => {
29426
29668
  if (!existsSync58(file)) return {};
29427
29669
  try {
29428
- return JSON.parse(readFileSync50(file, "utf-8")).hooks || {};
29670
+ return JSON.parse(readFileSync51(file, "utf-8")).hooks || {};
29429
29671
  } catch {
29430
29672
  return {};
29431
29673
  }
29432
29674
  };
29433
29675
  const cfg = readHooks(hooksJson);
29434
- const settings2 = readHooks(join61(dirname29(hooksJson), "settings.json"));
29676
+ const settings2 = readHooks(join62(dirname29(hooksJson), "settings.json"));
29435
29677
  const all = ["SessionStart", "UserPromptSubmit", "Stop", "PreCompact", "SessionEnd"];
29436
29678
  const hits = [];
29437
29679
  for (const ev of all) {
@@ -29478,7 +29720,7 @@ program.command("doctor").description("Quick health check across login, server,
29478
29720
  }
29479
29721
  let registered = false;
29480
29722
  try {
29481
- const cfg = JSON.parse(readFileSync50(target2.configPath, "utf-8"));
29723
+ const cfg = JSON.parse(readFileSync51(target2.configPath, "utf-8"));
29482
29724
  registered = JSON.stringify(cfg).includes("chat_recall_guard");
29483
29725
  } catch {
29484
29726
  }
@@ -29493,7 +29735,7 @@ program.command("doctor").description("Quick health check across login, server,
29493
29735
  const want = bundledSkillNames2().length;
29494
29736
  for (const t of skillTargets2()) {
29495
29737
  if (!t.available) continue;
29496
- const have = want === 0 ? 0 : bundledSkillNames2().filter((n) => existsSync58(join61(t.dir, n, ".chat-recall-managed"))).length;
29738
+ const have = want === 0 ? 0 : bundledSkillNames2().filter((n) => existsSync58(join62(t.dir, n, ".chat-recall-managed"))).length;
29497
29739
  note(
29498
29740
  have === want,
29499
29741
  `Skills \u2014 ${t.label}`,
@@ -29568,12 +29810,12 @@ program.command("doctor").description("Quick health check across login, server,
29568
29810
  note(watchRunning, "Watch daemon", watchRunning ? "running" : "not running (run `chat-recall watch --install-service` to keep collection running)");
29569
29811
  const dataDir = getDataDir();
29570
29812
  const legacyPaths = [
29571
- join61(dataDir, "cache.db"),
29572
- join61(dataDir, "cache.db-wal"),
29573
- join61(dataDir, "cache.db-shm"),
29574
- join61(dataDir, "index", "lancedb"),
29575
- join61(dataDir, "index", "knowledge_graph.db"),
29576
- join61(dataDir, "index", "metadata.db")
29813
+ join62(dataDir, "cache.db"),
29814
+ join62(dataDir, "cache.db-wal"),
29815
+ join62(dataDir, "cache.db-shm"),
29816
+ join62(dataDir, "index", "lancedb"),
29817
+ join62(dataDir, "index", "knowledge_graph.db"),
29818
+ join62(dataDir, "index", "metadata.db")
29577
29819
  ];
29578
29820
  const legacyPresent = legacyPaths.filter((p) => existsSync58(p));
29579
29821
  if (opts.purgeLocal) {
@@ -29582,11 +29824,11 @@ program.command("doctor").description("Quick health check across login, server,
29582
29824
  } else if (legacyPresent.length === 0) {
29583
29825
  note(true, "Purge legacy index", "nothing to remove");
29584
29826
  } else {
29585
- const { rmSync: rmSync10 } = await import("fs");
29827
+ const { rmSync: rmSync11 } = await import("fs");
29586
29828
  let removed = 0;
29587
29829
  for (const p of legacyPresent) {
29588
29830
  try {
29589
- rmSync10(p, { recursive: true, force: true });
29831
+ rmSync11(p, { recursive: true, force: true });
29590
29832
  removed++;
29591
29833
  } catch {
29592
29834
  }
@@ -29646,7 +29888,7 @@ companions.command("install").description("Install codeindex (downloads from mun
29646
29888
  if (result.installed) {
29647
29889
  const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
29648
29890
  console.log(chalk2.green(`\u2713 codeindex installed (${sizeMb}) \u2192 ${result.path}`));
29649
- const mcpJsonPath = join61(homedir23(), ".mcp.json");
29891
+ const mcpJsonPath = join62(homedir23(), ".mcp.json");
29650
29892
  const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
29651
29893
  if (reg.added) console.log(chalk2.dim(` Registered as MCP server in ${mcpJsonPath}`));
29652
29894
  else console.log(chalk2.dim(` Already registered as MCP server`));
@@ -29668,7 +29910,7 @@ companions.command("uninstall").description("Remove the codeindex binary chat-re
29668
29910
  const r = uninstallCodeindex2();
29669
29911
  if (r.removed) console.log(chalk2.green(`\u2713 Removed ${r.path}`));
29670
29912
  else console.log(chalk2.dim(` Nothing to remove at ${r.path}`));
29671
- const mcpJsonPath = join61(homedir23(), ".mcp.json");
29913
+ const mcpJsonPath = join62(homedir23(), ".mcp.json");
29672
29914
  const u = unregisterCodeindexMcp2(mcpJsonPath);
29673
29915
  if (u.removed) console.log(chalk2.dim(` Unregistered MCP server from ${mcpJsonPath}`));
29674
29916
  });
@@ -30220,10 +30462,10 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
30220
30462
  try {
30221
30463
  const { resolveProjectId: resolveProjectId2 } = await Promise.resolve().then(() => (init_project_resolver(), project_resolver_exports));
30222
30464
  const { renderTeamTasksMd: renderTeamTasksMd2, parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
30223
- const { writeFileSync: writeFileSync24, readFileSync: readFileSync50, existsSync: existsSync58 } = await import("node:fs");
30224
- const { join: join62 } = await import("node:path");
30225
- const file = join62(cwd, "TEAM_TASKS.md");
30226
- if (!opts.force && existsSync58(file) && parseTeamTasksFile2(readFileSync50(file, "utf8")).length > 0) {
30465
+ const { writeFileSync: writeFileSync24, readFileSync: readFileSync51, existsSync: existsSync58 } = await import("node:fs");
30466
+ const { join: join63 } = await import("node:path");
30467
+ const file = join63(cwd, "TEAM_TASKS.md");
30468
+ if (!opts.force && existsSync58(file) && parseTeamTasksFile2(readFileSync51(file, "utf8")).length > 0) {
30227
30469
  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.");
30228
30470
  process.exit(1);
30229
30471
  }
@@ -30242,14 +30484,14 @@ tasks.command("push [dir]").description("Push TEAM_TASKS.md status edits back to
30242
30484
  const cwd = dir || process.cwd();
30243
30485
  try {
30244
30486
  const { parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
30245
- const { readFileSync: readFileSync50, existsSync: existsSync58 } = await import("node:fs");
30246
- const { join: join62 } = await import("node:path");
30247
- const file = join62(cwd, "TEAM_TASKS.md");
30487
+ const { readFileSync: readFileSync51, existsSync: existsSync58 } = await import("node:fs");
30488
+ const { join: join63 } = await import("node:path");
30489
+ const file = join63(cwd, "TEAM_TASKS.md");
30248
30490
  if (!existsSync58(file)) {
30249
30491
  console.error(chalk2.red("no TEAM_TASKS.md here \u2014"), "run", chalk2.bold("chat-recall tasks pull"), "first");
30250
30492
  process.exit(1);
30251
30493
  }
30252
- const edits = parseTeamTasksFile2(readFileSync50(file, "utf8"));
30494
+ const edits = parseTeamTasksFile2(readFileSync51(file, "utf8"));
30253
30495
  let n = 0;
30254
30496
  for (const e of edits) {
30255
30497
  await serverPatch(`/api/tasks/${encodeURIComponent(e.id)}`, { status: e.intent });
@@ -30355,8 +30597,8 @@ team.command("publish <type> <name> <file>").description("Publish a local file a
30355
30597
  console.error(chalk2.red(`Invalid tool: ${opts.tool}. Must be one of: ${validTools.join(", ")}`));
30356
30598
  process.exit(1);
30357
30599
  }
30358
- const { readFileSync: readFileSync50 } = await import("fs");
30359
- const body2 = readFileSync50(file);
30600
+ const { readFileSync: readFileSync51 } = await import("fs");
30601
+ const body2 = readFileSync51(file);
30360
30602
  const { teamPublish: teamPublish2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
30361
30603
  const a = await teamPublish2({
30362
30604
  type,
@@ -30732,18 +30974,18 @@ program.command("task-hook").description("Move a board card when a prompt or a c
30732
30974
  }
30733
30975
  });
30734
30976
  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").option("--no-guard", "Don't install the PreToolUse decision guard").option("--no-task-board", "Don't install the hooks that claim and close board cards").action(async (opts) => {
30735
- const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync58, readFileSync: readFileSync50, writeFileSync: writeFileSync24, statSync: statSync46, unlinkSync: unlinkSync4 } = await import("fs");
30977
+ const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync58, readFileSync: readFileSync51, writeFileSync: writeFileSync24, statSync: statSync47, unlinkSync: unlinkSync4 } = await import("fs");
30736
30978
  const { fileURLToPath: fileURLToPath6 } = await import("url");
30737
30979
  const { boardHookEntries: boardHookEntries2, isBoardHookEntry: isBoardHookEntry2, registerBoardHooks: registerBoardHooks2, resolveCliEntry: resolveCliEntry2 } = await Promise.resolve().then(() => (init_board_hooks(), board_hooks_exports));
30738
30980
  const here = fileURLToPath6(new URL(".", import.meta.url));
30739
30981
  const findHook = (name) => {
30740
30982
  const candidates2 = [
30741
- join61(here, "..", "hooks", name),
30742
- join61(here, "..", "..", "hooks", name),
30983
+ join62(here, "..", "hooks", name),
30984
+ join62(here, "..", "..", "hooks", name),
30743
30985
  // The monorepo root, for a run straight from the checkout. The copy
30744
30986
  // under packages/cli/hooks is written by the package build, so a
30745
30987
  // freshly added script is only at the root until that build runs.
30746
- join61(here, "..", "..", "..", "hooks", name)
30988
+ join62(here, "..", "..", "..", "hooks", name)
30747
30989
  ];
30748
30990
  return candidates2.find((p) => existsSync58(p));
30749
30991
  };
@@ -30757,16 +30999,16 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30757
30999
  process.exit(1);
30758
31000
  }
30759
31001
  const hooksDir = getHooksDir();
30760
- const installedSaveHook = join61(hooksDir, "chat_recall_save_hook.sh");
30761
- const installedGuardHook = join61(hooksDir, "chat_recall_guard_hook.sh");
30762
- const installedResumeHook = join61(hooksDir, "chat_recall_resume_hook.sh");
30763
- const installedEscalateHook = join61(hooksDir, "chat_recall_escalate_hook.sh");
30764
- const installedWakeupHook = join61(hooksDir, "chat_recall_wakeup_hook.sh");
31002
+ const installedSaveHook = join62(hooksDir, "chat_recall_save_hook.sh");
31003
+ const installedGuardHook = join62(hooksDir, "chat_recall_guard_hook.sh");
31004
+ const installedResumeHook = join62(hooksDir, "chat_recall_resume_hook.sh");
31005
+ const installedEscalateHook = join62(hooksDir, "chat_recall_escalate_hook.sh");
31006
+ const installedWakeupHook = join62(hooksDir, "chat_recall_wakeup_hook.sh");
30765
31007
  const wakeupWiredInSettings = (home) => {
30766
- const settings2 = join61(home, "settings.json");
31008
+ const settings2 = join62(home, "settings.json");
30767
31009
  if (!existsSync58(settings2)) return false;
30768
31010
  try {
30769
- const cfg = JSON.parse(readFileSync50(settings2, "utf-8"));
31011
+ const cfg = JSON.parse(readFileSync51(settings2, "utf-8"));
30770
31012
  const arr = Array.isArray(cfg.hooks?.SessionStart) ? cfg.hooks.SessionStart : [];
30771
31013
  return arr.some((h) => (h?.hooks?.[0]?.command || "").includes("chat_recall_wakeup_hook"));
30772
31014
  } catch {
@@ -30777,7 +31019,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30777
31019
  const readHookConfig = (file) => {
30778
31020
  if (!existsSync58(file)) return { hooks: {} };
30779
31021
  try {
30780
- const c = JSON.parse(readFileSync50(file, "utf-8"));
31022
+ const c = JSON.parse(readFileSync51(file, "utf-8"));
30781
31023
  if (!c.hooks || typeof c.hooks !== "object") c.hooks = {};
30782
31024
  return c;
30783
31025
  } catch (err) {
@@ -30794,7 +31036,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30794
31036
  const readJson4 = (file) => {
30795
31037
  if (!existsSync58(file)) return {};
30796
31038
  try {
30797
- return JSON.parse(readFileSync50(file, "utf-8"));
31039
+ return JSON.parse(readFileSync51(file, "utf-8"));
30798
31040
  } catch {
30799
31041
  return {};
30800
31042
  }
@@ -30804,7 +31046,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30804
31046
  const notes = [];
30805
31047
  for (const target of reg.guardTargets()) {
30806
31048
  if (!existsSync58(target.homeDir)) continue;
30807
- const wrapper = join61(hooksDir, reg.guardWrapperName(target.tool));
31049
+ const wrapper = join62(hooksDir, reg.guardWrapperName(target.tool));
30808
31050
  writeFileSync24(wrapper, reg.guardWrapperSource(target.tool, hookPath));
30809
31051
  chmodSync5(wrapper, 493);
30810
31052
  mkdirSync23(dirname29(target.configPath), { recursive: true });
@@ -30826,7 +31068,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30826
31068
  for (const target of reg.guardTargets()) {
30827
31069
  if (!existsSync58(target.configPath)) continue;
30828
31070
  if (target.tool === "opencode") {
30829
- const body2 = readFileSync50(target.configPath, "utf-8");
31071
+ const body2 = readFileSync51(target.configPath, "utf-8");
30830
31072
  if (!body2.includes("chat-recall install-hooks")) continue;
30831
31073
  unlinkSync4(target.configPath);
30832
31074
  removed += 1;
@@ -30934,7 +31176,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30934
31176
  const config2 = readHookConfig(hooksJson);
30935
31177
  const home = dirname29(hooksJson);
30936
31178
  const alreadyWired = wakeupWiredInSettings(home);
30937
- if (alreadyWired) wakeupSkipped.push(join61(home, "settings.json"));
31179
+ if (alreadyWired) wakeupSkipped.push(join62(home, "settings.json"));
30938
31180
  const perProfile = installWakeup && !alreadyWired ? [...events, ["SessionStart", wakeupEntry]] : events;
30939
31181
  for (const event of HOOK_EVENTS) {
30940
31182
  const arr = Array.isArray(config2.hooks[event]) ? config2.hooks[event] : [];
@@ -30947,18 +31189,18 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30947
31189
  writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
30948
31190
  }
30949
31191
  if (installGuard) await addGuardToOtherTools(installedGuardHook);
30950
- const saveSz = statSync46(installedSaveHook).size;
31192
+ const saveSz = statSync47(installedSaveHook).size;
30951
31193
  console.log(chalk2.green(`\u2713 Installed chat-recall save hook (${saveSz} bytes)`));
30952
31194
  if (installResume) {
30953
- const resumeSz = statSync46(installedResumeHook).size;
31195
+ const resumeSz = statSync47(installedResumeHook).size;
30954
31196
  console.log(chalk2.green(`\u2713 Installed chat-recall resume-hint hook (${resumeSz} bytes)`));
30955
31197
  }
30956
31198
  if (installEscalate) {
30957
- const escalateSz = statSync46(installedEscalateHook).size;
31199
+ const escalateSz = statSync47(installedEscalateHook).size;
30958
31200
  console.log(chalk2.green(`\u2713 Installed chat-recall learnings-escalation hook (${escalateSz} bytes)`));
30959
31201
  }
30960
31202
  if (installGuard) {
30961
- const guardSz = statSync46(installedGuardHook).size;
31203
+ const guardSz = statSync47(installedGuardHook).size;
30962
31204
  console.log(chalk2.green(`\u2713 Installed chat-recall decision guard (${guardSz} bytes)`));
30963
31205
  }
30964
31206
  if (installTaskBoard) {
@@ -30966,7 +31208,7 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
30966
31208
  console.log(chalk2.dim(" A prompt naming a card id claims it; a commit message naming one closes it."));
30967
31209
  }
30968
31210
  if (installWakeup) {
30969
- const wakeupSz = statSync46(installedWakeupHook).size;
31211
+ const wakeupSz = statSync47(installedWakeupHook).size;
30970
31212
  console.log(chalk2.green(`\u2713 Installed chat-recall wake-up hook (${wakeupSz} bytes)`));
30971
31213
  for (const f of wakeupSkipped) {
30972
31214
  console.log(chalk2.dim(` (SessionStart already wired in ${f} \u2014 left as is, not duplicated)`));