chat-recall 0.6.5 → 0.6.7

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/watch.js CHANGED
@@ -18305,7 +18305,7 @@ var init_http = __esm({
18305
18305
  CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
18306
18306
  } catch {
18307
18307
  try {
18308
- if (true) CLI_VERSION = "0.6.5";
18308
+ if (true) CLI_VERSION = "0.6.7";
18309
18309
  } catch {
18310
18310
  }
18311
18311
  }
@@ -18821,7 +18821,7 @@ var init_telemetry = __esm({
18821
18821
  init_sync_client();
18822
18822
  cliVersion = () => {
18823
18823
  try {
18824
- return true ? "0.6.5" : "dev";
18824
+ return true ? "0.6.7" : "dev";
18825
18825
  } catch {
18826
18826
  return "dev";
18827
18827
  }
@@ -19090,12 +19090,16 @@ var init_client_events = __esm({
19090
19090
  // src/auto-update.ts
19091
19091
  var auto_update_exports = {};
19092
19092
  __export(auto_update_exports, {
19093
+ clearUpdateFailures: () => clearUpdateFailures,
19093
19094
  compareVersions: () => compareVersions,
19094
19095
  executeAutoUpdate: () => executeAutoUpdate,
19096
+ hasGivenUpOn: () => hasGivenUpOn,
19095
19097
  installedVersion: () => installedVersion,
19096
19098
  isAutoUpdateEnabled: () => isAutoUpdateEnabled,
19097
19099
  newerOf: () => newerOf,
19100
+ noteUpdateFailure: () => noteUpdateFailure,
19098
19101
  planAutoUpdate: () => planAutoUpdate,
19102
+ readUpdateState: () => readUpdateState,
19099
19103
  runAutoUpdate: () => runAutoUpdate,
19100
19104
  runningPrefix: () => runningPrefix,
19101
19105
  sweepStaleStaging: () => sweepStaleStaging,
@@ -19104,9 +19108,9 @@ __export(auto_update_exports, {
19104
19108
  import { execSync as execSync2 } from "node:child_process";
19105
19109
  import { readFileSync as readFileSync34 } from "node:fs";
19106
19110
  import { fileURLToPath as fileURLToPath2 } from "node:url";
19107
- import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync36 } from "node:fs";
19111
+ import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync36, mkdirSync as mkdirSync9 } from "node:fs";
19108
19112
  import { join as join41, dirname as dirname18, sep as sep2 } from "node:path";
19109
- import { tmpdir as tmpdir2 } from "node:os";
19113
+ import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
19110
19114
  import { createHash as createHash7 } from "node:crypto";
19111
19115
  function compareVersions(a, b) {
19112
19116
  const pa = String(a).split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
@@ -19220,16 +19224,25 @@ function runningPrefix(moduleUrl = import.meta.url) {
19220
19224
  const beforeNodeModules = here.slice(0, at);
19221
19225
  return beforeNodeModules.endsWith(`${sep2}lib`) ? beforeNodeModules.slice(0, -4) : beforeNodeModules;
19222
19226
  }
19227
+ function npmInstall(cmd) {
19228
+ try {
19229
+ execSync2(cmd, { stdio: ["ignore", "ignore", "pipe"] });
19230
+ } catch (e) {
19231
+ const err = e;
19232
+ const detail = String(err.stderr ?? "").trim().split("\n").filter((l) => l.trim() && !/^npm (notice|warn)\b/i.test(l)).slice(-4).join(" | ").slice(0, 400);
19233
+ throw new Error(detail ? `${err.message ?? "install failed"} \u2014 ${detail}` : err.message ?? "install failed");
19234
+ }
19235
+ }
19223
19236
  function realInstall(tgz) {
19224
19237
  const prefix = runningPrefix();
19225
19238
  if (prefix) {
19226
- execSync2(`npm install -g --prefix "${prefix}" "${tgz}"`, { stdio: "ignore" });
19239
+ npmInstall(`npm install -g --prefix "${prefix}" "${tgz}"`);
19227
19240
  return;
19228
19241
  }
19229
19242
  try {
19230
- execSync2(`npm install -g "${tgz}"`, { stdio: "ignore" });
19243
+ npmInstall(`npm install -g "${tgz}"`);
19231
19244
  } catch {
19232
- execSync2(`npm install -g --prefix "${process.env.HOME}/.local" "${tgz}"`, { stdio: "ignore" });
19245
+ npmInstall(`npm install -g --prefix "${process.env.HOME}/.local" "${tgz}"`);
19233
19246
  }
19234
19247
  }
19235
19248
  function realRestart(platform2) {
@@ -19237,6 +19250,37 @@ function realRestart(platform2) {
19237
19250
  else if (platform2 === "darwin") execSync2("launchctl kickstart -k gui/$(id -u)/com.chat-recall.watch", { stdio: "ignore", shell: "/bin/bash" });
19238
19251
  else if (platform2 === "win32") execSync2("schtasks /End /TN chat-recall-watch & schtasks /Run /TN chat-recall-watch", { stdio: "ignore" });
19239
19252
  }
19253
+ function updateStatePath() {
19254
+ const dir = process.env.CHAT_RECALL_DATA_DIR || join41(homedir15(), ".chat-recall");
19255
+ return join41(dir, "auto-update-state.json");
19256
+ }
19257
+ function readUpdateState() {
19258
+ try {
19259
+ return JSON.parse(readFileSync34(updateStatePath(), "utf8"));
19260
+ } catch {
19261
+ return {};
19262
+ }
19263
+ }
19264
+ function writeUpdateState(st) {
19265
+ try {
19266
+ const p = updateStatePath();
19267
+ mkdirSync9(dirname18(p), { recursive: true });
19268
+ writeFileSync8(p, JSON.stringify(st), "utf8");
19269
+ } catch {
19270
+ }
19271
+ }
19272
+ function hasGivenUpOn(target, st = readUpdateState()) {
19273
+ return st.target === target && (st.failures ?? 0) >= GIVE_UP_AFTER;
19274
+ }
19275
+ function noteUpdateFailure(target) {
19276
+ const st = readUpdateState();
19277
+ const failures = st.target === target ? (st.failures ?? 0) + 1 : 1;
19278
+ writeUpdateState({ target, failures });
19279
+ return failures;
19280
+ }
19281
+ function clearUpdateFailures() {
19282
+ writeUpdateState({});
19283
+ }
19240
19284
  async function runAutoUpdate(base2, authHeaders, ownVersion3, deps) {
19241
19285
  let caps;
19242
19286
  try {
@@ -19259,6 +19303,10 @@ async function runAutoUpdate(base2, authHeaders, ownVersion3, deps) {
19259
19303
  }
19260
19304
  return { updated: false, reason: plan.reason };
19261
19305
  }
19306
+ const target = plan.to ?? "";
19307
+ if (target && hasGivenUpOn(target)) {
19308
+ return { updated: false, reason: `gave up installing ${plan.to} after ${GIVE_UP_AFTER} failed attempts \u2014 run \`npm install -g chat-recall\` by hand` };
19309
+ }
19262
19310
  const result = await executeAutoUpdate(plan, {
19263
19311
  download: deps?.download ?? realDownload,
19264
19312
  install: deps?.install ?? realInstall,
@@ -19266,27 +19314,34 @@ async function runAutoUpdate(base2, authHeaders, ownVersion3, deps) {
19266
19314
  platform: deps?.platform
19267
19315
  });
19268
19316
  if (!result.updated) {
19269
- try {
19270
- const { reportClientEvent: reportClientEvent2 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
19271
- reportClientEvent2("auto_update_failed", { message: `${plan.from} \u2192 ${plan.to}: ${result.reason}` });
19272
- } catch {
19317
+ const failures = target ? noteUpdateFailure(target) : 1;
19318
+ if (failures <= GIVE_UP_AFTER) {
19319
+ try {
19320
+ const { reportClientEvent: reportClientEvent2 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
19321
+ const suffix = failures >= GIVE_UP_AFTER ? ` \u2014 giving up after ${failures} attempts, needs a manual \`npm install -g chat-recall\`` : ` (attempt ${failures} of ${GIVE_UP_AFTER})`;
19322
+ reportClientEvent2("auto_update_failed", { message: `${plan.from} \u2192 ${plan.to}: ${result.reason}${suffix}` });
19323
+ } catch {
19324
+ }
19273
19325
  }
19326
+ } else {
19327
+ clearUpdateFailures();
19274
19328
  }
19275
19329
  return result;
19276
19330
  }
19277
- var tarballUrl, reportedStandingSkip;
19331
+ var tarballUrl, GIVE_UP_AFTER, reportedStandingSkip;
19278
19332
  var init_auto_update = __esm({
19279
19333
  "src/auto-update.ts"() {
19280
19334
  "use strict";
19281
19335
  init_http();
19282
19336
  tarballUrl = (base2) => `${base2.replace(/\/+$/, "")}/install/chat-recall.tgz`;
19337
+ GIVE_UP_AFTER = 3;
19283
19338
  reportedStandingSkip = /* @__PURE__ */ new Set();
19284
19339
  }
19285
19340
  });
19286
19341
 
19287
19342
  // src/sync-client.ts
19288
19343
  import { join as join42, dirname as dirname19, basename as basename20 } from "node:path";
19289
- import { readFileSync as readFileSync35, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync32, statSync as statSync37 } from "node:fs";
19344
+ import { readFileSync as readFileSync35, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync4, rmSync as rmSync6, chmodSync as chmodSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync32, statSync as statSync37 } from "node:fs";
19290
19345
  import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
19291
19346
  import { createHash as createHash8 } from "node:crypto";
19292
19347
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -19395,7 +19450,7 @@ function persistSyncConfig(base2, config) {
19395
19450
  try {
19396
19451
  const all = readPersistedSyncConfigs();
19397
19452
  all[base2] = { fetchedAt: Date.now(), config };
19398
- mkdirSync9(dirname19(syncConfigCachePath()), { recursive: true });
19453
+ mkdirSync10(dirname19(syncConfigCachePath()), { recursive: true });
19399
19454
  writeFileSync9(syncConfigCachePath(), JSON.stringify(all, null, 2));
19400
19455
  } catch {
19401
19456
  }
@@ -20764,7 +20819,7 @@ var init_sync_client = __esm({
20764
20819
  ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
20765
20820
  } catch {
20766
20821
  try {
20767
- if (true) ownVersion = "0.6.5";
20822
+ if (true) ownVersion = "0.6.7";
20768
20823
  } catch {
20769
20824
  }
20770
20825
  }
@@ -20798,7 +20853,7 @@ var init_sync_client = __esm({
20798
20853
  // ../engine/dist/core/artifact-codec.js
20799
20854
  import { readFileSync as readFileSync36 } from "fs";
20800
20855
  import { basename as basename21, join as join43 } from "path";
20801
- import { homedir as homedir15 } from "os";
20856
+ import { homedir as homedir16 } from "os";
20802
20857
  function parseCommandText(content, format, fallbackName) {
20803
20858
  if (format === "toml") {
20804
20859
  const t = parseScalarToml(content);
@@ -20888,7 +20943,7 @@ function instructionsPath(tool, projectPath) {
20888
20943
  case "codex":
20889
20944
  return join43(codexBackend.homeDir(), file);
20890
20945
  case "opencode":
20891
- return join43(homedir15(), ".config", "opencode", file);
20946
+ return join43(homedir16(), ".config", "opencode", file);
20892
20947
  // Cursor has no single user-scope instructions FILE — user-level guidance
20893
20948
  // is User Rules plus ~/.cursor/rules/*.mdc. AGENTS.md at the home root is
20894
20949
  // the closest equivalent and is what a mirror can actually write.
@@ -20939,9 +20994,9 @@ var init_artifact_codec = __esm({
20939
20994
  });
20940
20995
 
20941
20996
  // ../engine/dist/core/toolkit-sync.js
20942
- import { existsSync as existsSync41, mkdirSync as mkdirSync10, readFileSync as readFileSync37, writeFileSync as writeFileSync10, cpSync } from "fs";
20997
+ import { existsSync as existsSync41, mkdirSync as mkdirSync11, readFileSync as readFileSync37, writeFileSync as writeFileSync10, cpSync } from "fs";
20943
20998
  import { dirname as dirname20, join as join44, basename as basename22 } from "path";
20944
- import { homedir as homedir16 } from "os";
20999
+ import { homedir as homedir17 } from "os";
20945
21000
  function rowFromMemoryItem(type, item) {
20946
21001
  const extra = item.extra || {};
20947
21002
  return {
@@ -20980,7 +21035,7 @@ function copySkill(row, toTool) {
20980
21035
  if (existsSync41(targetDir))
20981
21036
  return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
20982
21037
  try {
20983
- mkdirSync10(dirname20(targetDir), { recursive: true });
21038
+ mkdirSync11(dirname20(targetDir), { recursive: true });
20984
21039
  cpSync(skillDir, targetDir, { recursive: true });
20985
21040
  return { ok: true, targetPath: targetDir };
20986
21041
  } catch (e) {
@@ -20988,7 +21043,7 @@ function copySkill(row, toTool) {
20988
21043
  }
20989
21044
  }
20990
21045
  function readMcpEntry(tool, name) {
20991
- const home = homedir16();
21046
+ const home = homedir17();
20992
21047
  const tries = [];
20993
21048
  if (tool === "claude") {
20994
21049
  tries.push({ path: join44(home, ".mcp.json"), key: "mcpServers" });
@@ -21106,12 +21161,12 @@ function writeCodexMcpEntry(path, name, entry) {
21106
21161
  for (const [k, v] of Object.entries(entry.env))
21107
21162
  lines.push(`${k} = ${escape(String(v))}`);
21108
21163
  }
21109
- mkdirSync10(dirname20(path), { recursive: true });
21164
+ mkdirSync11(dirname20(path), { recursive: true });
21110
21165
  writeFileSync10(path, content + lines.join("\n") + "\n");
21111
21166
  return true;
21112
21167
  }
21113
21168
  function writeMcpEntry(toTool, name, entry) {
21114
- const home = homedir16();
21169
+ const home = homedir17();
21115
21170
  if (toTool === "codex") {
21116
21171
  const path2 = join44(home, ".codex", "config.toml");
21117
21172
  if (!writeCodexMcpEntry(path2, name, entry))
@@ -21141,7 +21196,7 @@ function writeMcpEntry(toTool, name, entry) {
21141
21196
  return { ok: false, status: 500, error: `Target config is malformed: ${path}` };
21142
21197
  }
21143
21198
  } else {
21144
- mkdirSync10(dirname20(path), { recursive: true });
21199
+ mkdirSync11(dirname20(path), { recursive: true });
21145
21200
  }
21146
21201
  cfg[key2] = cfg[key2] || {};
21147
21202
  if (cfg[key2][name])
@@ -21201,7 +21256,7 @@ function copyCommandOrAgent(type, row, toTool) {
21201
21256
  if (existsSync41(out.path))
21202
21257
  return { ok: false, status: 409, error: `Already exists: ${out.path}` };
21203
21258
  try {
21204
- mkdirSync10(dirname20(out.path), { recursive: true });
21259
+ mkdirSync11(dirname20(out.path), { recursive: true });
21205
21260
  writeFileSync10(out.path, out.content);
21206
21261
  return { ok: true, targetPath: out.path };
21207
21262
  } catch (e) {
@@ -21221,7 +21276,7 @@ function copyInstructions(row, toTool) {
21221
21276
  if (existsSync41(out.path))
21222
21277
  return { ok: false, status: 409, error: `Already exists: ${out.path}` };
21223
21278
  try {
21224
- mkdirSync10(dirname20(out.path), { recursive: true });
21279
+ mkdirSync11(dirname20(out.path), { recursive: true });
21225
21280
  writeFileSync10(out.path, out.content);
21226
21281
  return { ok: true, targetPath: out.path };
21227
21282
  } catch (e) {
@@ -21427,7 +21482,7 @@ __export(toolkit_pull_exports, {
21427
21482
  planPull: () => planPull,
21428
21483
  portableCommand: () => portableCommand
21429
21484
  });
21430
- import { existsSync as existsSync44, mkdirSync as mkdirSync12, statSync as statSync40, writeFileSync as writeFileSync12 } from "node:fs";
21485
+ import { existsSync as existsSync44, mkdirSync as mkdirSync13, statSync as statSync40, writeFileSync as writeFileSync12 } from "node:fs";
21431
21486
  import { dirname as dirname22, join as join47, basename as basename23, isAbsolute as isAbsolute3 } from "node:path";
21432
21487
  function isExecutableFile(p) {
21433
21488
  try {
@@ -21593,7 +21648,7 @@ function executePull(rows, opts = {}) {
21593
21648
  continue;
21594
21649
  }
21595
21650
  try {
21596
- mkdirSync12(dir, { recursive: true });
21651
+ mkdirSync13(dir, { recursive: true });
21597
21652
  writeFileSync12(file, body);
21598
21653
  outcomes.push({ type: "skill", name, tool, status: "written", path: file, redactedContent: redacted });
21599
21654
  } catch (e) {
@@ -21634,7 +21689,7 @@ function executePull(rows, opts = {}) {
21634
21689
  continue;
21635
21690
  }
21636
21691
  try {
21637
- mkdirSync12(dirname22(out.path), { recursive: true });
21692
+ mkdirSync13(dirname22(out.path), { recursive: true });
21638
21693
  writeFileSync12(out.path, out.content);
21639
21694
  outcomes.push({ type, name, tool, status: "written", path: out.path, redactedContent: redacted });
21640
21695
  } catch (e) {
@@ -22065,8 +22120,8 @@ var init_code_intel = __esm({
22065
22120
  });
22066
22121
 
22067
22122
  // ../engine/dist/core/companions.js
22068
- import { existsSync as existsSync47, mkdirSync as mkdirSync15, statSync as statSync43, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync42, writeFileSync as writeFileSync15 } from "node:fs";
22069
- import { homedir as homedir18 } from "node:os";
22123
+ import { existsSync as existsSync47, mkdirSync as mkdirSync16, statSync as statSync43, chmodSync as chmodSync4, unlinkSync as unlinkSync4, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync42, writeFileSync as writeFileSync15 } from "node:fs";
22124
+ import { homedir as homedir19 } from "node:os";
22070
22125
  import { join as join51 } from "node:path";
22071
22126
  import { execSync as execSync3 } from "node:child_process";
22072
22127
  import { createHash as createHash12 } from "node:crypto";
@@ -22169,7 +22224,7 @@ async function installCodeindex(opts = {}) {
22169
22224
  return status;
22170
22225
  if (!status.prebuiltAvailable)
22171
22226
  return status;
22172
- mkdirSync15(CODEINDEX_INSTALL_DIR, { recursive: true });
22227
+ mkdirSync16(CODEINDEX_INSTALL_DIR, { recursive: true });
22173
22228
  const tmpPath = `${CODEINDEX_BIN_PATH}.partial`;
22174
22229
  try {
22175
22230
  await downloadArtifact(status.artifactName, tmpPath);
@@ -22211,7 +22266,7 @@ var init_companions = __esm({
22211
22266
  "x86_64-windows": "a96fc8c8949bb399a44cc0615b12e458460a529c69f01e8829cc39a20f45ab29",
22212
22267
  "aarch64-windows": "42620d56272cd9ba9d785569d8d754d6b4a3e80f36f28db3e0c483fd600ed117"
22213
22268
  };
22214
- CODEINDEX_INSTALL_DIR = join51(homedir18(), ".local", "bin");
22269
+ CODEINDEX_INSTALL_DIR = join51(homedir19(), ".local", "bin");
22215
22270
  CODEINDEX_BIN_NAME = process.platform === "win32" ? "codeindex.exe" : "codeindex";
22216
22271
  CODEINDEX_BIN_PATH = join51(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
22217
22272
  }
@@ -23272,14 +23327,14 @@ __export(service_installer_exports, {
23272
23327
  uninstallService: () => uninstallService
23273
23328
  });
23274
23329
  import { execSync as execSync5 } from "child_process";
23275
- import { existsSync as existsSync48, mkdirSync as mkdirSync16, readFileSync as readFileSync44, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "fs";
23276
- import { homedir as homedir19 } from "os";
23330
+ import { existsSync as existsSync48, mkdirSync as mkdirSync17, readFileSync as readFileSync44, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "fs";
23331
+ import { homedir as homedir20 } from "os";
23277
23332
  import { join as join53 } from "path";
23278
23333
  import { fileURLToPath as fileURLToPath5 } from "url";
23279
23334
  function resolveDaemonPaths() {
23280
23335
  const watchJs = fileURLToPath5(new URL("./watch.js", import.meta.url));
23281
23336
  const node = process.execPath;
23282
- const dataDir = join53(homedir19(), ".chat-recall");
23337
+ const dataDir = join53(homedir20(), ".chat-recall");
23283
23338
  const logFile = join53(dataDir, "watch.log");
23284
23339
  return { watchJs, node, logFile, dataDir };
23285
23340
  }
@@ -23307,7 +23362,7 @@ function refreshServiceDefinition() {
23307
23362
  if (process.platform !== "linux") return false;
23308
23363
  try {
23309
23364
  const { watchJs, node, logFile } = resolveDaemonPaths();
23310
- const unitPath = join53(homedir19(), ".config", "systemd", "user", "chat-recall-watch.service");
23365
+ const unitPath = join53(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
23311
23366
  if (!existsSync48(unitPath)) return false;
23312
23367
  const current = readFileSync44(unitPath, "utf8");
23313
23368
  if (!current.includes(watchJs)) return false;
@@ -23322,7 +23377,7 @@ function refreshServiceDefinition() {
23322
23377
  }
23323
23378
  function installService() {
23324
23379
  const { watchJs, node, logFile, dataDir } = resolveDaemonPaths();
23325
- mkdirSync16(dataDir, { recursive: true });
23380
+ mkdirSync17(dataDir, { recursive: true });
23326
23381
  if (process.platform === "linux") {
23327
23382
  return installSystemd(watchJs, node, logFile);
23328
23383
  }
@@ -23338,10 +23393,10 @@ function installService() {
23338
23393
  );
23339
23394
  }
23340
23395
  function installSystemd(watchJs, node, logFile) {
23341
- const unitDir = join53(homedir19(), ".config", "systemd", "user");
23396
+ const unitDir = join53(homedir20(), ".config", "systemd", "user");
23342
23397
  const unitPath = join53(unitDir, "chat-recall-watch.service");
23343
23398
  const unit = renderSystemdUnit(watchJs, node, logFile);
23344
- mkdirSync16(unitDir, { recursive: true });
23399
+ mkdirSync17(unitDir, { recursive: true });
23345
23400
  writeFileSync16(unitPath, unit);
23346
23401
  try {
23347
23402
  execSync5("systemctl --user daemon-reload && systemctl --user enable chat-recall-watch.service && systemctl --user restart chat-recall-watch.service", { stdio: "inherit" });
@@ -23413,10 +23468,10 @@ function renderSystemdUnit(watchJs, node, logFile) {
23413
23468
  }
23414
23469
  function installLaunchd(watchJs, node, logFile) {
23415
23470
  const label = "com.chat-recall.watch";
23416
- const plistDir = join53(homedir19(), "Library", "LaunchAgents");
23471
+ const plistDir = join53(homedir20(), "Library", "LaunchAgents");
23417
23472
  const plistPath = join53(plistDir, `${label}.plist`);
23418
23473
  const plist = renderLaunchdPlist(watchJs, node, logFile, label);
23419
- mkdirSync16(plistDir, { recursive: true });
23474
+ mkdirSync17(plistDir, { recursive: true });
23420
23475
  writeFileSync16(plistPath, plist);
23421
23476
  try {
23422
23477
  try {
@@ -23470,7 +23525,7 @@ function uninstallService() {
23470
23525
  removed = true;
23471
23526
  } catch {
23472
23527
  }
23473
- const unitPath = join53(homedir19(), ".config", "systemd", "user", "chat-recall-watch.service");
23528
+ const unitPath = join53(homedir20(), ".config", "systemd", "user", "chat-recall-watch.service");
23474
23529
  if (existsSync48(unitPath)) {
23475
23530
  rmSync7(unitPath, { force: true });
23476
23531
  removed = true;
@@ -23482,7 +23537,7 @@ function uninstallService() {
23482
23537
  return removed;
23483
23538
  }
23484
23539
  if (process.platform === "darwin") {
23485
- const plistPath = join53(homedir19(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
23540
+ const plistPath = join53(homedir20(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
23486
23541
  try {
23487
23542
  execSync5(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
23488
23543
  } catch {
@@ -23532,7 +23587,7 @@ init_sync_client();
23532
23587
  init_http();
23533
23588
  import chokidar from "chokidar";
23534
23589
  import { dirname as dirname25, basename as basename25, join as join54, resolve as resolve2, sep as sep4 } from "path";
23535
- import { homedir as homedir20 } from "os";
23590
+ import { homedir as homedir21 } from "os";
23536
23591
 
23537
23592
  // src/intent-drain.ts
23538
23593
  init_http();
@@ -23544,14 +23599,14 @@ init_codex2();
23544
23599
  init_cursor();
23545
23600
  init_opencode();
23546
23601
  init_agy();
23547
- import { existsSync as existsSync45, readFileSync as readFileSync39, writeFileSync as writeFileSync13, appendFileSync, mkdirSync as mkdirSync13 } from "node:fs";
23602
+ import { existsSync as existsSync45, readFileSync as readFileSync39, writeFileSync as writeFileSync13, appendFileSync, mkdirSync as mkdirSync14 } from "node:fs";
23548
23603
  import { join as join48 } from "node:path";
23549
23604
 
23550
23605
  // src/project-tasks.ts
23551
23606
  init_http();
23552
- import { existsSync as existsSync42, readFileSync as readFileSync38, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, statSync as statSync38 } from "node:fs";
23607
+ import { existsSync as existsSync42, readFileSync as readFileSync38, writeFileSync as writeFileSync11, mkdirSync as mkdirSync12, statSync as statSync38 } from "node:fs";
23553
23608
  import { join as join45, dirname as dirname21 } from "node:path";
23554
- import { homedir as homedir17 } from "node:os";
23609
+ import { homedir as homedir18 } from "node:os";
23555
23610
 
23556
23611
  // ../engine/dist/core/secret-task-status.js
23557
23612
  var SECRET_TASK_STATUSES = ["open", "rotated", "not-a-secret", "dismissed"];
@@ -23619,7 +23674,7 @@ function parseTaskFile(content) {
23619
23674
  return out;
23620
23675
  }
23621
23676
  function ledgerPath2() {
23622
- return join45(homedir17(), ".chat-recall", "project-tasks-ledger.json");
23677
+ return join45(homedir18(), ".chat-recall", "project-tasks-ledger.json");
23623
23678
  }
23624
23679
  function readLedger() {
23625
23680
  try {
@@ -23630,7 +23685,7 @@ function readLedger() {
23630
23685
  }
23631
23686
  function writeLedger(l) {
23632
23687
  try {
23633
- mkdirSync11(dirname21(ledgerPath2()), { recursive: true });
23688
+ mkdirSync12(dirname21(ledgerPath2()), { recursive: true });
23634
23689
  } catch {
23635
23690
  }
23636
23691
  try {
@@ -23743,7 +23798,7 @@ function applyCodeRecommendation(intent) {
23743
23798
  for (const file of targets) {
23744
23799
  const dir = file.slice(0, file.lastIndexOf("/"));
23745
23800
  try {
23746
- mkdirSync13(dir, { recursive: true });
23801
+ mkdirSync14(dir, { recursive: true });
23747
23802
  } catch {
23748
23803
  }
23749
23804
  const existing = existsSync45(file) ? readFileSync39(file, "utf8") : "";
@@ -23848,7 +23903,7 @@ try {
23848
23903
  ownVersion2 = JSON.parse(readFileSync39(join48(dir, "../package.json"), "utf-8")).version || ownVersion2;
23849
23904
  } catch {
23850
23905
  try {
23851
- if (true) ownVersion2 = "0.6.5";
23906
+ if (true) ownVersion2 = "0.6.7";
23852
23907
  } catch {
23853
23908
  }
23854
23909
  }
@@ -23913,7 +23968,7 @@ init_auto_update();
23913
23968
 
23914
23969
  // src/code-index-cursor.ts
23915
23970
  init_paths();
23916
- import { readFileSync as readFileSync40, writeFileSync as writeFileSync14, mkdirSync as mkdirSync14 } from "fs";
23971
+ import { readFileSync as readFileSync40, writeFileSync as writeFileSync14, mkdirSync as mkdirSync15 } from "fs";
23917
23972
  import { join as join49, dirname as dirname24 } from "path";
23918
23973
  var cursorPath = () => join49(getDataDir(), "code-index-cursor.json");
23919
23974
  function readCursor() {
@@ -23933,7 +23988,7 @@ function readCursor() {
23933
23988
  function writeCursor(c) {
23934
23989
  try {
23935
23990
  const p = cursorPath();
23936
- mkdirSync14(dirname24(p), { recursive: true });
23991
+ mkdirSync15(dirname24(p), { recursive: true });
23937
23992
  writeFileSync14(p, JSON.stringify(c));
23938
23993
  } catch {
23939
23994
  }
@@ -24196,7 +24251,7 @@ async function runCollectorMigration(opts) {
24196
24251
  init_sync_ledger();
24197
24252
  init_collector_health();
24198
24253
  import { existsSync as existsSync49, readFileSync as readFileSync45, statSync as statSync44 } from "fs";
24199
- var CLI_VERSION3 = true ? "0.6.5" : "0.0.0";
24254
+ var CLI_VERSION3 = true ? "0.6.7" : "0.0.0";
24200
24255
  var DEBOUNCE_MS = 5e3;
24201
24256
  var CLAUDE_DIR = claudeBackend.projectsDir();
24202
24257
  var PLANS_DIR = claudeBackend.plansDir();
@@ -24633,7 +24688,7 @@ var WORKSPACE_MARKERS = [
24633
24688
  function isCodeWorkspace(p) {
24634
24689
  try {
24635
24690
  const resolved = resolve2(p);
24636
- if (resolved === sep4 || resolved === homedir20() || dirname25(resolved) === resolved) return false;
24691
+ if (resolved === sep4 || resolved === homedir21() || dirname25(resolved) === resolved) return false;
24637
24692
  } catch {
24638
24693
  return false;
24639
24694
  }
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bash
2
+ # chat-recall decision guard — fires before a tool call runs.
3
+ #
4
+ # Reads the harness's pre-execution payload on stdin, asks the decision register
5
+ # whether this call reaches for something already ruled out, and prints the
6
+ # answer in that harness's own dialect. Registered as:
7
+ #
8
+ # Claude Code PreToolUse settings.json hooks
9
+ # Codex PreToolUse hooks.json / [hooks] in config.toml
10
+ # Antigravity PreToolUse .agents/hooks.json
11
+ # Cursor beforeShellExecution .cursor/hooks.json
12
+ #
13
+ # OpenCode uses a TypeScript plugin rather than a script; `chat-recall guard`
14
+ # is the same entry point there.
15
+ #
16
+ # ── Silence is the correct failure mode ─────────────────────────────────────
17
+ #
18
+ # Every failure path exits 0 with no output, and this file never blocks. A guard
19
+ # that errors is a guard that stops work it was never asked to judge, and the
20
+ # first time it does that somebody removes it — after which it protects nothing.
21
+ # The binary itself is written the same way.
22
+ #
23
+ # Env:
24
+ # CHAT_RECALL_BIN path to the binary (default: from PATH)
25
+ # CHAT_RECALL_GUARD_HARNESS claude | codex | agy | cursor | opencode
26
+ # CHAT_RECALL_GUARD=0 turn the guard off entirely
27
+ # CHAT_RECALL_GUARD_ENFORCE=1 block instead of warning (opt in; see below)
28
+
29
+ set -uo pipefail
30
+
31
+ # Off by request. Checked first so disabling costs nothing.
32
+ if [[ "${CHAT_RECALL_GUARD:-1}" == "0" ]]; then exit 0; fi
33
+
34
+ # stdin is the payload. Read it even when we are about to bail, or the harness
35
+ # can see a broken pipe on a hook that was only meant to be quiet.
36
+ INPUT="$(cat 2>/dev/null || true)"
37
+ [[ -z "$INPUT" ]] && exit 0
38
+
39
+ BIN="${CHAT_RECALL_BIN:-$(command -v chat-recall 2>/dev/null || true)}"
40
+ if [[ -z "$BIN" ]] || [[ ! -x "$BIN" ]]; then exit 0; fi
41
+
42
+ HARNESS="${CHAT_RECALL_GUARD_HARNESS:-claude}"
43
+
44
+ ARGS=(guard --harness "$HARNESS")
45
+ # Enforcement is opt-in and per machine. The default is a warning on every
46
+ # harness, because a wrong block burns the user's turn while a wrong warning
47
+ # costs a line of context — and only one of those gets the guard uninstalled.
48
+ if [[ "${CHAT_RECALL_GUARD_ENFORCE:-0}" == "1" ]]; then ARGS+=(--enforce); fi
49
+
50
+ # A hard ceiling on top of the binary's own timeout. This runs in front of every
51
+ # tool call, so the worst case has to be bounded by something the harness can
52
+ # rely on rather than by a network.
53
+ printf '%s' "$INPUT" | timeout 8 "$BIN" "${ARGS[@]}" 2>/dev/null || true
54
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat-recall",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/munhq/chat-recall.git"