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/README.md +45 -0
- package/dist/auto-update.d.ts +12 -0
- package/dist/auto-update.d.ts.map +1 -1
- package/dist/auto-update.js +115 -9
- package/dist/auto-update.js.map +1 -1
- package/dist/board-hooks.d.ts +56 -0
- package/dist/board-hooks.d.ts.map +1 -0
- package/dist/board-hooks.js +144 -0
- package/dist/board-hooks.js.map +1 -0
- package/dist/cli.js +1501 -353
- package/dist/cli.js.map +1 -1
- package/dist/mcp-relay.js +1 -1
- package/dist/mcp.js +85 -30
- package/dist/star-nudge.d.ts +13 -0
- package/dist/star-nudge.d.ts.map +1 -0
- package/dist/star-nudge.js +83 -0
- package/dist/star-nudge.js.map +1 -0
- package/dist/watch.js +111 -56
- package/hooks/chat_recall_guard_hook.sh +54 -0
- package/package.json +1 -1
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:
|
|
2037
|
+
var { join: join62 } = __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"] ||
|
|
2080
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join62(__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:
|
|
2483
|
+
var { join: join62, 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"] ||
|
|
2546
|
+
target = bundlerOverrides["pino-worker"] || join62(__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"] ||
|
|
2564
|
+
target = bundlerOverrides["pino-worker"] || join62(__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
|
|
2586
|
+
return join62(__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
|
|
3575
|
+
let join62 = ",";
|
|
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
|
-
|
|
3589
|
+
join62 = `,
|
|
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 +=
|
|
3597
|
+
res += join62;
|
|
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 += `${
|
|
3603
|
+
res += `${join62}"... ${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
|
-
|
|
3624
|
+
join62 = `,
|
|
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 =
|
|
3638
|
+
separator = join62;
|
|
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 =
|
|
3644
|
+
separator = join62;
|
|
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
|
|
3685
|
+
let join62 = ",";
|
|
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
|
-
|
|
3698
|
+
join62 = `,
|
|
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 +=
|
|
3706
|
+
res += join62;
|
|
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 += `${
|
|
3712
|
+
res += `${join62}"... ${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
|
-
|
|
3725
|
+
join62 = `,
|
|
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 =
|
|
3734
|
+
separator = join62;
|
|
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
|
|
3792
|
+
const join63 = `,
|
|
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 +=
|
|
3799
|
+
res2 += join63;
|
|
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 += `${
|
|
3805
|
+
res2 += `${join63}"... ${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
|
|
3821
|
+
const join62 = `,
|
|
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,
|
|
3827
|
+
res += stringifyTypedArray(value, join62, maximumBreadth);
|
|
3828
3828
|
keys = keys.slice(value.length);
|
|
3829
3829
|
maximumPropertiesToStringify -= value.length;
|
|
3830
|
-
separator =
|
|
3830
|
+
separator = join62;
|
|
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 =
|
|
3841
|
+
separator = join62;
|
|
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 =
|
|
3847
|
+
separator = join62;
|
|
3848
3848
|
}
|
|
3849
3849
|
if (separator !== "") {
|
|
3850
3850
|
res = `
|
|
@@ -7443,11 +7443,11 @@ async function archiveRawSession(store, sessionId) {
|
|
|
7443
7443
|
return await store.putRawSession(sessionId, exp.tool, Math.floor(exp.mtime), gz, size);
|
|
7444
7444
|
}
|
|
7445
7445
|
function detectForkPredecessor(sessionPath) {
|
|
7446
|
-
const { readFileSync:
|
|
7447
|
-
const { dirname:
|
|
7446
|
+
const { readFileSync: readFileSync50, readdirSync: readdirSync35 } = requireFs();
|
|
7447
|
+
const { dirname: dirname30, join: join62, basename: basename28 } = requirePath();
|
|
7448
7448
|
let text;
|
|
7449
7449
|
try {
|
|
7450
|
-
text =
|
|
7450
|
+
text = readFileSync50(sessionPath, "utf-8");
|
|
7451
7451
|
} catch {
|
|
7452
7452
|
return null;
|
|
7453
7453
|
}
|
|
@@ -7469,7 +7469,7 @@ function detectForkPredecessor(sessionPath) {
|
|
|
7469
7469
|
return null;
|
|
7470
7470
|
if (text.includes(`"uuid":"${headParent}"`))
|
|
7471
7471
|
return null;
|
|
7472
|
-
const dir =
|
|
7472
|
+
const dir = dirname30(sessionPath);
|
|
7473
7473
|
const self = basename28(sessionPath);
|
|
7474
7474
|
let entries;
|
|
7475
7475
|
try {
|
|
@@ -7481,7 +7481,7 @@ function detectForkPredecessor(sessionPath) {
|
|
|
7481
7481
|
if (!f.endsWith(".jsonl") || f === self || f === "sessions-index.json")
|
|
7482
7482
|
continue;
|
|
7483
7483
|
try {
|
|
7484
|
-
if (
|
|
7484
|
+
if (readFileSync50(join62(dir, f), "utf-8").includes(`"uuid":"${headParent}"`)) {
|
|
7485
7485
|
return basename28(f, ".jsonl");
|
|
7486
7486
|
}
|
|
7487
7487
|
} catch {
|
|
@@ -13995,10 +13995,10 @@ var init_types = __esm({
|
|
|
13995
13995
|
|
|
13996
13996
|
// ../engine/dist/transcript/opencode.js
|
|
13997
13997
|
async function parseOpenCodeSubagents(parentSessionId) {
|
|
13998
|
-
const { existsSync:
|
|
13998
|
+
const { existsSync: existsSync57 } = await import("fs");
|
|
13999
13999
|
const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
|
|
14000
14000
|
const dbPath = opencodeBackend2.dbPath();
|
|
14001
|
-
if (!
|
|
14001
|
+
if (!existsSync57(dbPath))
|
|
14002
14002
|
return [];
|
|
14003
14003
|
const realParent = parentSessionId.replace("opencode_", "");
|
|
14004
14004
|
const db = openSqliteReadonlyOrThrow(dbPath);
|
|
@@ -14039,10 +14039,10 @@ async function parseOpenCodeSubagents(parentSessionId) {
|
|
|
14039
14039
|
}
|
|
14040
14040
|
}
|
|
14041
14041
|
async function parseOpenCodeTranscript(sessionId) {
|
|
14042
|
-
const { existsSync:
|
|
14042
|
+
const { existsSync: existsSync57 } = await import("fs");
|
|
14043
14043
|
const { opencodeBackend: opencodeBackend2 } = await Promise.resolve().then(() => (init_opencode(), opencode_exports));
|
|
14044
14044
|
const dbPath = opencodeBackend2.dbPath();
|
|
14045
|
-
if (!
|
|
14045
|
+
if (!existsSync57(dbPath))
|
|
14046
14046
|
return [];
|
|
14047
14047
|
const realId = sessionId.replace("opencode_", "");
|
|
14048
14048
|
const db = openSqliteReadonlyOrThrow(dbPath);
|
|
@@ -18817,7 +18817,7 @@ var init_http = __esm({
|
|
|
18817
18817
|
CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
18818
18818
|
} catch {
|
|
18819
18819
|
try {
|
|
18820
|
-
if (true) CLI_VERSION = "0.6.
|
|
18820
|
+
if (true) CLI_VERSION = "0.6.7";
|
|
18821
18821
|
} catch {
|
|
18822
18822
|
}
|
|
18823
18823
|
}
|
|
@@ -19337,7 +19337,7 @@ var init_telemetry = __esm({
|
|
|
19337
19337
|
init_sync_client();
|
|
19338
19338
|
cliVersion2 = () => {
|
|
19339
19339
|
try {
|
|
19340
|
-
return true ? "0.6.
|
|
19340
|
+
return true ? "0.6.7" : "dev";
|
|
19341
19341
|
} catch {
|
|
19342
19342
|
return "dev";
|
|
19343
19343
|
}
|
|
@@ -19666,12 +19666,16 @@ var init_client_events = __esm({
|
|
|
19666
19666
|
// src/auto-update.ts
|
|
19667
19667
|
var auto_update_exports = {};
|
|
19668
19668
|
__export(auto_update_exports, {
|
|
19669
|
+
clearUpdateFailures: () => clearUpdateFailures,
|
|
19669
19670
|
compareVersions: () => compareVersions,
|
|
19670
19671
|
executeAutoUpdate: () => executeAutoUpdate,
|
|
19672
|
+
hasGivenUpOn: () => hasGivenUpOn,
|
|
19671
19673
|
installedVersion: () => installedVersion,
|
|
19672
19674
|
isAutoUpdateEnabled: () => isAutoUpdateEnabled,
|
|
19673
19675
|
newerOf: () => newerOf,
|
|
19676
|
+
noteUpdateFailure: () => noteUpdateFailure,
|
|
19674
19677
|
planAutoUpdate: () => planAutoUpdate,
|
|
19678
|
+
readUpdateState: () => readUpdateState,
|
|
19675
19679
|
runAutoUpdate: () => runAutoUpdate,
|
|
19676
19680
|
runningPrefix: () => runningPrefix,
|
|
19677
19681
|
sweepStaleStaging: () => sweepStaleStaging,
|
|
@@ -19680,9 +19684,9 @@ __export(auto_update_exports, {
|
|
|
19680
19684
|
import { execSync as execSync2 } from "node:child_process";
|
|
19681
19685
|
import { readFileSync as readFileSync34 } from "node:fs";
|
|
19682
19686
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19683
|
-
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync36 } from "node:fs";
|
|
19687
|
+
import { writeFileSync as writeFileSync8, mkdtempSync as mkdtempSync3, rmSync as rmSync5, readdirSync as readdirSync31, statSync as statSync36, mkdirSync as mkdirSync9 } from "node:fs";
|
|
19684
19688
|
import { join as join41, dirname as dirname18, sep as sep2 } from "node:path";
|
|
19685
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
19689
|
+
import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
|
|
19686
19690
|
import { createHash as createHash8 } from "node:crypto";
|
|
19687
19691
|
function compareVersions(a, b) {
|
|
19688
19692
|
const pa = String(a).split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
@@ -19796,16 +19800,25 @@ function runningPrefix(moduleUrl = import.meta.url) {
|
|
|
19796
19800
|
const beforeNodeModules = here.slice(0, at);
|
|
19797
19801
|
return beforeNodeModules.endsWith(`${sep2}lib`) ? beforeNodeModules.slice(0, -4) : beforeNodeModules;
|
|
19798
19802
|
}
|
|
19803
|
+
function npmInstall(cmd) {
|
|
19804
|
+
try {
|
|
19805
|
+
execSync2(cmd, { stdio: ["ignore", "ignore", "pipe"] });
|
|
19806
|
+
} catch (e) {
|
|
19807
|
+
const err = e;
|
|
19808
|
+
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);
|
|
19809
|
+
throw new Error(detail ? `${err.message ?? "install failed"} \u2014 ${detail}` : err.message ?? "install failed");
|
|
19810
|
+
}
|
|
19811
|
+
}
|
|
19799
19812
|
function realInstall(tgz) {
|
|
19800
19813
|
const prefix = runningPrefix();
|
|
19801
19814
|
if (prefix) {
|
|
19802
|
-
|
|
19815
|
+
npmInstall(`npm install -g --prefix "${prefix}" "${tgz}"`);
|
|
19803
19816
|
return;
|
|
19804
19817
|
}
|
|
19805
19818
|
try {
|
|
19806
|
-
|
|
19819
|
+
npmInstall(`npm install -g "${tgz}"`);
|
|
19807
19820
|
} catch {
|
|
19808
|
-
|
|
19821
|
+
npmInstall(`npm install -g --prefix "${process.env.HOME}/.local" "${tgz}"`);
|
|
19809
19822
|
}
|
|
19810
19823
|
}
|
|
19811
19824
|
function realRestart(platform2) {
|
|
@@ -19813,6 +19826,37 @@ function realRestart(platform2) {
|
|
|
19813
19826
|
else if (platform2 === "darwin") execSync2("launchctl kickstart -k gui/$(id -u)/com.chat-recall.watch", { stdio: "ignore", shell: "/bin/bash" });
|
|
19814
19827
|
else if (platform2 === "win32") execSync2("schtasks /End /TN chat-recall-watch & schtasks /Run /TN chat-recall-watch", { stdio: "ignore" });
|
|
19815
19828
|
}
|
|
19829
|
+
function updateStatePath() {
|
|
19830
|
+
const dir = process.env.CHAT_RECALL_DATA_DIR || join41(homedir15(), ".chat-recall");
|
|
19831
|
+
return join41(dir, "auto-update-state.json");
|
|
19832
|
+
}
|
|
19833
|
+
function readUpdateState() {
|
|
19834
|
+
try {
|
|
19835
|
+
return JSON.parse(readFileSync34(updateStatePath(), "utf8"));
|
|
19836
|
+
} catch {
|
|
19837
|
+
return {};
|
|
19838
|
+
}
|
|
19839
|
+
}
|
|
19840
|
+
function writeUpdateState(st) {
|
|
19841
|
+
try {
|
|
19842
|
+
const p = updateStatePath();
|
|
19843
|
+
mkdirSync9(dirname18(p), { recursive: true });
|
|
19844
|
+
writeFileSync8(p, JSON.stringify(st), "utf8");
|
|
19845
|
+
} catch {
|
|
19846
|
+
}
|
|
19847
|
+
}
|
|
19848
|
+
function hasGivenUpOn(target, st = readUpdateState()) {
|
|
19849
|
+
return st.target === target && (st.failures ?? 0) >= GIVE_UP_AFTER;
|
|
19850
|
+
}
|
|
19851
|
+
function noteUpdateFailure(target) {
|
|
19852
|
+
const st = readUpdateState();
|
|
19853
|
+
const failures = st.target === target ? (st.failures ?? 0) + 1 : 1;
|
|
19854
|
+
writeUpdateState({ target, failures });
|
|
19855
|
+
return failures;
|
|
19856
|
+
}
|
|
19857
|
+
function clearUpdateFailures() {
|
|
19858
|
+
writeUpdateState({});
|
|
19859
|
+
}
|
|
19816
19860
|
async function runAutoUpdate(base3, authHeaders, ownVersion3, deps) {
|
|
19817
19861
|
let caps;
|
|
19818
19862
|
try {
|
|
@@ -19835,6 +19879,10 @@ async function runAutoUpdate(base3, authHeaders, ownVersion3, deps) {
|
|
|
19835
19879
|
}
|
|
19836
19880
|
return { updated: false, reason: plan.reason };
|
|
19837
19881
|
}
|
|
19882
|
+
const target = plan.to ?? "";
|
|
19883
|
+
if (target && hasGivenUpOn(target)) {
|
|
19884
|
+
return { updated: false, reason: `gave up installing ${plan.to} after ${GIVE_UP_AFTER} failed attempts \u2014 run \`npm install -g chat-recall\` by hand` };
|
|
19885
|
+
}
|
|
19838
19886
|
const result = await executeAutoUpdate(plan, {
|
|
19839
19887
|
download: deps?.download ?? realDownload,
|
|
19840
19888
|
install: deps?.install ?? realInstall,
|
|
@@ -19842,20 +19890,27 @@ async function runAutoUpdate(base3, authHeaders, ownVersion3, deps) {
|
|
|
19842
19890
|
platform: deps?.platform
|
|
19843
19891
|
});
|
|
19844
19892
|
if (!result.updated) {
|
|
19845
|
-
|
|
19846
|
-
|
|
19847
|
-
|
|
19848
|
-
|
|
19893
|
+
const failures = target ? noteUpdateFailure(target) : 1;
|
|
19894
|
+
if (failures <= GIVE_UP_AFTER) {
|
|
19895
|
+
try {
|
|
19896
|
+
const { reportClientEvent: reportClientEvent2 } = await Promise.resolve().then(() => (init_client_events(), client_events_exports));
|
|
19897
|
+
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})`;
|
|
19898
|
+
reportClientEvent2("auto_update_failed", { message: `${plan.from} \u2192 ${plan.to}: ${result.reason}${suffix}` });
|
|
19899
|
+
} catch {
|
|
19900
|
+
}
|
|
19849
19901
|
}
|
|
19902
|
+
} else {
|
|
19903
|
+
clearUpdateFailures();
|
|
19850
19904
|
}
|
|
19851
19905
|
return result;
|
|
19852
19906
|
}
|
|
19853
|
-
var tarballUrl, reportedStandingSkip;
|
|
19907
|
+
var tarballUrl, GIVE_UP_AFTER, reportedStandingSkip;
|
|
19854
19908
|
var init_auto_update = __esm({
|
|
19855
19909
|
"src/auto-update.ts"() {
|
|
19856
19910
|
"use strict";
|
|
19857
19911
|
init_http();
|
|
19858
19912
|
tarballUrl = (base3) => `${base3.replace(/\/+$/, "")}/install/chat-recall.tgz`;
|
|
19913
|
+
GIVE_UP_AFTER = 3;
|
|
19859
19914
|
reportedStandingSkip = /* @__PURE__ */ new Set();
|
|
19860
19915
|
}
|
|
19861
19916
|
});
|
|
@@ -19890,7 +19945,7 @@ __export(sync_client_exports, {
|
|
|
19890
19945
|
syncSessions: () => syncSessions
|
|
19891
19946
|
});
|
|
19892
19947
|
import { join as join42, dirname as dirname19, basename as basename20 } from "node:path";
|
|
19893
|
-
import { readFileSync as readFileSync35, writeFileSync as writeFileSync9, mkdirSync as
|
|
19948
|
+
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";
|
|
19894
19949
|
import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
|
|
19895
19950
|
import { createHash as createHash9 } from "node:crypto";
|
|
19896
19951
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -19925,7 +19980,7 @@ function classifyError(msg, err) {
|
|
|
19925
19980
|
}
|
|
19926
19981
|
function saveCredentials(c) {
|
|
19927
19982
|
assertTransportSafe(c.serverUrl);
|
|
19928
|
-
|
|
19983
|
+
mkdirSync10(dirname19(credPath()), { recursive: true });
|
|
19929
19984
|
const targets = loadAllCredentials().filter((t) => t.serverUrl !== c.serverUrl);
|
|
19930
19985
|
targets.unshift(c);
|
|
19931
19986
|
writeFileSync9(credPath(), JSON.stringify({ targets }, null, 2));
|
|
@@ -20020,7 +20075,7 @@ function persistSyncConfig(base3, config2) {
|
|
|
20020
20075
|
try {
|
|
20021
20076
|
const all = readPersistedSyncConfigs();
|
|
20022
20077
|
all[base3] = { fetchedAt: Date.now(), config: config2 };
|
|
20023
|
-
|
|
20078
|
+
mkdirSync10(dirname19(syncConfigCachePath()), { recursive: true });
|
|
20024
20079
|
writeFileSync9(syncConfigCachePath(), JSON.stringify(all, null, 2));
|
|
20025
20080
|
} catch {
|
|
20026
20081
|
}
|
|
@@ -21426,7 +21481,7 @@ var init_sync_client = __esm({
|
|
|
21426
21481
|
ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
21427
21482
|
} catch {
|
|
21428
21483
|
try {
|
|
21429
|
-
if (true) ownVersion = "0.6.
|
|
21484
|
+
if (true) ownVersion = "0.6.7";
|
|
21430
21485
|
} catch {
|
|
21431
21486
|
}
|
|
21432
21487
|
}
|
|
@@ -21678,16 +21733,16 @@ __export(mcp_clients_exports, {
|
|
|
21678
21733
|
inspectMcpClients: () => inspectMcpClients,
|
|
21679
21734
|
registerMcpEverywhere: () => registerMcpEverywhere
|
|
21680
21735
|
});
|
|
21681
|
-
import { existsSync as
|
|
21682
|
-
import { homedir as
|
|
21683
|
-
import { dirname as
|
|
21736
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync12, readFileSync as readFileSync37, writeFileSync as writeFileSync12 } from "fs";
|
|
21737
|
+
import { homedir as homedir16 } from "os";
|
|
21738
|
+
import { dirname as dirname21, join as join46 } from "path";
|
|
21684
21739
|
function envHome(varName, fallback) {
|
|
21685
21740
|
const v = process.env[varName];
|
|
21686
21741
|
return v && v.trim() ? v.trim() : fallback;
|
|
21687
21742
|
}
|
|
21688
21743
|
function writeFileMkdir(path, body) {
|
|
21689
|
-
|
|
21690
|
-
|
|
21744
|
+
mkdirSync12(dirname21(path), { recursive: true });
|
|
21745
|
+
writeFileSync12(path, body);
|
|
21691
21746
|
}
|
|
21692
21747
|
function readJsonObject(path) {
|
|
21693
21748
|
const raw = readFileSync37(path, "utf-8");
|
|
@@ -21697,7 +21752,7 @@ function readJsonObject(path) {
|
|
|
21697
21752
|
}
|
|
21698
21753
|
function registerMcpServersJson(target, path, spec) {
|
|
21699
21754
|
let cfg = {};
|
|
21700
|
-
if (
|
|
21755
|
+
if (existsSync43(path)) {
|
|
21701
21756
|
try {
|
|
21702
21757
|
cfg = readJsonObject(path);
|
|
21703
21758
|
} catch (err) {
|
|
@@ -21726,7 +21781,7 @@ function registerOpencodeJson(path, spec) {
|
|
|
21726
21781
|
const id = "opencode";
|
|
21727
21782
|
const label = "OpenCode";
|
|
21728
21783
|
let cfg = {};
|
|
21729
|
-
if (
|
|
21784
|
+
if (existsSync43(path)) {
|
|
21730
21785
|
try {
|
|
21731
21786
|
cfg = readJsonObject(path);
|
|
21732
21787
|
} catch (err) {
|
|
@@ -21765,7 +21820,7 @@ function registerCodexToml(path, spec) {
|
|
|
21765
21820
|
const id = "codex";
|
|
21766
21821
|
const label = "Codex";
|
|
21767
21822
|
const block = codexBlock(spec);
|
|
21768
|
-
const prev =
|
|
21823
|
+
const prev = existsSync43(path) ? readFileSync37(path, "utf-8") : "";
|
|
21769
21824
|
const lines = prev.length ? prev.split("\n") : [];
|
|
21770
21825
|
const start = lines.findIndex((l) => l.trim() === CODEX_HEADER);
|
|
21771
21826
|
if (start === -1) {
|
|
@@ -21797,16 +21852,16 @@ function getMcpClient(id) {
|
|
|
21797
21852
|
return t;
|
|
21798
21853
|
}
|
|
21799
21854
|
function registerMcpEverywhere(spec, opts = {}) {
|
|
21800
|
-
const home = opts.home ??
|
|
21855
|
+
const home = opts.home ?? homedir16();
|
|
21801
21856
|
const forced = new Set(opts.extraIds ?? []);
|
|
21802
21857
|
return MCP_CLIENTS.filter((c) => c.isPresent(home) || forced.has(c.id)).map((c) => c.register(spec, home));
|
|
21803
21858
|
}
|
|
21804
21859
|
function inspectMcpClients(opts = {}) {
|
|
21805
|
-
const home = opts.home ??
|
|
21860
|
+
const home = opts.home ?? homedir16();
|
|
21806
21861
|
return MCP_CLIENTS.map((c) => {
|
|
21807
21862
|
const path = c.configPath(home);
|
|
21808
21863
|
let registered = false;
|
|
21809
|
-
if (
|
|
21864
|
+
if (existsSync43(path)) {
|
|
21810
21865
|
try {
|
|
21811
21866
|
const raw = readFileSync37(path, "utf-8");
|
|
21812
21867
|
registered = c.id === "codex" ? raw.includes(CODEX_HEADER) : !!(JSON.parse(raw).mcpServers?.[MCP_SERVER_NAME] ?? JSON.parse(raw).mcp?.[MCP_SERVER_NAME]);
|
|
@@ -21828,7 +21883,7 @@ var init_mcp_clients = __esm({
|
|
|
21828
21883
|
id: "claude",
|
|
21829
21884
|
label: "Claude Code",
|
|
21830
21885
|
bin: "claude",
|
|
21831
|
-
configPath: (home) =>
|
|
21886
|
+
configPath: (home) => join46(home, ".mcp.json"),
|
|
21832
21887
|
// Claude Code is the reference client and `init`'s historical target, so we
|
|
21833
21888
|
// always write it — a fresh machine has no ~/.claude yet on first install.
|
|
21834
21889
|
isPresent: () => true,
|
|
@@ -21840,10 +21895,10 @@ var init_mcp_clients = __esm({
|
|
|
21840
21895
|
id: "codex",
|
|
21841
21896
|
label: "Codex",
|
|
21842
21897
|
bin: "codex",
|
|
21843
|
-
configPath: (home) =>
|
|
21898
|
+
configPath: (home) => join46(envHome("CHAT_RECALL_CODEX_HOME", join46(home, ".codex")), "config.toml"),
|
|
21844
21899
|
isPresent(home) {
|
|
21845
21900
|
const p = this.configPath(home);
|
|
21846
|
-
return
|
|
21901
|
+
return existsSync43(p) || existsSync43(dirname21(p));
|
|
21847
21902
|
},
|
|
21848
21903
|
register(spec, home) {
|
|
21849
21904
|
return registerCodexToml(this.configPath(home), spec);
|
|
@@ -21853,10 +21908,10 @@ var init_mcp_clients = __esm({
|
|
|
21853
21908
|
id: "opencode",
|
|
21854
21909
|
label: "OpenCode",
|
|
21855
21910
|
bin: "opencode",
|
|
21856
|
-
configPath: (home) =>
|
|
21911
|
+
configPath: (home) => join46(envHome("XDG_CONFIG_HOME", join46(home, ".config")), "opencode", "opencode.json"),
|
|
21857
21912
|
isPresent(home) {
|
|
21858
21913
|
const p = this.configPath(home);
|
|
21859
|
-
return
|
|
21914
|
+
return existsSync43(p) || existsSync43(dirname21(p));
|
|
21860
21915
|
},
|
|
21861
21916
|
register(spec, home) {
|
|
21862
21917
|
return registerOpencodeJson(this.configPath(home), spec);
|
|
@@ -21868,9 +21923,9 @@ var init_mcp_clients = __esm({
|
|
|
21868
21923
|
id: "agy",
|
|
21869
21924
|
label: "Antigravity",
|
|
21870
21925
|
bin: "agy",
|
|
21871
|
-
configPath: (home) =>
|
|
21926
|
+
configPath: (home) => join46(home, ".gemini", "config", "mcp_config.json"),
|
|
21872
21927
|
isPresent(home) {
|
|
21873
|
-
return
|
|
21928
|
+
return existsSync43(join46(home, ".gemini", "antigravity-cli")) || existsSync43(this.configPath(home));
|
|
21874
21929
|
},
|
|
21875
21930
|
register(spec, home) {
|
|
21876
21931
|
return registerMcpServersJson(this, this.configPath(home), spec);
|
|
@@ -21880,10 +21935,10 @@ var init_mcp_clients = __esm({
|
|
|
21880
21935
|
id: "cursor",
|
|
21881
21936
|
label: "Cursor",
|
|
21882
21937
|
bin: "cursor",
|
|
21883
|
-
configPath: (home) =>
|
|
21938
|
+
configPath: (home) => join46(home, ".cursor", "mcp.json"),
|
|
21884
21939
|
isPresent(home) {
|
|
21885
21940
|
const p = this.configPath(home);
|
|
21886
|
-
return
|
|
21941
|
+
return existsSync43(p) || existsSync43(dirname21(p));
|
|
21887
21942
|
},
|
|
21888
21943
|
register(spec, home) {
|
|
21889
21944
|
return registerMcpServersJson(this, this.configPath(home), spec);
|
|
@@ -21907,9 +21962,9 @@ __export(companions_exports, {
|
|
|
21907
21962
|
uninstallCodeindex: () => uninstallCodeindex,
|
|
21908
21963
|
unregisterCodeindexMcp: () => unregisterCodeindexMcp
|
|
21909
21964
|
});
|
|
21910
|
-
import { existsSync as
|
|
21911
|
-
import { homedir as
|
|
21912
|
-
import { join as
|
|
21965
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync13, statSync as statSync39, chmodSync as chmodSync4, unlinkSync as unlinkSync3, renameSync as renameSync5, createWriteStream, readFileSync as readFileSync38, writeFileSync as writeFileSync13 } from "node:fs";
|
|
21966
|
+
import { homedir as homedir17 } from "node:os";
|
|
21967
|
+
import { join as join47 } from "node:path";
|
|
21913
21968
|
import { execSync as execSync3 } from "node:child_process";
|
|
21914
21969
|
import { createHash as createHash10 } from "node:crypto";
|
|
21915
21970
|
function expectedCodeindexSha256(artifact) {
|
|
@@ -22011,7 +22066,7 @@ async function installCodeindex(opts = {}) {
|
|
|
22011
22066
|
return status;
|
|
22012
22067
|
if (!status.prebuiltAvailable)
|
|
22013
22068
|
return status;
|
|
22014
|
-
|
|
22069
|
+
mkdirSync13(CODEINDEX_INSTALL_DIR, { recursive: true });
|
|
22015
22070
|
const tmpPath = `${CODEINDEX_BIN_PATH}.partial`;
|
|
22016
22071
|
try {
|
|
22017
22072
|
await downloadArtifact(status.artifactName, tmpPath);
|
|
@@ -22030,7 +22085,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
|
|
|
22030
22085
|
renameSync5(tmpPath, CODEINDEX_BIN_PATH);
|
|
22031
22086
|
} catch (err) {
|
|
22032
22087
|
try {
|
|
22033
|
-
if (
|
|
22088
|
+
if (existsSync44(tmpPath))
|
|
22034
22089
|
unlinkSync3(tmpPath);
|
|
22035
22090
|
} catch {
|
|
22036
22091
|
}
|
|
@@ -22039,7 +22094,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
|
|
|
22039
22094
|
return checkCodeindexStatus();
|
|
22040
22095
|
}
|
|
22041
22096
|
function uninstallCodeindex() {
|
|
22042
|
-
if (
|
|
22097
|
+
if (existsSync44(CODEINDEX_BIN_PATH)) {
|
|
22043
22098
|
unlinkSync3(CODEINDEX_BIN_PATH);
|
|
22044
22099
|
return { removed: true, path: CODEINDEX_BIN_PATH };
|
|
22045
22100
|
}
|
|
@@ -22047,7 +22102,7 @@ function uninstallCodeindex() {
|
|
|
22047
22102
|
}
|
|
22048
22103
|
function registerCodeindexMcp(mcpJsonPath, binaryPath) {
|
|
22049
22104
|
let cfg = {};
|
|
22050
|
-
if (
|
|
22105
|
+
if (existsSync44(mcpJsonPath)) {
|
|
22051
22106
|
try {
|
|
22052
22107
|
cfg = JSON.parse(readFileSync38(mcpJsonPath, "utf-8"));
|
|
22053
22108
|
} catch {
|
|
@@ -22058,11 +22113,11 @@ function registerCodeindexMcp(mcpJsonPath, binaryPath) {
|
|
|
22058
22113
|
if (cfg.mcpServers["codeindex"])
|
|
22059
22114
|
return { added: false };
|
|
22060
22115
|
cfg.mcpServers["codeindex"] = { command: binaryPath, args: ["--mcp"] };
|
|
22061
|
-
|
|
22116
|
+
writeFileSync13(mcpJsonPath, JSON.stringify(cfg, null, 2));
|
|
22062
22117
|
return { added: true };
|
|
22063
22118
|
}
|
|
22064
22119
|
function unregisterCodeindexMcp(mcpJsonPath) {
|
|
22065
|
-
if (!
|
|
22120
|
+
if (!existsSync44(mcpJsonPath))
|
|
22066
22121
|
return { removed: false };
|
|
22067
22122
|
let cfg;
|
|
22068
22123
|
try {
|
|
@@ -22073,7 +22128,7 @@ function unregisterCodeindexMcp(mcpJsonPath) {
|
|
|
22073
22128
|
if (!cfg.mcpServers?.["codeindex"])
|
|
22074
22129
|
return { removed: false };
|
|
22075
22130
|
delete cfg.mcpServers["codeindex"];
|
|
22076
|
-
|
|
22131
|
+
writeFileSync13(mcpJsonPath, JSON.stringify(cfg, null, 2));
|
|
22077
22132
|
return { removed: true };
|
|
22078
22133
|
}
|
|
22079
22134
|
var CODEINDEX_REPO, CODEINDEX_VERSION, CODEINDEX_SHA256, CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME, CODEINDEX_BIN_PATH;
|
|
@@ -22091,9 +22146,9 @@ var init_companions = __esm({
|
|
|
22091
22146
|
"x86_64-windows": "a96fc8c8949bb399a44cc0615b12e458460a529c69f01e8829cc39a20f45ab29",
|
|
22092
22147
|
"aarch64-windows": "42620d56272cd9ba9d785569d8d754d6b4a3e80f36f28db3e0c483fd600ed117"
|
|
22093
22148
|
};
|
|
22094
|
-
CODEINDEX_INSTALL_DIR =
|
|
22149
|
+
CODEINDEX_INSTALL_DIR = join47(homedir17(), ".local", "bin");
|
|
22095
22150
|
CODEINDEX_BIN_NAME = process.platform === "win32" ? "codeindex.exe" : "codeindex";
|
|
22096
|
-
CODEINDEX_BIN_PATH =
|
|
22151
|
+
CODEINDEX_BIN_PATH = join47(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
|
|
22097
22152
|
}
|
|
22098
22153
|
});
|
|
22099
22154
|
|
|
@@ -22108,11 +22163,11 @@ __export(install_skills_exports, {
|
|
|
22108
22163
|
skillsSourceDir: () => skillsSourceDir,
|
|
22109
22164
|
uninstallSkills: () => uninstallSkills
|
|
22110
22165
|
});
|
|
22111
|
-
import { readdirSync as readdirSync33, existsSync as
|
|
22166
|
+
import { readdirSync as readdirSync33, existsSync as existsSync45, mkdirSync as mkdirSync14, rmSync as rmSync7, cpSync, writeFileSync as writeFileSync14, readFileSync as readFileSync39, statSync as statSync40, realpathSync as realpathSync4 } from "fs";
|
|
22112
22167
|
import { createHash as createHash11 } from "crypto";
|
|
22113
|
-
import { join as
|
|
22168
|
+
import { join as join48, basename as basename21 } from "path";
|
|
22114
22169
|
function skillsSourceDir() {
|
|
22115
|
-
return
|
|
22170
|
+
return join48(import.meta.dirname, "..", "skills");
|
|
22116
22171
|
}
|
|
22117
22172
|
function version() {
|
|
22118
22173
|
try {
|
|
@@ -22125,9 +22180,9 @@ function skillsFingerprint() {
|
|
|
22125
22180
|
const src = skillsSourceDir();
|
|
22126
22181
|
const h = createHash11("sha256");
|
|
22127
22182
|
for (const name of bundledSkillNames()) {
|
|
22128
|
-
for (const rel of filesUnder(
|
|
22183
|
+
for (const rel of filesUnder(join48(src, name)).sort()) {
|
|
22129
22184
|
h.update(name + "/" + rel);
|
|
22130
|
-
h.update(readFileSync39(
|
|
22185
|
+
h.update(readFileSync39(join48(src, name, rel)));
|
|
22131
22186
|
}
|
|
22132
22187
|
}
|
|
22133
22188
|
return h.digest("hex").slice(0, 12);
|
|
@@ -22137,7 +22192,7 @@ function filesUnder(dir, prefix = "") {
|
|
|
22137
22192
|
for (const e of readdirSync33(dir, { withFileTypes: true })) {
|
|
22138
22193
|
if (e.name === MARKER) continue;
|
|
22139
22194
|
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
22140
|
-
if (e.isDirectory()) out.push(...filesUnder(
|
|
22195
|
+
if (e.isDirectory()) out.push(...filesUnder(join48(dir, e.name), rel));
|
|
22141
22196
|
else out.push(rel);
|
|
22142
22197
|
}
|
|
22143
22198
|
return out;
|
|
@@ -22147,8 +22202,8 @@ function skillStamp() {
|
|
|
22147
22202
|
}
|
|
22148
22203
|
function bundledSkillNames() {
|
|
22149
22204
|
const src = skillsSourceDir();
|
|
22150
|
-
if (!
|
|
22151
|
-
return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() &&
|
|
22205
|
+
if (!existsSync45(src)) return [];
|
|
22206
|
+
return readdirSync33(src, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync45(join48(src, e.name, "SKILL.md"))).map((e) => e.name).sort();
|
|
22152
22207
|
}
|
|
22153
22208
|
function realPathOrSelf(p) {
|
|
22154
22209
|
try {
|
|
@@ -22161,7 +22216,7 @@ function claudeSkillTargets() {
|
|
|
22161
22216
|
const targets = [];
|
|
22162
22217
|
const seen = /* @__PURE__ */ new Set();
|
|
22163
22218
|
for (const home of claudeHomeDirs()) {
|
|
22164
|
-
const dir =
|
|
22219
|
+
const dir = join48(home, "skills");
|
|
22165
22220
|
const key2 = realPathOrSelf(dir);
|
|
22166
22221
|
if (seen.has(key2)) continue;
|
|
22167
22222
|
seen.add(key2);
|
|
@@ -22172,7 +22227,7 @@ function claudeSkillTargets() {
|
|
|
22172
22227
|
dir,
|
|
22173
22228
|
// Judge each profile on ITS OWN transcripts, not the primary's: a home
|
|
22174
22229
|
// with no projects/ is a directory someone made, not a Claude install.
|
|
22175
|
-
available:
|
|
22230
|
+
available: existsSync45(join48(home, "projects"))
|
|
22176
22231
|
});
|
|
22177
22232
|
}
|
|
22178
22233
|
if (targets.length === 0) {
|
|
@@ -22187,16 +22242,16 @@ function skillTargets() {
|
|
|
22187
22242
|
// both produces "skill conflict" duplicate warnings. Every tool we support
|
|
22188
22243
|
// has its own dir, so per-tool coverage is complete.
|
|
22189
22244
|
...claudeSkillTargets(),
|
|
22190
|
-
{ id: "agy", label: "Antigravity", dir:
|
|
22245
|
+
{ id: "agy", label: "Antigravity", dir: join48(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
|
|
22191
22246
|
{ id: "codex", label: "Codex", dir: codexBackend.skillsDir(), available: codexBackend.isAvailable() },
|
|
22192
22247
|
{ id: "opencode", label: "OpenCode", dir: opencodeBackend.skillsDir(), available: opencodeBackend.isAvailable() },
|
|
22193
22248
|
// Cursor DOES have its own dir (~/.cursor/skills). `skills-cursor` next to
|
|
22194
22249
|
// it is Cursor's bundled set — read-only, never an install target.
|
|
22195
|
-
{ id: "cursor", label: "Cursor", dir:
|
|
22250
|
+
{ id: "cursor", label: "Cursor", dir: join48(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
|
|
22196
22251
|
];
|
|
22197
22252
|
}
|
|
22198
22253
|
function isManaged(dir) {
|
|
22199
|
-
return
|
|
22254
|
+
return existsSync45(join48(dir, MARKER));
|
|
22200
22255
|
}
|
|
22201
22256
|
function installSkills(opts = {}) {
|
|
22202
22257
|
const onlyAvailable = opts.onlyAvailable ?? true;
|
|
@@ -22213,15 +22268,15 @@ function installSkills(opts = {}) {
|
|
|
22213
22268
|
continue;
|
|
22214
22269
|
}
|
|
22215
22270
|
for (const name of names) {
|
|
22216
|
-
const dest =
|
|
22217
|
-
if (
|
|
22271
|
+
const dest = join48(t.dir, name);
|
|
22272
|
+
if (existsSync45(dest) && !isManaged(dest)) {
|
|
22218
22273
|
skippedUserOwned.push(name);
|
|
22219
22274
|
continue;
|
|
22220
22275
|
}
|
|
22221
|
-
|
|
22276
|
+
mkdirSync14(t.dir, { recursive: true });
|
|
22222
22277
|
rmSync7(dest, { recursive: true, force: true });
|
|
22223
|
-
cpSync(
|
|
22224
|
-
|
|
22278
|
+
cpSync(join48(src, name), dest, { recursive: true });
|
|
22279
|
+
writeFileSync14(join48(dest, MARKER), `${stamp}
|
|
22225
22280
|
`);
|
|
22226
22281
|
installed.push(name);
|
|
22227
22282
|
}
|
|
@@ -22234,13 +22289,13 @@ function uninstallSkills() {
|
|
|
22234
22289
|
const perTarget = [];
|
|
22235
22290
|
for (const t of skillTargets()) {
|
|
22236
22291
|
const removed = [];
|
|
22237
|
-
if (!
|
|
22292
|
+
if (!existsSync45(t.dir)) {
|
|
22238
22293
|
perTarget.push({ id: t.id, label: t.label, removed });
|
|
22239
22294
|
continue;
|
|
22240
22295
|
}
|
|
22241
22296
|
for (const entry of readdirSync33(t.dir, { withFileTypes: true })) {
|
|
22242
22297
|
if (!entry.isDirectory()) continue;
|
|
22243
|
-
const dir =
|
|
22298
|
+
const dir = join48(t.dir, entry.name);
|
|
22244
22299
|
if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
|
|
22245
22300
|
rmSync7(dir, { recursive: true, force: true });
|
|
22246
22301
|
removed.push(entry.name);
|
|
@@ -22257,8 +22312,8 @@ function skillsNeedRefresh() {
|
|
|
22257
22312
|
for (const t of skillTargets()) {
|
|
22258
22313
|
if (!t.available) continue;
|
|
22259
22314
|
for (const name of names) {
|
|
22260
|
-
const marker =
|
|
22261
|
-
if (!
|
|
22315
|
+
const marker = join48(t.dir, name, MARKER);
|
|
22316
|
+
if (!existsSync45(marker)) return true;
|
|
22262
22317
|
try {
|
|
22263
22318
|
if (readFileSync39(marker, "utf-8").trim() !== cur) return true;
|
|
22264
22319
|
} catch {
|
|
@@ -22275,8 +22330,8 @@ function skillStatus() {
|
|
|
22275
22330
|
let installed = 0;
|
|
22276
22331
|
let ver = null;
|
|
22277
22332
|
for (const name of names) {
|
|
22278
|
-
const marker =
|
|
22279
|
-
if (
|
|
22333
|
+
const marker = join48(t.dir, name, MARKER);
|
|
22334
|
+
if (existsSync45(marker)) {
|
|
22280
22335
|
installed++;
|
|
22281
22336
|
try {
|
|
22282
22337
|
ver = readFileSync39(marker, "utf-8").trim();
|
|
@@ -22304,8 +22359,8 @@ var init_install_skills = __esm({
|
|
|
22304
22359
|
|
|
22305
22360
|
// ../engine/dist/core/artifact-codec.js
|
|
22306
22361
|
import { readFileSync as readFileSync40 } from "fs";
|
|
22307
|
-
import { basename as basename22, join as
|
|
22308
|
-
import { homedir as
|
|
22362
|
+
import { basename as basename22, join as join49 } from "path";
|
|
22363
|
+
import { homedir as homedir18 } from "os";
|
|
22309
22364
|
function parseCommandText(content, format, fallbackName) {
|
|
22310
22365
|
if (format === "toml") {
|
|
22311
22366
|
const t = parseScalarToml(content);
|
|
@@ -22339,29 +22394,29 @@ function readInstructions(filePath, name) {
|
|
|
22339
22394
|
function commandPath(tool, name) {
|
|
22340
22395
|
switch (tool) {
|
|
22341
22396
|
case "claude":
|
|
22342
|
-
return
|
|
22397
|
+
return join49(claudeBackend.commandsDir(), `${name}.md`);
|
|
22343
22398
|
case "agy":
|
|
22344
|
-
return
|
|
22399
|
+
return join49(join49(agyBackend.homeDir(), "commands"), `${name}.toml`);
|
|
22345
22400
|
case "opencode":
|
|
22346
|
-
return
|
|
22401
|
+
return join49(opencodeBackend.commandsDir(), `${name}.md`);
|
|
22347
22402
|
case "codex":
|
|
22348
|
-
return
|
|
22403
|
+
return join49(codexBackend.promptsDir(), `${name}.md`);
|
|
22349
22404
|
case "cursor":
|
|
22350
|
-
return
|
|
22405
|
+
return join49(cursorHomeDir(), "commands", `${name}.md`);
|
|
22351
22406
|
}
|
|
22352
22407
|
}
|
|
22353
22408
|
function agentPath(tool, name) {
|
|
22354
22409
|
switch (tool) {
|
|
22355
22410
|
case "claude":
|
|
22356
|
-
return
|
|
22411
|
+
return join49(claudeBackend.agentsDir(), `${name}.md`);
|
|
22357
22412
|
case "agy":
|
|
22358
|
-
return
|
|
22413
|
+
return join49(join49(agyBackend.homeDir(), "agents"), `${name}.md`);
|
|
22359
22414
|
case "opencode":
|
|
22360
|
-
return
|
|
22415
|
+
return join49(opencodeBackend.agentsDir(), `${name}.md`);
|
|
22361
22416
|
case "codex":
|
|
22362
|
-
return
|
|
22417
|
+
return join49(codexBackend.agentsDir(), `${name}.toml`);
|
|
22363
22418
|
case "cursor":
|
|
22364
|
-
return
|
|
22419
|
+
return join49(cursorHomeDir(), "agents", `${name}.md`);
|
|
22365
22420
|
}
|
|
22366
22421
|
}
|
|
22367
22422
|
function instructionsFilename(tool) {
|
|
@@ -22386,21 +22441,21 @@ function instructionsFilename(tool) {
|
|
|
22386
22441
|
function instructionsPath(tool, projectPath) {
|
|
22387
22442
|
const file = instructionsFilename(tool);
|
|
22388
22443
|
if (projectPath)
|
|
22389
|
-
return
|
|
22444
|
+
return join49(projectPath, file);
|
|
22390
22445
|
switch (tool) {
|
|
22391
22446
|
case "claude":
|
|
22392
|
-
return
|
|
22447
|
+
return join49(claudeBackend.homeDir(), file);
|
|
22393
22448
|
case "agy":
|
|
22394
|
-
return
|
|
22449
|
+
return join49(agyBackend.homeDir(), file);
|
|
22395
22450
|
case "codex":
|
|
22396
|
-
return
|
|
22451
|
+
return join49(codexBackend.homeDir(), file);
|
|
22397
22452
|
case "opencode":
|
|
22398
|
-
return
|
|
22453
|
+
return join49(homedir18(), ".config", "opencode", file);
|
|
22399
22454
|
// Cursor has no single user-scope instructions FILE — user-level guidance
|
|
22400
22455
|
// is User Rules plus ~/.cursor/rules/*.mdc. AGENTS.md at the home root is
|
|
22401
22456
|
// the closest equivalent and is what a mirror can actually write.
|
|
22402
22457
|
case "cursor":
|
|
22403
|
-
return
|
|
22458
|
+
return join49(cursorHomeDir(), file);
|
|
22404
22459
|
}
|
|
22405
22460
|
}
|
|
22406
22461
|
function emit(type, art, toTool, projectPath) {
|
|
@@ -22464,9 +22519,9 @@ __export(toolkit_sync_exports, {
|
|
|
22464
22519
|
supportedTargetsFor: () => supportedTargetsFor,
|
|
22465
22520
|
writeMcpEntry: () => writeMcpEntry
|
|
22466
22521
|
});
|
|
22467
|
-
import { existsSync as
|
|
22468
|
-
import { dirname as
|
|
22469
|
-
import { homedir as
|
|
22522
|
+
import { existsSync as existsSync46, mkdirSync as mkdirSync15, readFileSync as readFileSync41, writeFileSync as writeFileSync15, cpSync as cpSync2 } from "fs";
|
|
22523
|
+
import { dirname as dirname22, join as join50, basename as basename23 } from "path";
|
|
22524
|
+
import { homedir as homedir19 } from "os";
|
|
22470
22525
|
function supportedTargetsFor(type) {
|
|
22471
22526
|
return SUPPORTED_TARGETS[type];
|
|
22472
22527
|
}
|
|
@@ -22507,26 +22562,26 @@ function skillsDirFor(tool) {
|
|
|
22507
22562
|
case "claude":
|
|
22508
22563
|
return claudeBackend.skillsDir();
|
|
22509
22564
|
case "agy":
|
|
22510
|
-
return
|
|
22565
|
+
return join50(agyBackend.homeDir(), "skills");
|
|
22511
22566
|
case "opencode":
|
|
22512
22567
|
return opencodeBackend.skillsDir();
|
|
22513
22568
|
case "codex":
|
|
22514
22569
|
return codexBackend.skillsDir();
|
|
22515
22570
|
// user skills, NOT .system
|
|
22516
22571
|
case "cursor":
|
|
22517
|
-
return
|
|
22572
|
+
return join50(cursorHomeDir(), "skills");
|
|
22518
22573
|
}
|
|
22519
22574
|
}
|
|
22520
22575
|
function copySkill(row, toTool) {
|
|
22521
|
-
const skillDir = row.extra.skillDir ||
|
|
22522
|
-
if (!
|
|
22576
|
+
const skillDir = row.extra.skillDir || dirname22(row.filePath);
|
|
22577
|
+
if (!existsSync46(skillDir))
|
|
22523
22578
|
return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
|
|
22524
22579
|
const name = row.extra.skillName || basename23(skillDir);
|
|
22525
|
-
const targetDir =
|
|
22526
|
-
if (
|
|
22580
|
+
const targetDir = join50(skillsDirFor(toTool), name);
|
|
22581
|
+
if (existsSync46(targetDir))
|
|
22527
22582
|
return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
|
|
22528
22583
|
try {
|
|
22529
|
-
|
|
22584
|
+
mkdirSync15(dirname22(targetDir), { recursive: true });
|
|
22530
22585
|
cpSync2(skillDir, targetDir, { recursive: true });
|
|
22531
22586
|
return { ok: true, targetPath: targetDir };
|
|
22532
22587
|
} catch (e) {
|
|
@@ -22534,28 +22589,28 @@ function copySkill(row, toTool) {
|
|
|
22534
22589
|
}
|
|
22535
22590
|
}
|
|
22536
22591
|
function readMcpEntry(tool, name) {
|
|
22537
|
-
const home =
|
|
22592
|
+
const home = homedir19();
|
|
22538
22593
|
const tries = [];
|
|
22539
22594
|
if (tool === "claude") {
|
|
22540
|
-
tries.push({ path:
|
|
22541
|
-
tries.push({ path:
|
|
22595
|
+
tries.push({ path: join50(home, ".mcp.json"), key: "mcpServers" });
|
|
22596
|
+
tries.push({ path: join50(home, ".claude.json"), key: "mcpServers" });
|
|
22542
22597
|
} else if (tool === "gemini") {
|
|
22543
|
-
tries.push({ path:
|
|
22598
|
+
tries.push({ path: join50(home, ".gemini", "settings.json"), key: "mcpServers" });
|
|
22544
22599
|
} else if (tool === "agy") {
|
|
22545
|
-
tries.push({ path:
|
|
22600
|
+
tries.push({ path: join50(home, ".gemini", "config", "mcp_config.json"), key: "mcpServers" });
|
|
22546
22601
|
} else if (tool === "cursor") {
|
|
22547
|
-
tries.push({ path:
|
|
22602
|
+
tries.push({ path: join50(cursorHomeDir(), "mcp.json"), key: "mcpServers" });
|
|
22548
22603
|
} else if (tool === "opencode") {
|
|
22549
|
-
tries.push({ path:
|
|
22550
|
-
tries.push({ path:
|
|
22551
|
-
tries.push({ path:
|
|
22604
|
+
tries.push({ path: join50(home, ".config", "opencode", "opencode.json"), key: "mcp" });
|
|
22605
|
+
tries.push({ path: join50(home, ".config", "opencode", "config.json"), key: "mcp" });
|
|
22606
|
+
tries.push({ path: join50(home, ".opencode", "config.json"), key: "mcp" });
|
|
22552
22607
|
} else if (tool === "codex") {
|
|
22553
|
-
const entry = readCodexMcpEntry(
|
|
22608
|
+
const entry = readCodexMcpEntry(join50(home, ".codex", "config.toml"), name);
|
|
22554
22609
|
if (entry)
|
|
22555
22610
|
return entry;
|
|
22556
22611
|
}
|
|
22557
22612
|
for (const t of tries) {
|
|
22558
|
-
if (!
|
|
22613
|
+
if (!existsSync46(t.path))
|
|
22559
22614
|
continue;
|
|
22560
22615
|
try {
|
|
22561
22616
|
const cfg = JSON.parse(readFileSync41(t.path, "utf-8"));
|
|
@@ -22568,7 +22623,7 @@ function readMcpEntry(tool, name) {
|
|
|
22568
22623
|
return null;
|
|
22569
22624
|
}
|
|
22570
22625
|
function readCodexMcpEntry(path, target) {
|
|
22571
|
-
if (!
|
|
22626
|
+
if (!existsSync46(path))
|
|
22572
22627
|
return null;
|
|
22573
22628
|
let content;
|
|
22574
22629
|
try {
|
|
@@ -22622,7 +22677,7 @@ function readCodexMcpEntry(path, target) {
|
|
|
22622
22677
|
}
|
|
22623
22678
|
function writeCodexMcpEntry(path, name, entry) {
|
|
22624
22679
|
let content = "";
|
|
22625
|
-
if (
|
|
22680
|
+
if (existsSync46(path))
|
|
22626
22681
|
content = readFileSync41(path, "utf-8");
|
|
22627
22682
|
const header = `[mcp_servers.${name}]`;
|
|
22628
22683
|
if (content.includes(header))
|
|
@@ -22652,14 +22707,14 @@ function writeCodexMcpEntry(path, name, entry) {
|
|
|
22652
22707
|
for (const [k, v] of Object.entries(entry.env))
|
|
22653
22708
|
lines.push(`${k} = ${escape(String(v))}`);
|
|
22654
22709
|
}
|
|
22655
|
-
|
|
22656
|
-
|
|
22710
|
+
mkdirSync15(dirname22(path), { recursive: true });
|
|
22711
|
+
writeFileSync15(path, content + lines.join("\n") + "\n");
|
|
22657
22712
|
return true;
|
|
22658
22713
|
}
|
|
22659
22714
|
function writeMcpEntry(toTool, name, entry) {
|
|
22660
|
-
const home =
|
|
22715
|
+
const home = homedir19();
|
|
22661
22716
|
if (toTool === "codex") {
|
|
22662
|
-
const path2 =
|
|
22717
|
+
const path2 = join50(home, ".codex", "config.toml");
|
|
22663
22718
|
if (!writeCodexMcpEntry(path2, name, entry))
|
|
22664
22719
|
return { ok: false, status: 409, error: `MCP "${name}" already exists in ${path2}.` };
|
|
22665
22720
|
return { ok: true, targetPath: path2 };
|
|
@@ -22667,27 +22722,27 @@ function writeMcpEntry(toTool, name, entry) {
|
|
|
22667
22722
|
let path;
|
|
22668
22723
|
let key2;
|
|
22669
22724
|
if (toTool === "claude") {
|
|
22670
|
-
path =
|
|
22725
|
+
path = join50(home, ".mcp.json");
|
|
22671
22726
|
key2 = "mcpServers";
|
|
22672
22727
|
} else if (toTool === "agy") {
|
|
22673
|
-
path =
|
|
22728
|
+
path = join50(home, ".gemini", "config", "mcp_config.json");
|
|
22674
22729
|
key2 = "mcpServers";
|
|
22675
22730
|
} else if (toTool === "cursor") {
|
|
22676
|
-
path =
|
|
22731
|
+
path = join50(cursorHomeDir(), "mcp.json");
|
|
22677
22732
|
key2 = "mcpServers";
|
|
22678
22733
|
} else {
|
|
22679
|
-
path =
|
|
22734
|
+
path = join50(home, ".config", "opencode", "opencode.json");
|
|
22680
22735
|
key2 = "mcp";
|
|
22681
22736
|
}
|
|
22682
22737
|
let cfg = {};
|
|
22683
|
-
if (
|
|
22738
|
+
if (existsSync46(path)) {
|
|
22684
22739
|
try {
|
|
22685
22740
|
cfg = JSON.parse(readFileSync41(path, "utf-8"));
|
|
22686
22741
|
} catch {
|
|
22687
22742
|
return { ok: false, status: 500, error: `Target config is malformed: ${path}` };
|
|
22688
22743
|
}
|
|
22689
22744
|
} else {
|
|
22690
|
-
|
|
22745
|
+
mkdirSync15(dirname22(path), { recursive: true });
|
|
22691
22746
|
}
|
|
22692
22747
|
cfg[key2] = cfg[key2] || {};
|
|
22693
22748
|
if (cfg[key2][name])
|
|
@@ -22714,7 +22769,7 @@ function writeMcpEntry(toTool, name, entry) {
|
|
|
22714
22769
|
}
|
|
22715
22770
|
cfg[key2][name] = normalized;
|
|
22716
22771
|
try {
|
|
22717
|
-
|
|
22772
|
+
writeFileSync15(path, JSON.stringify(cfg, null, 2));
|
|
22718
22773
|
return { ok: true, targetPath: path };
|
|
22719
22774
|
} catch (e) {
|
|
22720
22775
|
return { ok: false, status: 500, error: e instanceof Error ? e.message : "write failed" };
|
|
@@ -22744,11 +22799,11 @@ function copyCommandOrAgent(type, row, toTool) {
|
|
|
22744
22799
|
return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
|
|
22745
22800
|
}
|
|
22746
22801
|
const out = emit(type, art, toTool);
|
|
22747
|
-
if (
|
|
22802
|
+
if (existsSync46(out.path))
|
|
22748
22803
|
return { ok: false, status: 409, error: `Already exists: ${out.path}` };
|
|
22749
22804
|
try {
|
|
22750
|
-
|
|
22751
|
-
|
|
22805
|
+
mkdirSync15(dirname22(out.path), { recursive: true });
|
|
22806
|
+
writeFileSync15(out.path, out.content);
|
|
22752
22807
|
return { ok: true, targetPath: out.path };
|
|
22753
22808
|
} catch (e) {
|
|
22754
22809
|
return { ok: false, status: 500, error: e instanceof Error ? e.message : "write failed" };
|
|
@@ -22764,11 +22819,11 @@ function copyInstructions(row, toTool) {
|
|
|
22764
22819
|
return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
|
|
22765
22820
|
}
|
|
22766
22821
|
const out = emit("instructions", art, toTool, row.projectPath || void 0);
|
|
22767
|
-
if (
|
|
22822
|
+
if (existsSync46(out.path))
|
|
22768
22823
|
return { ok: false, status: 409, error: `Already exists: ${out.path}` };
|
|
22769
22824
|
try {
|
|
22770
|
-
|
|
22771
|
-
|
|
22825
|
+
mkdirSync15(dirname22(out.path), { recursive: true });
|
|
22826
|
+
writeFileSync15(out.path, out.content);
|
|
22772
22827
|
return { ok: true, targetPath: out.path };
|
|
22773
22828
|
} catch (e) {
|
|
22774
22829
|
return { ok: false, status: 500, error: e instanceof Error ? e.message : "write failed" };
|
|
@@ -22914,8 +22969,8 @@ __export(toolkit_pull_exports, {
|
|
|
22914
22969
|
planPull: () => planPull,
|
|
22915
22970
|
portableCommand: () => portableCommand
|
|
22916
22971
|
});
|
|
22917
|
-
import { existsSync as
|
|
22918
|
-
import { dirname as
|
|
22972
|
+
import { existsSync as existsSync47, mkdirSync as mkdirSync16, statSync as statSync41, writeFileSync as writeFileSync16 } from "node:fs";
|
|
22973
|
+
import { dirname as dirname23, join as join51, basename as basename24, isAbsolute as isAbsolute3 } from "node:path";
|
|
22919
22974
|
function isExecutableFile(p) {
|
|
22920
22975
|
try {
|
|
22921
22976
|
return statSync41(p).isFile();
|
|
@@ -23065,9 +23120,9 @@ function executePull(rows, opts = {}) {
|
|
|
23065
23120
|
const outcomes = [];
|
|
23066
23121
|
for (const { name, body, truncated, redacted, tools } of skills) {
|
|
23067
23122
|
for (const tool of tools) {
|
|
23068
|
-
const dir =
|
|
23069
|
-
const file =
|
|
23070
|
-
if (
|
|
23123
|
+
const dir = join51(skillsDirFor(tool), name);
|
|
23124
|
+
const file = join51(dir, "SKILL.md");
|
|
23125
|
+
if (existsSync47(file)) {
|
|
23071
23126
|
outcomes.push({ type: "skill", name, tool, status: "present" });
|
|
23072
23127
|
continue;
|
|
23073
23128
|
}
|
|
@@ -23080,8 +23135,8 @@ function executePull(rows, opts = {}) {
|
|
|
23080
23135
|
continue;
|
|
23081
23136
|
}
|
|
23082
23137
|
try {
|
|
23083
|
-
|
|
23084
|
-
|
|
23138
|
+
mkdirSync16(dir, { recursive: true });
|
|
23139
|
+
writeFileSync16(file, body);
|
|
23085
23140
|
outcomes.push({ type: "skill", name, tool, status: "written", path: file, redactedContent: redacted });
|
|
23086
23141
|
} catch (e) {
|
|
23087
23142
|
outcomes.push({ type: "skill", name, tool, status: "failed", reason: e instanceof Error ? e.message : "write failed" });
|
|
@@ -23112,7 +23167,7 @@ function executePull(rows, opts = {}) {
|
|
|
23112
23167
|
outcomes.push({ type, name, tool, status: "failed", reason: e instanceof Error ? e.message : "emit failed" });
|
|
23113
23168
|
continue;
|
|
23114
23169
|
}
|
|
23115
|
-
if (
|
|
23170
|
+
if (existsSync47(out.path)) {
|
|
23116
23171
|
outcomes.push({ type, name, tool, status: "present" });
|
|
23117
23172
|
continue;
|
|
23118
23173
|
}
|
|
@@ -23121,8 +23176,8 @@ function executePull(rows, opts = {}) {
|
|
|
23121
23176
|
continue;
|
|
23122
23177
|
}
|
|
23123
23178
|
try {
|
|
23124
|
-
|
|
23125
|
-
|
|
23179
|
+
mkdirSync16(dirname23(out.path), { recursive: true });
|
|
23180
|
+
writeFileSync16(out.path, out.content);
|
|
23126
23181
|
outcomes.push({ type, name, tool, status: "written", path: out.path, redactedContent: redacted });
|
|
23127
23182
|
} catch (e) {
|
|
23128
23183
|
outcomes.push({ type, name, tool, status: "failed", reason: e instanceof Error ? e.message : "write failed" });
|
|
@@ -23258,6 +23313,103 @@ var init_init_progress = __esm({
|
|
|
23258
23313
|
}
|
|
23259
23314
|
});
|
|
23260
23315
|
|
|
23316
|
+
// src/board-hooks.ts
|
|
23317
|
+
var board_hooks_exports = {};
|
|
23318
|
+
__export(board_hooks_exports, {
|
|
23319
|
+
BOARD_HOOKS: () => BOARD_HOOKS,
|
|
23320
|
+
boardHookCommand: () => boardHookCommand,
|
|
23321
|
+
boardHookEntries: () => boardHookEntries,
|
|
23322
|
+
isBoardHookEntry: () => isBoardHookEntry,
|
|
23323
|
+
mergeBoardHooks: () => mergeBoardHooks,
|
|
23324
|
+
registerBoardHooks: () => registerBoardHooks,
|
|
23325
|
+
removeBoardHooks: () => removeBoardHooks,
|
|
23326
|
+
resolveCliEntry: () => resolveCliEntry
|
|
23327
|
+
});
|
|
23328
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync17, readFileSync as readFileSync42, writeFileSync as writeFileSync17 } from "node:fs";
|
|
23329
|
+
import { dirname as dirname24, join as join52 } from "node:path";
|
|
23330
|
+
function boardHookCommand(execPath, cliEntry, mode) {
|
|
23331
|
+
return `"${execPath}" "${cliEntry}" task-hook --${mode}`;
|
|
23332
|
+
}
|
|
23333
|
+
function isBoardHookEntry(entry) {
|
|
23334
|
+
const cmd = entry?.hooks?.[0]?.command;
|
|
23335
|
+
if (typeof cmd !== "string") return false;
|
|
23336
|
+
if (cmd.includes("chat_recall_task_hook.sh")) return true;
|
|
23337
|
+
return cmd.includes("task-hook") && (cmd.includes("--claim") || cmd.includes("--close"));
|
|
23338
|
+
}
|
|
23339
|
+
function boardHookEntries(execPath, cliEntry) {
|
|
23340
|
+
return BOARD_HOOKS.map(({ event, mode }) => [
|
|
23341
|
+
event,
|
|
23342
|
+
{ matcher: "", hooks: [{ type: "command", command: boardHookCommand(execPath, cliEntry, mode) }] }
|
|
23343
|
+
]);
|
|
23344
|
+
}
|
|
23345
|
+
function mergeBoardHooks(config2, execPath, cliEntry) {
|
|
23346
|
+
if (!config2.hooks || typeof config2.hooks !== "object") config2.hooks = {};
|
|
23347
|
+
const entries = boardHookEntries(execPath, cliEntry);
|
|
23348
|
+
for (const { event } of BOARD_HOOKS) {
|
|
23349
|
+
const current = Array.isArray(config2.hooks[event]) ? config2.hooks[event] : [];
|
|
23350
|
+
const kept = current.filter((h) => !isBoardHookEntry(h));
|
|
23351
|
+
for (const [ev, entry] of entries) if (ev === event) kept.push(entry);
|
|
23352
|
+
config2.hooks[event] = kept;
|
|
23353
|
+
}
|
|
23354
|
+
}
|
|
23355
|
+
function removeBoardHooks(config2) {
|
|
23356
|
+
if (!config2.hooks) return 0;
|
|
23357
|
+
let removed = 0;
|
|
23358
|
+
for (const { event } of BOARD_HOOKS) {
|
|
23359
|
+
const current = Array.isArray(config2.hooks[event]) ? config2.hooks[event] : [];
|
|
23360
|
+
const kept = current.filter((h) => !isBoardHookEntry(h));
|
|
23361
|
+
removed += current.length - kept.length;
|
|
23362
|
+
if (kept.length) config2.hooks[event] = kept;
|
|
23363
|
+
else delete config2.hooks[event];
|
|
23364
|
+
}
|
|
23365
|
+
return removed;
|
|
23366
|
+
}
|
|
23367
|
+
function readConfig(file) {
|
|
23368
|
+
if (!existsSync48(file)) return { hooks: {} };
|
|
23369
|
+
try {
|
|
23370
|
+
const parsed = JSON.parse(readFileSync42(file, "utf-8"));
|
|
23371
|
+
if (!parsed.hooks || typeof parsed.hooks !== "object") parsed.hooks = {};
|
|
23372
|
+
return parsed;
|
|
23373
|
+
} catch {
|
|
23374
|
+
return { hooks: {} };
|
|
23375
|
+
}
|
|
23376
|
+
}
|
|
23377
|
+
function registerBoardHooks(files, execPath, cliEntry) {
|
|
23378
|
+
const written = [];
|
|
23379
|
+
for (const file of files) {
|
|
23380
|
+
if (existsSync48(file)) {
|
|
23381
|
+
try {
|
|
23382
|
+
JSON.parse(readFileSync42(file, "utf-8"));
|
|
23383
|
+
} catch {
|
|
23384
|
+
continue;
|
|
23385
|
+
}
|
|
23386
|
+
}
|
|
23387
|
+
const config2 = readConfig(file);
|
|
23388
|
+
mergeBoardHooks(config2, execPath, cliEntry);
|
|
23389
|
+
try {
|
|
23390
|
+
mkdirSync17(dirname24(file), { recursive: true });
|
|
23391
|
+
writeFileSync17(file, JSON.stringify(config2, null, 2) + "\n");
|
|
23392
|
+
written.push(file);
|
|
23393
|
+
} catch {
|
|
23394
|
+
}
|
|
23395
|
+
}
|
|
23396
|
+
return written;
|
|
23397
|
+
}
|
|
23398
|
+
function resolveCliEntry(distDir) {
|
|
23399
|
+
const bundled = join52(distDir, "cli.js");
|
|
23400
|
+
return existsSync48(bundled) ? bundled : join52(distDir, "cli.ts");
|
|
23401
|
+
}
|
|
23402
|
+
var BOARD_HOOKS;
|
|
23403
|
+
var init_board_hooks = __esm({
|
|
23404
|
+
"src/board-hooks.ts"() {
|
|
23405
|
+
"use strict";
|
|
23406
|
+
BOARD_HOOKS = [
|
|
23407
|
+
{ event: "UserPromptSubmit", mode: "claim" },
|
|
23408
|
+
{ event: "SessionEnd", mode: "close" }
|
|
23409
|
+
];
|
|
23410
|
+
}
|
|
23411
|
+
});
|
|
23412
|
+
|
|
23261
23413
|
// src/repair.ts
|
|
23262
23414
|
var repair_exports = {};
|
|
23263
23415
|
__export(repair_exports, {
|
|
@@ -23613,6 +23765,222 @@ var init_escalate = __esm({
|
|
|
23613
23765
|
}
|
|
23614
23766
|
});
|
|
23615
23767
|
|
|
23768
|
+
// ../engine/dist/core/guard-registration.js
|
|
23769
|
+
var guard_registration_exports = {};
|
|
23770
|
+
__export(guard_registration_exports, {
|
|
23771
|
+
GUARD_ID: () => GUARD_ID,
|
|
23772
|
+
guardCommand: () => guardCommand,
|
|
23773
|
+
guardTargets: () => guardTargets,
|
|
23774
|
+
guardWrapperName: () => guardWrapperName,
|
|
23775
|
+
guardWrapperSource: () => guardWrapperSource,
|
|
23776
|
+
opencodeConfigDir: () => opencodeConfigDir,
|
|
23777
|
+
opencodePluginDir: () => opencodePluginDir,
|
|
23778
|
+
opencodePluginSource: () => opencodePluginSource,
|
|
23779
|
+
registerGuard: () => registerGuard,
|
|
23780
|
+
unregisterGuard: () => unregisterGuard
|
|
23781
|
+
});
|
|
23782
|
+
import { homedir as homedir20 } from "os";
|
|
23783
|
+
import { join as join53 } from "path";
|
|
23784
|
+
function opencodeConfigDir() {
|
|
23785
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
23786
|
+
const base3 = xdg && xdg.trim() ? xdg : join53(homedir20(), ".config");
|
|
23787
|
+
return join53(base3, "opencode");
|
|
23788
|
+
}
|
|
23789
|
+
function opencodePluginDir() {
|
|
23790
|
+
return join53(opencodeConfigDir(), "plugins");
|
|
23791
|
+
}
|
|
23792
|
+
function guardTargets() {
|
|
23793
|
+
return [
|
|
23794
|
+
{
|
|
23795
|
+
tool: "codex",
|
|
23796
|
+
label: "Codex",
|
|
23797
|
+
configPath: join53(codexBackend.homeDir(), "hooks.json"),
|
|
23798
|
+
homeDir: codexBackend.homeDir(),
|
|
23799
|
+
// Codex will not run a hook until a person has reviewed it, and the
|
|
23800
|
+
// approval is bound to the hook's hash. There is no way to grant it from
|
|
23801
|
+
// outside: /etc/codex/requirements.toml is the root-owned admin policy,
|
|
23802
|
+
// and the only other route is the per-invocation
|
|
23803
|
+
// --dangerously-bypass-hook-trust flag. An installer that could grant its
|
|
23804
|
+
// own approval is the thing the gate exists to stop.
|
|
23805
|
+
note: "Codex asks you to approve a hook before it runs one. Run /hooks in Codex once to approve this."
|
|
23806
|
+
},
|
|
23807
|
+
{
|
|
23808
|
+
tool: "agy",
|
|
23809
|
+
label: "Antigravity",
|
|
23810
|
+
configPath: join53(agyBackend.homeDir(), "hooks.json"),
|
|
23811
|
+
homeDir: agyBackend.homeDir()
|
|
23812
|
+
},
|
|
23813
|
+
{
|
|
23814
|
+
tool: "cursor",
|
|
23815
|
+
label: "Cursor",
|
|
23816
|
+
configPath: join53(cursorBackend.homeDir(), "hooks.json"),
|
|
23817
|
+
homeDir: cursorBackend.homeDir(),
|
|
23818
|
+
note: "Cursor checks a shell command before it runs and a file edit after it is written."
|
|
23819
|
+
},
|
|
23820
|
+
{
|
|
23821
|
+
tool: "opencode",
|
|
23822
|
+
label: "OpenCode",
|
|
23823
|
+
configPath: join53(opencodePluginDir(), "chat-recall-guard.js"),
|
|
23824
|
+
homeDir: opencodeConfigDir(),
|
|
23825
|
+
note: "OpenCode plugin hooks do not fire for subagent tool calls, so a delegating agent bypasses this."
|
|
23826
|
+
}
|
|
23827
|
+
];
|
|
23828
|
+
}
|
|
23829
|
+
function guardWrapperName(tool) {
|
|
23830
|
+
return `chat_recall_guard_${tool}.sh`;
|
|
23831
|
+
}
|
|
23832
|
+
function guardWrapperSource(tool, hookPath) {
|
|
23833
|
+
return `#!/bin/sh
|
|
23834
|
+
# Written by \`chat-recall install-hooks\`. Re-running replaces it.
|
|
23835
|
+
CHAT_RECALL_GUARD_HARNESS=${tool}
|
|
23836
|
+
export CHAT_RECALL_GUARD_HARNESS
|
|
23837
|
+
exec ${JSON.stringify(hookPath)} "$@"
|
|
23838
|
+
`;
|
|
23839
|
+
}
|
|
23840
|
+
function guardCommand(wrapperPath) {
|
|
23841
|
+
return wrapperPath.includes(" ") ? `"${wrapperPath}"` : wrapperPath;
|
|
23842
|
+
}
|
|
23843
|
+
function mergeCodex(config2, command) {
|
|
23844
|
+
const c = config2 && typeof config2 === "object" ? { ...config2 } : {};
|
|
23845
|
+
const hooks = c.hooks && typeof c.hooks === "object" ? { ...c.hooks } : {};
|
|
23846
|
+
const kept = asArray2(hooks.PreToolUse).filter((e) => !asArray2(e?.hooks).some((h) => isOurs(h?.command)));
|
|
23847
|
+
kept.push({
|
|
23848
|
+
matcher: CODEX_MATCHER,
|
|
23849
|
+
hooks: [{ type: "command", command, statusMessage: "chat-recall: checking decisions", timeout: TIMEOUT_SECS }]
|
|
23850
|
+
});
|
|
23851
|
+
hooks.PreToolUse = kept;
|
|
23852
|
+
c.hooks = hooks;
|
|
23853
|
+
return c;
|
|
23854
|
+
}
|
|
23855
|
+
function stripCodex(config2) {
|
|
23856
|
+
const c = config2 && typeof config2 === "object" ? { ...config2 } : {};
|
|
23857
|
+
const hooks = c.hooks && typeof c.hooks === "object" ? { ...c.hooks } : {};
|
|
23858
|
+
const before = asArray2(hooks.PreToolUse);
|
|
23859
|
+
const kept = before.filter((e) => !asArray2(e?.hooks).some((h) => isOurs(h?.command)));
|
|
23860
|
+
if (kept.length)
|
|
23861
|
+
hooks.PreToolUse = kept;
|
|
23862
|
+
else
|
|
23863
|
+
delete hooks.PreToolUse;
|
|
23864
|
+
c.hooks = hooks;
|
|
23865
|
+
return { config: c, removed: before.length - kept.length };
|
|
23866
|
+
}
|
|
23867
|
+
function mergeAgy(config2, command) {
|
|
23868
|
+
const c = config2 && typeof config2 === "object" ? { ...config2 } : {};
|
|
23869
|
+
c[GUARD_ID] = {
|
|
23870
|
+
enabled: true,
|
|
23871
|
+
PreToolUse: [{
|
|
23872
|
+
matcher: AGY_MATCHER,
|
|
23873
|
+
hooks: [{ type: "command", command, timeout: TIMEOUT_SECS }]
|
|
23874
|
+
}]
|
|
23875
|
+
};
|
|
23876
|
+
return c;
|
|
23877
|
+
}
|
|
23878
|
+
function stripAgy(config2) {
|
|
23879
|
+
const c = config2 && typeof config2 === "object" ? { ...config2 } : {};
|
|
23880
|
+
const had = Object.prototype.hasOwnProperty.call(c, GUARD_ID) ? 1 : 0;
|
|
23881
|
+
delete c[GUARD_ID];
|
|
23882
|
+
return { config: c, removed: had };
|
|
23883
|
+
}
|
|
23884
|
+
function mergeCursor(config2, command) {
|
|
23885
|
+
const c = config2 && typeof config2 === "object" ? { ...config2 } : {};
|
|
23886
|
+
c.version = typeof c.version === "number" ? c.version : 1;
|
|
23887
|
+
const hooks = c.hooks && typeof c.hooks === "object" ? { ...c.hooks } : {};
|
|
23888
|
+
for (const event of ["beforeShellExecution", "afterFileEdit"]) {
|
|
23889
|
+
const kept = asArray2(hooks[event]).filter((e) => !isOurs(e?.command));
|
|
23890
|
+
kept.push({ command });
|
|
23891
|
+
hooks[event] = kept;
|
|
23892
|
+
}
|
|
23893
|
+
c.hooks = hooks;
|
|
23894
|
+
return c;
|
|
23895
|
+
}
|
|
23896
|
+
function stripCursor(config2) {
|
|
23897
|
+
const c = config2 && typeof config2 === "object" ? { ...config2 } : {};
|
|
23898
|
+
const hooks = c.hooks && typeof c.hooks === "object" ? { ...c.hooks } : {};
|
|
23899
|
+
let removed = 0;
|
|
23900
|
+
for (const event of ["beforeShellExecution", "afterFileEdit"]) {
|
|
23901
|
+
const before = asArray2(hooks[event]);
|
|
23902
|
+
const kept = before.filter((e) => !isOurs(e?.command));
|
|
23903
|
+
removed += before.length - kept.length;
|
|
23904
|
+
if (kept.length)
|
|
23905
|
+
hooks[event] = kept;
|
|
23906
|
+
else
|
|
23907
|
+
delete hooks[event];
|
|
23908
|
+
}
|
|
23909
|
+
c.hooks = hooks;
|
|
23910
|
+
return { config: c, removed };
|
|
23911
|
+
}
|
|
23912
|
+
function registerGuard(tool, config2, command) {
|
|
23913
|
+
switch (tool) {
|
|
23914
|
+
case "codex":
|
|
23915
|
+
return mergeCodex(config2, command);
|
|
23916
|
+
case "agy":
|
|
23917
|
+
return mergeAgy(config2, command);
|
|
23918
|
+
case "cursor":
|
|
23919
|
+
return mergeCursor(config2, command);
|
|
23920
|
+
}
|
|
23921
|
+
}
|
|
23922
|
+
function unregisterGuard(tool, config2) {
|
|
23923
|
+
switch (tool) {
|
|
23924
|
+
case "codex":
|
|
23925
|
+
return stripCodex(config2);
|
|
23926
|
+
case "agy":
|
|
23927
|
+
return stripAgy(config2);
|
|
23928
|
+
case "cursor":
|
|
23929
|
+
return stripCursor(config2);
|
|
23930
|
+
}
|
|
23931
|
+
}
|
|
23932
|
+
function opencodePluginSource(hookPath) {
|
|
23933
|
+
return `// Written by \`chat-recall install-hooks\`. Re-running replaces it.
|
|
23934
|
+
//
|
|
23935
|
+
// Sends each tool call to the same guard the other harnesses run, and prints
|
|
23936
|
+
// what it says. Blocking is opt-in: set CHAT_RECALL_GUARD_ENFORCE=1 and a
|
|
23937
|
+
// finding throws, which is how an OpenCode plugin stops a call.
|
|
23938
|
+
import { spawn } from "child_process";
|
|
23939
|
+
|
|
23940
|
+
const HOOK = ${JSON.stringify(hookPath)};
|
|
23941
|
+
|
|
23942
|
+
function ask(payload) {
|
|
23943
|
+
return new Promise((resolve) => {
|
|
23944
|
+
let out = "";
|
|
23945
|
+
const child = spawn(HOOK, {
|
|
23946
|
+
env: { ...process.env, CHAT_RECALL_GUARD_HARNESS: "opencode" },
|
|
23947
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
23948
|
+
});
|
|
23949
|
+
const done = setTimeout(() => { child.kill(); resolve(null); }, ${TIMEOUT_SECS * 1e3});
|
|
23950
|
+
child.stdout.on("data", (c) => { out += c; });
|
|
23951
|
+
child.on("error", () => { clearTimeout(done); resolve(null); });
|
|
23952
|
+
child.on("close", () => {
|
|
23953
|
+
clearTimeout(done);
|
|
23954
|
+
try { resolve(out.trim() ? JSON.parse(out) : null); } catch { resolve(null); }
|
|
23955
|
+
});
|
|
23956
|
+
child.stdin.end(JSON.stringify(payload));
|
|
23957
|
+
});
|
|
23958
|
+
}
|
|
23959
|
+
|
|
23960
|
+
export const ChatRecallGuard = async () => ({
|
|
23961
|
+
"tool.execute.before": async (input, output) => {
|
|
23962
|
+
const verdict = await ask({ tool: input?.tool, args: output?.args });
|
|
23963
|
+
if (!verdict || !verdict.message) return;
|
|
23964
|
+
if (verdict.block) throw new Error(verdict.message);
|
|
23965
|
+
console.error(verdict.message);
|
|
23966
|
+
},
|
|
23967
|
+
});
|
|
23968
|
+
`;
|
|
23969
|
+
}
|
|
23970
|
+
var GUARD_ID, AGY_MATCHER, CODEX_MATCHER, TIMEOUT_SECS, isOurs, asArray2;
|
|
23971
|
+
var init_guard_registration = __esm({
|
|
23972
|
+
"../engine/dist/core/guard-registration.js"() {
|
|
23973
|
+
"use strict";
|
|
23974
|
+
init_backends();
|
|
23975
|
+
GUARD_ID = "chat-recall-guard";
|
|
23976
|
+
AGY_MATCHER = "run_command|write_to_file|replace_file_content|multi_replace_file_content";
|
|
23977
|
+
CODEX_MATCHER = "Bash|apply_patch|Edit|Write";
|
|
23978
|
+
TIMEOUT_SECS = 8;
|
|
23979
|
+
isOurs = (cmd) => typeof cmd === "string" && cmd.includes("chat_recall_guard");
|
|
23980
|
+
asArray2 = (v) => Array.isArray(v) ? v : [];
|
|
23981
|
+
}
|
|
23982
|
+
});
|
|
23983
|
+
|
|
23616
23984
|
// src/verify-deep.ts
|
|
23617
23985
|
var verify_deep_exports = {};
|
|
23618
23986
|
__export(verify_deep_exports, {
|
|
@@ -23681,11 +24049,11 @@ var verify_repair_exports = {};
|
|
|
23681
24049
|
__export(verify_repair_exports, {
|
|
23682
24050
|
forceFullResync: () => forceFullResync
|
|
23683
24051
|
});
|
|
23684
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
24052
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync49 } from "fs";
|
|
23685
24053
|
function forceFullResync(server, sessionIds) {
|
|
23686
24054
|
if (sessionIds.length === 0) return 0;
|
|
23687
24055
|
const path = ledgerFilePath();
|
|
23688
|
-
if (
|
|
24056
|
+
if (existsSync49(path)) {
|
|
23689
24057
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/-/g, "");
|
|
23690
24058
|
try {
|
|
23691
24059
|
copyFileSync3(path, `${path}.bak-${stamp}`);
|
|
@@ -23875,8 +24243,8 @@ __export(collector_exports, {
|
|
|
23875
24243
|
});
|
|
23876
24244
|
import { spawn as spawn3 } from "node:child_process";
|
|
23877
24245
|
import { execSync as execSync4 } from "node:child_process";
|
|
23878
|
-
import { readFileSync as
|
|
23879
|
-
import { join as
|
|
24246
|
+
import { readFileSync as readFileSync43 } from "node:fs";
|
|
24247
|
+
import { join as join54, basename as basename26 } from "node:path";
|
|
23880
24248
|
async function resolveCodeindexBin(autoInstall = true) {
|
|
23881
24249
|
let st = checkCodeindexStatus();
|
|
23882
24250
|
if (!st.installed && autoInstall)
|
|
@@ -24055,7 +24423,7 @@ async function collectCode(opts) {
|
|
|
24055
24423
|
}
|
|
24056
24424
|
const complexity = (r) => {
|
|
24057
24425
|
try {
|
|
24058
|
-
const txt =
|
|
24426
|
+
const txt = readFileSync43(join54(ws, r), "utf8");
|
|
24059
24427
|
if (txt.length > 2e6)
|
|
24060
24428
|
return 1;
|
|
24061
24429
|
const m = txt.match(CF_RE);
|
|
@@ -24839,9 +25207,9 @@ __export(project_tasks_exports, {
|
|
|
24839
25207
|
pushProjectTaskStatuses: () => pushProjectTaskStatuses,
|
|
24840
25208
|
renderTeamTasksMd: () => renderTeamTasksMd
|
|
24841
25209
|
});
|
|
24842
|
-
import { existsSync as
|
|
24843
|
-
import { join as
|
|
24844
|
-
import { homedir as
|
|
25210
|
+
import { existsSync as existsSync50, readFileSync as readFileSync44, writeFileSync as writeFileSync18, mkdirSync as mkdirSync18, statSync as statSync42 } from "node:fs";
|
|
25211
|
+
import { join as join55, dirname as dirname25 } from "node:path";
|
|
25212
|
+
import { homedir as homedir21 } from "node:os";
|
|
24845
25213
|
function isTeamTaskStatus(v) {
|
|
24846
25214
|
return !!v && TEAM_STATUSES.has(v);
|
|
24847
25215
|
}
|
|
@@ -24891,22 +25259,22 @@ function parseTaskFile(content) {
|
|
|
24891
25259
|
return out;
|
|
24892
25260
|
}
|
|
24893
25261
|
function ledgerPath2() {
|
|
24894
|
-
return
|
|
25262
|
+
return join55(homedir21(), ".chat-recall", "project-tasks-ledger.json");
|
|
24895
25263
|
}
|
|
24896
25264
|
function readLedger() {
|
|
24897
25265
|
try {
|
|
24898
|
-
return JSON.parse(
|
|
25266
|
+
return JSON.parse(readFileSync44(ledgerPath2(), "utf8"));
|
|
24899
25267
|
} catch {
|
|
24900
25268
|
return {};
|
|
24901
25269
|
}
|
|
24902
25270
|
}
|
|
24903
25271
|
function writeLedger(l) {
|
|
24904
25272
|
try {
|
|
24905
|
-
|
|
25273
|
+
mkdirSync18(dirname25(ledgerPath2()), { recursive: true });
|
|
24906
25274
|
} catch {
|
|
24907
25275
|
}
|
|
24908
25276
|
try {
|
|
24909
|
-
|
|
25277
|
+
writeFileSync18(ledgerPath2(), JSON.stringify(l));
|
|
24910
25278
|
} catch {
|
|
24911
25279
|
}
|
|
24912
25280
|
}
|
|
@@ -24924,8 +25292,8 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
24924
25292
|
continue;
|
|
24925
25293
|
}
|
|
24926
25294
|
for (const project of projects) {
|
|
24927
|
-
const file =
|
|
24928
|
-
if (!
|
|
25295
|
+
const file = join55(project, fk.filename);
|
|
25296
|
+
if (!existsSync50(file)) continue;
|
|
24929
25297
|
let mtime;
|
|
24930
25298
|
try {
|
|
24931
25299
|
mtime = statSync42(file).mtimeMs;
|
|
@@ -24935,7 +25303,7 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
24935
25303
|
if (ledger[file] === mtime) continue;
|
|
24936
25304
|
let changed;
|
|
24937
25305
|
try {
|
|
24938
|
-
changed = parseTaskFile(
|
|
25306
|
+
changed = parseTaskFile(readFileSync44(file, "utf8")).filter((t) => t.kind === fk.kind);
|
|
24939
25307
|
} catch {
|
|
24940
25308
|
continue;
|
|
24941
25309
|
}
|
|
@@ -25007,10 +25375,10 @@ __export(intent_drain_exports, {
|
|
|
25007
25375
|
drainSyncIntents: () => drainSyncIntents,
|
|
25008
25376
|
instructionTargets: () => instructionTargets
|
|
25009
25377
|
});
|
|
25010
|
-
import { existsSync as
|
|
25011
|
-
import { join as
|
|
25378
|
+
import { existsSync as existsSync51, readFileSync as readFileSync45, writeFileSync as writeFileSync19, appendFileSync, mkdirSync as mkdirSync19 } from "node:fs";
|
|
25379
|
+
import { join as join56 } from "node:path";
|
|
25012
25380
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
25013
|
-
import { dirname as
|
|
25381
|
+
import { dirname as dirname26 } from "node:path";
|
|
25014
25382
|
function instructionTargets(rootPath, isGlobal) {
|
|
25015
25383
|
const installed = INSTRUCTION_TOOLS.filter((t) => {
|
|
25016
25384
|
try {
|
|
@@ -25022,17 +25390,17 @@ function instructionTargets(rootPath, isGlobal) {
|
|
|
25022
25390
|
const tools = installed.length ? installed : [INSTRUCTION_TOOLS[0]];
|
|
25023
25391
|
if (isGlobal) return [...new Set(tools.map((t) => instructionsPath(t.id)))];
|
|
25024
25392
|
if (!rootPath) return [];
|
|
25025
|
-
const present = INSTRUCTION_FILENAMES.map((f) =>
|
|
25393
|
+
const present = INSTRUCTION_FILENAMES.map((f) => join56(rootPath, f)).filter((p) => existsSync51(p));
|
|
25026
25394
|
if (present.length) return present;
|
|
25027
|
-
return [...new Set(tools.map((t) =>
|
|
25395
|
+
return [...new Set(tools.map((t) => join56(rootPath, instructionsFilename(t.id))))];
|
|
25028
25396
|
}
|
|
25029
25397
|
function applyCodeRecommendation(intent) {
|
|
25030
25398
|
try {
|
|
25031
25399
|
const meta = JSON.parse(intent.name || "{}");
|
|
25032
25400
|
if (intent.artifact_type === "write_tasks_file") {
|
|
25033
25401
|
if (!meta.rootPath || !meta.content) return { status: "error", result: JSON.stringify({ error: "missing rootPath or content" }) };
|
|
25034
|
-
const file =
|
|
25035
|
-
|
|
25402
|
+
const file = join56(meta.rootPath, meta.filename || "CODE_TASKS.md");
|
|
25403
|
+
writeFileSync19(file, meta.content);
|
|
25036
25404
|
return { status: "done", result: JSON.stringify({ wrote: file }) };
|
|
25037
25405
|
}
|
|
25038
25406
|
if (intent.artifact_type === "append_claude_md") {
|
|
@@ -25047,10 +25415,10 @@ function applyCodeRecommendation(intent) {
|
|
|
25047
25415
|
for (const file of targets) {
|
|
25048
25416
|
const dir = file.slice(0, file.lastIndexOf("/"));
|
|
25049
25417
|
try {
|
|
25050
|
-
|
|
25418
|
+
mkdirSync19(dir, { recursive: true });
|
|
25051
25419
|
} catch {
|
|
25052
25420
|
}
|
|
25053
|
-
const existing =
|
|
25421
|
+
const existing = existsSync51(file) ? readFileSync45(file, "utf8") : "";
|
|
25054
25422
|
if (existing.includes(text)) {
|
|
25055
25423
|
skipped.push(file);
|
|
25056
25424
|
continue;
|
|
@@ -25060,7 +25428,7 @@ function applyCodeRecommendation(intent) {
|
|
|
25060
25428
|
${text}
|
|
25061
25429
|
`;
|
|
25062
25430
|
if (existing) appendFileSync(file, block);
|
|
25063
|
-
else
|
|
25431
|
+
else writeFileSync19(file, `# ${meta.rootPath?.split("/").pop() || "Global"} instructions
|
|
25064
25432
|
${block}`);
|
|
25065
25433
|
appended.push(file);
|
|
25066
25434
|
}
|
|
@@ -25224,11 +25592,11 @@ var init_intent_drain = __esm({
|
|
|
25224
25592
|
];
|
|
25225
25593
|
ownVersion2 = "0.0.0";
|
|
25226
25594
|
try {
|
|
25227
|
-
const dir =
|
|
25228
|
-
ownVersion2 = JSON.parse(
|
|
25595
|
+
const dir = dirname26(fileURLToPath4(import.meta.url));
|
|
25596
|
+
ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
25229
25597
|
} catch {
|
|
25230
25598
|
try {
|
|
25231
|
-
if (true) ownVersion2 = "0.6.
|
|
25599
|
+
if (true) ownVersion2 = "0.6.7";
|
|
25232
25600
|
} catch {
|
|
25233
25601
|
}
|
|
25234
25602
|
}
|
|
@@ -25429,48 +25797,48 @@ var team_merge_exports = {};
|
|
|
25429
25797
|
__export(team_merge_exports, {
|
|
25430
25798
|
mergePullResult: () => mergePullResult
|
|
25431
25799
|
});
|
|
25432
|
-
import { existsSync as
|
|
25433
|
-
import { dirname as
|
|
25800
|
+
import { existsSync as existsSync52, mkdirSync as mkdirSync20, readFileSync as readFileSync46, rmSync as rmSync8, writeFileSync as writeFileSync20, statSync as statSync43 } from "fs";
|
|
25801
|
+
import { dirname as dirname27, join as join57 } from "path";
|
|
25434
25802
|
import { createHash as createHash14 } from "crypto";
|
|
25435
25803
|
function installPathFor(art, tool) {
|
|
25436
25804
|
switch (art.type) {
|
|
25437
25805
|
case "skill":
|
|
25438
25806
|
switch (tool) {
|
|
25439
25807
|
case "claude":
|
|
25440
|
-
return
|
|
25808
|
+
return join57(claudeBackend.skillsDir(), art.name, "SKILL.md");
|
|
25441
25809
|
// Antigravity's OWN skills dir. It used to resolve to the shared
|
|
25442
25810
|
// ~/.gemini/skills while toolkit-sync installed to
|
|
25443
25811
|
// ~/.gemini/antigravity-cli/skills, so a team skill landed in one place
|
|
25444
25812
|
// and a synced skill in the other.
|
|
25445
25813
|
case "agy":
|
|
25446
|
-
return
|
|
25814
|
+
return join57(agyBackend.homeDir(), "skills", art.name, "SKILL.md");
|
|
25447
25815
|
case "opencode":
|
|
25448
|
-
return
|
|
25816
|
+
return join57(opencodeBackend.skillsDir(), art.name, "SKILL.md");
|
|
25449
25817
|
// User skills go to ~/.codex/skills (NOT .system, which is OpenAI's bundle).
|
|
25450
25818
|
case "codex":
|
|
25451
|
-
return
|
|
25819
|
+
return join57(codexBackend.skillsDir(), art.name, "SKILL.md");
|
|
25452
25820
|
case "cursor":
|
|
25453
|
-
return
|
|
25821
|
+
return join57(cursorHomeDir(), "skills", art.name, "SKILL.md");
|
|
25454
25822
|
}
|
|
25455
25823
|
break;
|
|
25456
25824
|
case "agent":
|
|
25457
25825
|
if (tool === "claude")
|
|
25458
|
-
return
|
|
25826
|
+
return join57(claudeBackend.agentsDir(), `${art.name}.md`);
|
|
25459
25827
|
return null;
|
|
25460
25828
|
case "command":
|
|
25461
25829
|
if (tool === "claude")
|
|
25462
|
-
return
|
|
25830
|
+
return join57(claudeBackend.commandsDir(), `${art.name}.md`);
|
|
25463
25831
|
return null;
|
|
25464
25832
|
case "hook":
|
|
25465
25833
|
if (tool === "claude")
|
|
25466
|
-
return
|
|
25834
|
+
return join57(claudeBackend.homeDir(), "chat-recall-hooks", `${art.name}.json`);
|
|
25467
25835
|
return null;
|
|
25468
25836
|
case "plan":
|
|
25469
25837
|
switch (tool) {
|
|
25470
25838
|
case "claude":
|
|
25471
|
-
return
|
|
25839
|
+
return join57(claudeBackend.plansDir(), `${art.name}.md`);
|
|
25472
25840
|
case "opencode":
|
|
25473
|
-
return
|
|
25841
|
+
return join57(opencodeBackend.plansDir(), `${art.name}.md`);
|
|
25474
25842
|
case "agy":
|
|
25475
25843
|
return null;
|
|
25476
25844
|
// Antigravity plans live per-session
|
|
@@ -25481,11 +25849,11 @@ function installPathFor(art, tool) {
|
|
|
25481
25849
|
}
|
|
25482
25850
|
break;
|
|
25483
25851
|
case "instructions":
|
|
25484
|
-
return
|
|
25852
|
+
return join57(getDataDir(), "team-instructions", tool, `${art.name}.md`);
|
|
25485
25853
|
case "mcp":
|
|
25486
|
-
return
|
|
25854
|
+
return join57(getDataDir(), "team-mcps", `${art.name}.json`);
|
|
25487
25855
|
case "plugin":
|
|
25488
|
-
return
|
|
25856
|
+
return join57(getDataDir(), "team-plugins", tool, art.name, "manifest.json");
|
|
25489
25857
|
}
|
|
25490
25858
|
return null;
|
|
25491
25859
|
}
|
|
@@ -25524,8 +25892,8 @@ async function mergePullResult(opts) {
|
|
|
25524
25892
|
pending.push({ artifactId: a.id, tool, artifactType: a.type, artifactName: a.name, sha256: sha, installedAt: Date.now() });
|
|
25525
25893
|
continue;
|
|
25526
25894
|
}
|
|
25527
|
-
|
|
25528
|
-
|
|
25895
|
+
mkdirSync20(dirname27(path), { recursive: true });
|
|
25896
|
+
writeFileSync20(path, body);
|
|
25529
25897
|
pending.push({ artifactId: a.id, tool, artifactType: a.type, artifactName: a.name, sha256: sha, installedAt: Date.now() });
|
|
25530
25898
|
report.written.push({ artifactId: a.id, path });
|
|
25531
25899
|
} catch (err) {
|
|
@@ -25546,7 +25914,7 @@ async function mergePullResult(opts) {
|
|
|
25546
25914
|
if (!path)
|
|
25547
25915
|
continue;
|
|
25548
25916
|
try {
|
|
25549
|
-
if (
|
|
25917
|
+
if (existsSync52(path))
|
|
25550
25918
|
rmSync8(path, { force: true });
|
|
25551
25919
|
await teamInstallsForget(id, row.tool);
|
|
25552
25920
|
report.removed.push({ artifactId: id, path });
|
|
@@ -25564,12 +25932,12 @@ async function mergePullResult(opts) {
|
|
|
25564
25932
|
}
|
|
25565
25933
|
function readFileIfExists(path) {
|
|
25566
25934
|
try {
|
|
25567
|
-
if (!
|
|
25935
|
+
if (!existsSync52(path))
|
|
25568
25936
|
return null;
|
|
25569
25937
|
const s = statSync43(path);
|
|
25570
25938
|
if (!s.isFile())
|
|
25571
25939
|
return null;
|
|
25572
|
-
return
|
|
25940
|
+
return readFileSync46(path);
|
|
25573
25941
|
} catch {
|
|
25574
25942
|
return null;
|
|
25575
25943
|
}
|
|
@@ -25650,8 +26018,8 @@ var init_vault_crypto = __esm({
|
|
|
25650
26018
|
});
|
|
25651
26019
|
|
|
25652
26020
|
// ../engine/dist/core/vault-source.js
|
|
25653
|
-
import { existsSync as
|
|
25654
|
-
import { extname, join as
|
|
26021
|
+
import { existsSync as existsSync53, readdirSync as readdirSync34, statSync as statSync44 } from "fs";
|
|
26022
|
+
import { extname, join as join58 } from "path";
|
|
25655
26023
|
function* walkVaultSources() {
|
|
25656
26024
|
const t = loadSettings().team.vault;
|
|
25657
26025
|
const include2 = new Set(t.syncTools);
|
|
@@ -25676,7 +26044,7 @@ function safeStat(path) {
|
|
|
25676
26044
|
}
|
|
25677
26045
|
function* walkClaude() {
|
|
25678
26046
|
const root = claudeBackend.projectsDir();
|
|
25679
|
-
if (!
|
|
26047
|
+
if (!existsSync53(root))
|
|
25680
26048
|
return;
|
|
25681
26049
|
let projectDirs = [];
|
|
25682
26050
|
try {
|
|
@@ -25688,7 +26056,7 @@ function* walkClaude() {
|
|
|
25688
26056
|
if (proj.startsWith("."))
|
|
25689
26057
|
continue;
|
|
25690
26058
|
const projectPath = resolveProjectDirName(proj);
|
|
25691
|
-
const projDir =
|
|
26059
|
+
const projDir = join58(root, proj);
|
|
25692
26060
|
let entries = [];
|
|
25693
26061
|
try {
|
|
25694
26062
|
entries = readdirSync34(projDir);
|
|
@@ -25698,7 +26066,7 @@ function* walkClaude() {
|
|
|
25698
26066
|
for (const f of entries) {
|
|
25699
26067
|
if (extname(f) !== ".jsonl")
|
|
25700
26068
|
continue;
|
|
25701
|
-
const path =
|
|
26069
|
+
const path = join58(projDir, f);
|
|
25702
26070
|
const s = safeStat(path);
|
|
25703
26071
|
if (!s)
|
|
25704
26072
|
continue;
|
|
@@ -25728,8 +26096,8 @@ function* walkCursor() {
|
|
|
25728
26096
|
}
|
|
25729
26097
|
}
|
|
25730
26098
|
function* walkCodex() {
|
|
25731
|
-
const sessionsRoot =
|
|
25732
|
-
if (!
|
|
26099
|
+
const sessionsRoot = join58(codexBackend.homeDir(), "sessions");
|
|
26100
|
+
if (!existsSync53(sessionsRoot))
|
|
25733
26101
|
return;
|
|
25734
26102
|
const stack = [sessionsRoot];
|
|
25735
26103
|
while (stack.length) {
|
|
@@ -25741,7 +26109,7 @@ function* walkCodex() {
|
|
|
25741
26109
|
continue;
|
|
25742
26110
|
}
|
|
25743
26111
|
for (const e of entries) {
|
|
25744
|
-
const path =
|
|
26112
|
+
const path = join58(dir, e.name);
|
|
25745
26113
|
if (e.isDirectory()) {
|
|
25746
26114
|
stack.push(path);
|
|
25747
26115
|
continue;
|
|
@@ -25757,8 +26125,8 @@ function* walkCodex() {
|
|
|
25757
26125
|
}
|
|
25758
26126
|
}
|
|
25759
26127
|
function* walkAgy() {
|
|
25760
|
-
const brainDir =
|
|
25761
|
-
if (!
|
|
26128
|
+
const brainDir = join58(agyBackend.homeDir(), "brain");
|
|
26129
|
+
if (!existsSync53(brainDir))
|
|
25762
26130
|
return;
|
|
25763
26131
|
let sessionDirs = [];
|
|
25764
26132
|
try {
|
|
@@ -25767,18 +26135,18 @@ function* walkAgy() {
|
|
|
25767
26135
|
return;
|
|
25768
26136
|
}
|
|
25769
26137
|
for (const rawId of sessionDirs) {
|
|
25770
|
-
const sessionPath =
|
|
26138
|
+
const sessionPath = join58(brainDir, rawId);
|
|
25771
26139
|
try {
|
|
25772
26140
|
if (!statSync44(sessionPath).isDirectory())
|
|
25773
26141
|
continue;
|
|
25774
26142
|
} catch {
|
|
25775
26143
|
continue;
|
|
25776
26144
|
}
|
|
25777
|
-
let filePath =
|
|
25778
|
-
if (!
|
|
25779
|
-
filePath =
|
|
26145
|
+
let filePath = join58(sessionPath, ".system_generated", "logs", "transcript.jsonl");
|
|
26146
|
+
if (!existsSync53(filePath)) {
|
|
26147
|
+
filePath = join58(sessionPath, ".system_generated", "logs", "transcript_full.jsonl");
|
|
25780
26148
|
}
|
|
25781
|
-
if (!
|
|
26149
|
+
if (!existsSync53(filePath))
|
|
25782
26150
|
continue;
|
|
25783
26151
|
const s = safeStat(filePath);
|
|
25784
26152
|
if (!s)
|
|
@@ -25814,8 +26182,8 @@ __export(vault_client_exports, {
|
|
|
25814
26182
|
vaultStatus: () => vaultStatus,
|
|
25815
26183
|
vaultSync: () => vaultSync
|
|
25816
26184
|
});
|
|
25817
|
-
import { mkdirSync as
|
|
25818
|
-
import { dirname as
|
|
26185
|
+
import { mkdirSync as mkdirSync21, readFileSync as readFileSync47, writeFileSync as writeFileSync21, existsSync as existsSync54 } from "fs";
|
|
26186
|
+
import { dirname as dirname28, join as join59 } from "path";
|
|
25819
26187
|
function context2() {
|
|
25820
26188
|
const s = loadSettings().team;
|
|
25821
26189
|
if (!s.enabled)
|
|
@@ -25954,7 +26322,7 @@ async function vaultSync(passphrase) {
|
|
|
25954
26322
|
continue;
|
|
25955
26323
|
}
|
|
25956
26324
|
try {
|
|
25957
|
-
const plaintext =
|
|
26325
|
+
const plaintext = readFileSync47(src.path);
|
|
25958
26326
|
const sourceSha = sha256Hex(plaintext);
|
|
25959
26327
|
const priorSha = uploaded.get(uploadKey(src.sessionId, src.tool));
|
|
25960
26328
|
if (priorSha === sourceSha) {
|
|
@@ -26006,7 +26374,7 @@ async function vaultSync(passphrase) {
|
|
|
26006
26374
|
}
|
|
26007
26375
|
function importedPathFor(tool, sessionId) {
|
|
26008
26376
|
const base3 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
|
|
26009
|
-
return
|
|
26377
|
+
return join59(IMPORTED_ROOT(), tool, `${base3}.jsonl`);
|
|
26010
26378
|
}
|
|
26011
26379
|
async function vaultRestore(passphrase, opts = {}) {
|
|
26012
26380
|
const ctx = context2();
|
|
@@ -26027,7 +26395,7 @@ async function vaultRestore(passphrase, opts = {}) {
|
|
|
26027
26395
|
}
|
|
26028
26396
|
try {
|
|
26029
26397
|
const path = importedPathFor(blob.tool, blob.sessionId);
|
|
26030
|
-
if (
|
|
26398
|
+
if (existsSync54(path)) {
|
|
26031
26399
|
const localStat = await import("fs").then((m) => m.statSync(path));
|
|
26032
26400
|
if (Math.abs(localStat.mtimeMs - blob.mtimeSourceMs) < 1) {
|
|
26033
26401
|
report.skipped.push({ sessionId: blob.sessionId, tool: blob.tool, reason: "unchanged" });
|
|
@@ -26043,8 +26411,8 @@ async function vaultRestore(passphrase, opts = {}) {
|
|
|
26043
26411
|
throw new Error("cipher integrity check failed");
|
|
26044
26412
|
}
|
|
26045
26413
|
const plaintext = decryptBlob(key2, cipher);
|
|
26046
|
-
|
|
26047
|
-
|
|
26414
|
+
mkdirSync21(dirname28(path), { recursive: true });
|
|
26415
|
+
writeFileSync21(path, Buffer.from(plaintext));
|
|
26048
26416
|
const { utimesSync } = await import("fs");
|
|
26049
26417
|
const t = blob.mtimeSourceMs / 1e3;
|
|
26050
26418
|
utimesSync(path, t, t);
|
|
@@ -26095,7 +26463,7 @@ var init_vault_client = __esm({
|
|
|
26095
26463
|
uploadKey = (sessionId, tool) => `${sessionId}\0${tool}`;
|
|
26096
26464
|
VaultPassphraseMismatchError = class extends Error {
|
|
26097
26465
|
};
|
|
26098
|
-
IMPORTED_ROOT = () =>
|
|
26466
|
+
IMPORTED_ROOT = () => join59(getDataDir(), "imported");
|
|
26099
26467
|
}
|
|
26100
26468
|
});
|
|
26101
26469
|
|
|
@@ -26236,6 +26604,413 @@ var init_keyring = __esm({
|
|
|
26236
26604
|
}
|
|
26237
26605
|
});
|
|
26238
26606
|
|
|
26607
|
+
// ../engine/dist/core/decision-guard.js
|
|
26608
|
+
function normaliseName(raw) {
|
|
26609
|
+
let t = raw.trim().replace(/^["'`]|["'`,;]+$/g, "").trim();
|
|
26610
|
+
if (!t || NOT_A_PACKAGE.has(t))
|
|
26611
|
+
return null;
|
|
26612
|
+
if (t.startsWith("-"))
|
|
26613
|
+
return null;
|
|
26614
|
+
if (/^(?:\.{1,2}\/|\/|~\/|[a-z]+:\/\/)/i.test(t))
|
|
26615
|
+
return null;
|
|
26616
|
+
if (/\.(?:tgz|tar\.gz|whl|zip)$/i.test(t))
|
|
26617
|
+
return null;
|
|
26618
|
+
if (t.startsWith("@")) {
|
|
26619
|
+
const slash = t.indexOf("/");
|
|
26620
|
+
t = slash > 0 ? t.slice(slash + 1) : t.slice(1);
|
|
26621
|
+
}
|
|
26622
|
+
t = t.split("@")[0];
|
|
26623
|
+
t = t.split(/[=<>~!^]/)[0];
|
|
26624
|
+
t = t.replace(/\[[^\]]*\]$/, "");
|
|
26625
|
+
t = t.trim().toLowerCase();
|
|
26626
|
+
if (!t || t.length < 2)
|
|
26627
|
+
return null;
|
|
26628
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(t))
|
|
26629
|
+
return null;
|
|
26630
|
+
return t;
|
|
26631
|
+
}
|
|
26632
|
+
function fromCommand(command) {
|
|
26633
|
+
const out = [];
|
|
26634
|
+
for (const { re } of INSTALL_PATTERNS) {
|
|
26635
|
+
re.lastIndex = 0;
|
|
26636
|
+
let m;
|
|
26637
|
+
while ((m = re.exec(command)) !== null) {
|
|
26638
|
+
for (const tok of (m[1] ?? "").split(/\s+/)) {
|
|
26639
|
+
const n = normaliseName(tok);
|
|
26640
|
+
if (n)
|
|
26641
|
+
out.push(n);
|
|
26642
|
+
}
|
|
26643
|
+
}
|
|
26644
|
+
}
|
|
26645
|
+
return out;
|
|
26646
|
+
}
|
|
26647
|
+
function fromContent(content) {
|
|
26648
|
+
const out = [];
|
|
26649
|
+
for (const re of IMPORT_PATTERNS) {
|
|
26650
|
+
re.lastIndex = 0;
|
|
26651
|
+
let m;
|
|
26652
|
+
while ((m = re.exec(content)) !== null) {
|
|
26653
|
+
const spec = m[1] ?? m[2];
|
|
26654
|
+
if (!spec)
|
|
26655
|
+
continue;
|
|
26656
|
+
const root = spec.split(/[./]/)[0];
|
|
26657
|
+
const n = normaliseName(spec.startsWith("@") ? spec : root);
|
|
26658
|
+
if (n)
|
|
26659
|
+
out.push(n);
|
|
26660
|
+
}
|
|
26661
|
+
}
|
|
26662
|
+
return out;
|
|
26663
|
+
}
|
|
26664
|
+
function fromManifest(content) {
|
|
26665
|
+
const out = [];
|
|
26666
|
+
for (const m of content.matchAll(/"([^"]+)"\s*:\s*"[^"]*"/g)) {
|
|
26667
|
+
const n = normaliseName(m[1]);
|
|
26668
|
+
if (n)
|
|
26669
|
+
out.push(n);
|
|
26670
|
+
}
|
|
26671
|
+
for (const m of content.matchAll(/^\s*([A-Za-z][\w.-]*)\s*(?:=|==|>=|$)/gm)) {
|
|
26672
|
+
const n = normaliseName(m[1]);
|
|
26673
|
+
if (n)
|
|
26674
|
+
out.push(n);
|
|
26675
|
+
}
|
|
26676
|
+
return out;
|
|
26677
|
+
}
|
|
26678
|
+
function introducedBy(input) {
|
|
26679
|
+
const names = /* @__PURE__ */ new Set();
|
|
26680
|
+
let via = null;
|
|
26681
|
+
if (input.command) {
|
|
26682
|
+
for (const n of fromCommand(input.command)) {
|
|
26683
|
+
names.add(n);
|
|
26684
|
+
via = "install";
|
|
26685
|
+
}
|
|
26686
|
+
}
|
|
26687
|
+
if (input.content) {
|
|
26688
|
+
if (isManifest(input.path)) {
|
|
26689
|
+
for (const n of fromManifest(input.content)) {
|
|
26690
|
+
names.add(n);
|
|
26691
|
+
via = via ?? "manifest";
|
|
26692
|
+
}
|
|
26693
|
+
}
|
|
26694
|
+
for (const n of fromContent(input.content)) {
|
|
26695
|
+
names.add(n);
|
|
26696
|
+
via = via ?? "import";
|
|
26697
|
+
}
|
|
26698
|
+
}
|
|
26699
|
+
return { names: [...names], via };
|
|
26700
|
+
}
|
|
26701
|
+
var INSTALL_PATTERNS, NOT_A_PACKAGE, MANIFESTS, IMPORT_PATTERNS, isManifest;
|
|
26702
|
+
var init_decision_guard = __esm({
|
|
26703
|
+
"../engine/dist/core/decision-guard.js"() {
|
|
26704
|
+
"use strict";
|
|
26705
|
+
INSTALL_PATTERNS = [
|
|
26706
|
+
// npm/pnpm/yarn/bun. `add` and `install`, global or not.
|
|
26707
|
+
{ re: /\b(?:npm|pnpm|yarn|bun)\s+(?:install|add|i)\b([^\n;&|]*)/gi, via: "install" },
|
|
26708
|
+
// pip / pipx / uv
|
|
26709
|
+
{ re: /\b(?:pip3?|pipx|uv\s+pip)\s+install\b([^\n;&|]*)/gi, via: "install" },
|
|
26710
|
+
// cargo
|
|
26711
|
+
{ re: /\bcargo\s+add\b([^\n;&|]*)/gi, via: "install" },
|
|
26712
|
+
// go
|
|
26713
|
+
{ re: /\bgo\s+get\b([^\n;&|]*)/gi, via: "install" },
|
|
26714
|
+
// ruby
|
|
26715
|
+
{ re: /\b(?:gem\s+install|bundle\s+add)\b([^\n;&|]*)/gi, via: "install" },
|
|
26716
|
+
// php
|
|
26717
|
+
{ re: /\bcomposer\s+require\b([^\n;&|]*)/gi, via: "install" }
|
|
26718
|
+
];
|
|
26719
|
+
NOT_A_PACKAGE = /* @__PURE__ */ new Set([
|
|
26720
|
+
"-g",
|
|
26721
|
+
"--global",
|
|
26722
|
+
"-d",
|
|
26723
|
+
"-D",
|
|
26724
|
+
"--save",
|
|
26725
|
+
"--save-dev",
|
|
26726
|
+
"--save-exact",
|
|
26727
|
+
"--dev",
|
|
26728
|
+
"-e",
|
|
26729
|
+
"--editable",
|
|
26730
|
+
"--user",
|
|
26731
|
+
"--upgrade",
|
|
26732
|
+
"-U",
|
|
26733
|
+
"--force",
|
|
26734
|
+
"-f",
|
|
26735
|
+
"--yes",
|
|
26736
|
+
"-y",
|
|
26737
|
+
"--prefix",
|
|
26738
|
+
"--no-save",
|
|
26739
|
+
"--legacy-peer-deps",
|
|
26740
|
+
"--frozen-lockfile",
|
|
26741
|
+
"-r",
|
|
26742
|
+
"--requirement",
|
|
26743
|
+
"--quiet",
|
|
26744
|
+
"-q",
|
|
26745
|
+
"--silent",
|
|
26746
|
+
".",
|
|
26747
|
+
"..",
|
|
26748
|
+
"*"
|
|
26749
|
+
]);
|
|
26750
|
+
MANIFESTS = [
|
|
26751
|
+
"package.json",
|
|
26752
|
+
"cargo.toml",
|
|
26753
|
+
"requirements.txt",
|
|
26754
|
+
"pyproject.toml",
|
|
26755
|
+
"go.mod",
|
|
26756
|
+
"gemfile",
|
|
26757
|
+
"composer.json",
|
|
26758
|
+
"build.gradle",
|
|
26759
|
+
"pom.xml"
|
|
26760
|
+
];
|
|
26761
|
+
IMPORT_PATTERNS = [
|
|
26762
|
+
/\bimport\s+(?:[\w*{}\s,]+\s+from\s+)?["']([^"']+)["']/g,
|
|
26763
|
+
// ES
|
|
26764
|
+
/\brequire\s*\(\s*["']([^"']+)["']\s*\)/g,
|
|
26765
|
+
// CJS
|
|
26766
|
+
// Python. The bare `import X` form must END there: without the anchor it also
|
|
26767
|
+
// matched the `import Keycloak` half of `import Keycloak from "keycloak-js"`,
|
|
26768
|
+
// so one JS line yielded both `keycloak` and `keycloak-js` and the guard
|
|
26769
|
+
// would have reported a library nobody named.
|
|
26770
|
+
/^\s*from\s+([\w.]+)\s+import\b/gm,
|
|
26771
|
+
/^\s*import\s+([\w.]+)(?:\s+as\s+\w+)?\s*$/gm,
|
|
26772
|
+
/^\s*use\s+([a-z0-9_]+)\s*(?:::|;)/gmi
|
|
26773
|
+
// Rust
|
|
26774
|
+
];
|
|
26775
|
+
isManifest = (p) => {
|
|
26776
|
+
if (!p)
|
|
26777
|
+
return false;
|
|
26778
|
+
const base3 = p.split(/[\\/]/).pop()?.toLowerCase() ?? "";
|
|
26779
|
+
return MANIFESTS.includes(base3);
|
|
26780
|
+
};
|
|
26781
|
+
}
|
|
26782
|
+
});
|
|
26783
|
+
|
|
26784
|
+
// ../engine/dist/core/guard-adapters.js
|
|
26785
|
+
var guard_adapters_exports = {};
|
|
26786
|
+
__export(guard_adapters_exports, {
|
|
26787
|
+
COVERAGE: () => COVERAGE,
|
|
26788
|
+
explain: () => explain,
|
|
26789
|
+
namesFrom: () => namesFrom,
|
|
26790
|
+
readPayload: () => readPayload,
|
|
26791
|
+
writeVerdict: () => writeVerdict
|
|
26792
|
+
});
|
|
26793
|
+
function readPayload(harness, payload) {
|
|
26794
|
+
const p = payload ?? {};
|
|
26795
|
+
switch (harness) {
|
|
26796
|
+
case "claude":
|
|
26797
|
+
case "codex": {
|
|
26798
|
+
const input = p.tool_input ?? p.toolInput ?? {};
|
|
26799
|
+
return {
|
|
26800
|
+
tool: p.tool_name ?? p.toolName ?? null,
|
|
26801
|
+
command: input.command ?? null,
|
|
26802
|
+
path: input.file_path ?? input.path ?? null,
|
|
26803
|
+
content: input.new_string ?? input.content ?? input.new_str ?? null
|
|
26804
|
+
};
|
|
26805
|
+
}
|
|
26806
|
+
case "agy": {
|
|
26807
|
+
const call2 = p.toolCall ?? p.tool_call ?? {};
|
|
26808
|
+
const input = call2.args ?? p.tool_input ?? p.args ?? {};
|
|
26809
|
+
return {
|
|
26810
|
+
tool: call2.name ?? p.tool_name ?? p.tool ?? null,
|
|
26811
|
+
command: input.CommandLine ?? input.command ?? input.cmd ?? null,
|
|
26812
|
+
path: input.TargetFile ?? input.AbsolutePath ?? input.file_path ?? input.path ?? null,
|
|
26813
|
+
content: input.CodeContent ?? input.ReplacementContent ?? input.content ?? input.new_string ?? null
|
|
26814
|
+
};
|
|
26815
|
+
}
|
|
26816
|
+
case "cursor": {
|
|
26817
|
+
const edits = Array.isArray(p.edits) ? p.edits : [];
|
|
26818
|
+
return {
|
|
26819
|
+
tool: p.hook_event_name ?? null,
|
|
26820
|
+
command: p.command ?? null,
|
|
26821
|
+
path: p.file_path ?? null,
|
|
26822
|
+
content: edits.map((e) => e?.new_string ?? e?.newText ?? "").join("\n") || p.content || null
|
|
26823
|
+
};
|
|
26824
|
+
}
|
|
26825
|
+
case "opencode": {
|
|
26826
|
+
const args = p.args ?? p.output?.args ?? {};
|
|
26827
|
+
return {
|
|
26828
|
+
tool: p.tool ?? p.input?.tool ?? null,
|
|
26829
|
+
command: args.command ?? null,
|
|
26830
|
+
path: args.filePath ?? args.path ?? null,
|
|
26831
|
+
content: args.content ?? args.newString ?? null
|
|
26832
|
+
};
|
|
26833
|
+
}
|
|
26834
|
+
}
|
|
26835
|
+
}
|
|
26836
|
+
function namesFrom(harness, payload) {
|
|
26837
|
+
return introducedBy(readPayload(harness, payload));
|
|
26838
|
+
}
|
|
26839
|
+
function explain(findings) {
|
|
26840
|
+
if (!findings.length)
|
|
26841
|
+
return "";
|
|
26842
|
+
const lines = findings.map((f) => {
|
|
26843
|
+
const head = f.instead ? `${f.name} was ruled out \u2014 ${f.instead} is the decision` : `${f.name} was ruled out`;
|
|
26844
|
+
const when = f.since ? ` (${f.since})` : "";
|
|
26845
|
+
const where = f.source_session ? ` \xB7 session ${f.source_session}` : "";
|
|
26846
|
+
return `- ${head}${when}${where}`;
|
|
26847
|
+
});
|
|
26848
|
+
return [
|
|
26849
|
+
"chat-recall: this reaches for something already decided against.",
|
|
26850
|
+
...lines,
|
|
26851
|
+
"Proceed only if this is a deliberate reversal. If it is, record it:",
|
|
26852
|
+
"recall_decision_record with the area, so the old decision is closed rather than contradicted."
|
|
26853
|
+
].join("\n");
|
|
26854
|
+
}
|
|
26855
|
+
function writeVerdict(harness, verdict, message) {
|
|
26856
|
+
if (verdict === "allow" || !message)
|
|
26857
|
+
return { exitCode: 0 };
|
|
26858
|
+
switch (harness) {
|
|
26859
|
+
case "claude":
|
|
26860
|
+
case "codex": {
|
|
26861
|
+
if (verdict === "deny") {
|
|
26862
|
+
return {
|
|
26863
|
+
json: { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: message } },
|
|
26864
|
+
exitCode: 0
|
|
26865
|
+
};
|
|
26866
|
+
}
|
|
26867
|
+
return {
|
|
26868
|
+
json: { hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: message } },
|
|
26869
|
+
exitCode: 0
|
|
26870
|
+
};
|
|
26871
|
+
}
|
|
26872
|
+
case "agy": {
|
|
26873
|
+
const decision = verdict === "deny" ? "deny" : verdict === "ask" ? "ask" : "allow";
|
|
26874
|
+
return { json: { decision, reason: message }, exitCode: 0 };
|
|
26875
|
+
}
|
|
26876
|
+
case "cursor": {
|
|
26877
|
+
if (verdict === "deny") {
|
|
26878
|
+
return { json: { permission: "deny", user_message: message, userMessage: message }, exitCode: 0 };
|
|
26879
|
+
}
|
|
26880
|
+
return {
|
|
26881
|
+
json: { permission: "allow", agent_message: message, agentMessage: message },
|
|
26882
|
+
exitCode: 0
|
|
26883
|
+
};
|
|
26884
|
+
}
|
|
26885
|
+
case "opencode": {
|
|
26886
|
+
return { json: { block: verdict === "deny", message }, exitCode: 0 };
|
|
26887
|
+
}
|
|
26888
|
+
}
|
|
26889
|
+
}
|
|
26890
|
+
var COVERAGE;
|
|
26891
|
+
var init_guard_adapters = __esm({
|
|
26892
|
+
"../engine/dist/core/guard-adapters.js"() {
|
|
26893
|
+
"use strict";
|
|
26894
|
+
init_decision_guard();
|
|
26895
|
+
COVERAGE = {
|
|
26896
|
+
claude: { commands: true, edits: "before" },
|
|
26897
|
+
codex: { commands: true, edits: "before" },
|
|
26898
|
+
agy: { commands: true, edits: "before" },
|
|
26899
|
+
cursor: {
|
|
26900
|
+
commands: true,
|
|
26901
|
+
edits: "after",
|
|
26902
|
+
note: "Cursor exposes beforeShellExecution but only afterFileEdit, so an import added in an edit is reported after the write, not prevented."
|
|
26903
|
+
},
|
|
26904
|
+
opencode: {
|
|
26905
|
+
commands: true,
|
|
26906
|
+
edits: "before",
|
|
26907
|
+
note: "OpenCode plugin hooks do not fire for subagent tool calls (anomalyco/opencode#5894), so an agent that delegates bypasses this."
|
|
26908
|
+
}
|
|
26909
|
+
};
|
|
26910
|
+
}
|
|
26911
|
+
});
|
|
26912
|
+
|
|
26913
|
+
// ../engine/dist/core/task-hook.js
|
|
26914
|
+
var task_hook_exports = {};
|
|
26915
|
+
__export(task_hook_exports, {
|
|
26916
|
+
CLAIMABLE_STATUSES: () => CLAIMABLE_STATUSES,
|
|
26917
|
+
CLOSEABLE_STATUSES: () => CLOSEABLE_STATUSES,
|
|
26918
|
+
COMMIT_LOG_FORMAT: () => COMMIT_LOG_FORMAT,
|
|
26919
|
+
MAX_EVIDENCE_COMMITS: () => MAX_EVIDENCE_COMMITS,
|
|
26920
|
+
MAX_EVIDENCE_FILES: () => MAX_EVIDENCE_FILES,
|
|
26921
|
+
TASK_ID_PATTERN: () => TASK_ID_PATTERN,
|
|
26922
|
+
extractTaskIds: () => extractTaskIds,
|
|
26923
|
+
mayClaim: () => mayClaim,
|
|
26924
|
+
mayClose: () => mayClose,
|
|
26925
|
+
parseChangedFiles: () => parseChangedFiles,
|
|
26926
|
+
parseCommitLog: () => parseCommitLog,
|
|
26927
|
+
planCloses: () => planCloses
|
|
26928
|
+
});
|
|
26929
|
+
function extractTaskIds(text) {
|
|
26930
|
+
if (!text)
|
|
26931
|
+
return [];
|
|
26932
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26933
|
+
const out = [];
|
|
26934
|
+
for (const match of text.toLowerCase().matchAll(TASK_ID_PATTERN)) {
|
|
26935
|
+
if (seen.has(match[0]))
|
|
26936
|
+
continue;
|
|
26937
|
+
seen.add(match[0]);
|
|
26938
|
+
out.push(match[0]);
|
|
26939
|
+
}
|
|
26940
|
+
return out;
|
|
26941
|
+
}
|
|
26942
|
+
function parseCommitLog(raw) {
|
|
26943
|
+
if (!raw)
|
|
26944
|
+
return [];
|
|
26945
|
+
const out = [];
|
|
26946
|
+
for (const record3 of raw.split("")) {
|
|
26947
|
+
const sep4 = record3.indexOf("");
|
|
26948
|
+
if (sep4 < 0)
|
|
26949
|
+
continue;
|
|
26950
|
+
const sha = record3.slice(0, sep4).trim();
|
|
26951
|
+
if (!/^[0-9a-f]{7,40}$/i.test(sha))
|
|
26952
|
+
continue;
|
|
26953
|
+
out.push({ sha, message: record3.slice(sep4 + 1) });
|
|
26954
|
+
}
|
|
26955
|
+
return out;
|
|
26956
|
+
}
|
|
26957
|
+
function planCloses(commits) {
|
|
26958
|
+
const byId = /* @__PURE__ */ new Map();
|
|
26959
|
+
for (const commit of commits) {
|
|
26960
|
+
for (const id of extractTaskIds(commit.message)) {
|
|
26961
|
+
const shas = byId.get(id) ?? [];
|
|
26962
|
+
if (!shas.includes(commit.sha) && shas.length < MAX_EVIDENCE_COMMITS)
|
|
26963
|
+
shas.push(commit.sha);
|
|
26964
|
+
byId.set(id, shas);
|
|
26965
|
+
}
|
|
26966
|
+
}
|
|
26967
|
+
return [...byId].map(([id, shas]) => ({ id, commits: shas }));
|
|
26968
|
+
}
|
|
26969
|
+
function parseChangedFiles(raw) {
|
|
26970
|
+
if (!raw)
|
|
26971
|
+
return [];
|
|
26972
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26973
|
+
const out = [];
|
|
26974
|
+
for (const line of raw.split("\n")) {
|
|
26975
|
+
const file = line.trim();
|
|
26976
|
+
if (!file)
|
|
26977
|
+
continue;
|
|
26978
|
+
if (file.startsWith("/") || file.startsWith("~"))
|
|
26979
|
+
continue;
|
|
26980
|
+
if (/(^|\/)\.\.(\/|$)/.test(file))
|
|
26981
|
+
continue;
|
|
26982
|
+
if (seen.has(file))
|
|
26983
|
+
continue;
|
|
26984
|
+
seen.add(file);
|
|
26985
|
+
out.push(file);
|
|
26986
|
+
if (out.length >= MAX_EVIDENCE_FILES)
|
|
26987
|
+
break;
|
|
26988
|
+
}
|
|
26989
|
+
return out;
|
|
26990
|
+
}
|
|
26991
|
+
function mayClaim(status, linkedSessionId, sessionId) {
|
|
26992
|
+
if (!sessionId)
|
|
26993
|
+
return false;
|
|
26994
|
+
if (linkedSessionId === sessionId && status === "in_progress")
|
|
26995
|
+
return false;
|
|
26996
|
+
return CLAIMABLE_STATUSES.has(String(status ?? ""));
|
|
26997
|
+
}
|
|
26998
|
+
function mayClose(status) {
|
|
26999
|
+
return CLOSEABLE_STATUSES.has(String(status ?? ""));
|
|
27000
|
+
}
|
|
27001
|
+
var TASK_ID_PATTERN, CLAIMABLE_STATUSES, CLOSEABLE_STATUSES, MAX_EVIDENCE_COMMITS, MAX_EVIDENCE_FILES, COMMIT_LOG_FORMAT;
|
|
27002
|
+
var init_task_hook = __esm({
|
|
27003
|
+
"../engine/dist/core/task-hook.js"() {
|
|
27004
|
+
"use strict";
|
|
27005
|
+
TASK_ID_PATTERN = /\bt_[0-9a-f]{18}\b/g;
|
|
27006
|
+
CLAIMABLE_STATUSES = /* @__PURE__ */ new Set(["todo"]);
|
|
27007
|
+
CLOSEABLE_STATUSES = /* @__PURE__ */ new Set(["todo", "in_progress"]);
|
|
27008
|
+
MAX_EVIDENCE_COMMITS = 20;
|
|
27009
|
+
MAX_EVIDENCE_FILES = 50;
|
|
27010
|
+
COMMIT_LOG_FORMAT = "%H%x1f%B%x1e";
|
|
27011
|
+
}
|
|
27012
|
+
});
|
|
27013
|
+
|
|
26239
27014
|
// src/service-installer.ts
|
|
26240
27015
|
var service_installer_exports = {};
|
|
26241
27016
|
__export(service_installer_exports, {
|
|
@@ -26250,15 +27025,15 @@ __export(service_installer_exports, {
|
|
|
26250
27025
|
uninstallService: () => uninstallService
|
|
26251
27026
|
});
|
|
26252
27027
|
import { execSync as execSync5 } from "child_process";
|
|
26253
|
-
import { existsSync as
|
|
26254
|
-
import { homedir as
|
|
26255
|
-
import { join as
|
|
27028
|
+
import { existsSync as existsSync55, mkdirSync as mkdirSync22, readFileSync as readFileSync48, rmSync as rmSync9, writeFileSync as writeFileSync22 } from "fs";
|
|
27029
|
+
import { homedir as homedir22 } from "os";
|
|
27030
|
+
import { join as join60 } from "path";
|
|
26256
27031
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
26257
27032
|
function resolveDaemonPaths() {
|
|
26258
27033
|
const watchJs = fileURLToPath5(new URL("./watch.js", import.meta.url));
|
|
26259
27034
|
const node = process.execPath;
|
|
26260
|
-
const dataDir =
|
|
26261
|
-
const logFile =
|
|
27035
|
+
const dataDir = join60(homedir22(), ".chat-recall");
|
|
27036
|
+
const logFile = join60(dataDir, "watch.log");
|
|
26262
27037
|
return { watchJs, node, logFile, dataDir };
|
|
26263
27038
|
}
|
|
26264
27039
|
function tryCmd(cmd) {
|
|
@@ -26285,13 +27060,13 @@ function refreshServiceDefinition() {
|
|
|
26285
27060
|
if (process.platform !== "linux") return false;
|
|
26286
27061
|
try {
|
|
26287
27062
|
const { watchJs, node, logFile } = resolveDaemonPaths();
|
|
26288
|
-
const unitPath =
|
|
26289
|
-
if (!
|
|
26290
|
-
const current =
|
|
27063
|
+
const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
|
|
27064
|
+
if (!existsSync55(unitPath)) return false;
|
|
27065
|
+
const current = readFileSync48(unitPath, "utf8");
|
|
26291
27066
|
if (!current.includes(watchJs)) return false;
|
|
26292
27067
|
const wanted = renderSystemdUnit(watchJs, node, logFile);
|
|
26293
|
-
if (
|
|
26294
|
-
|
|
27068
|
+
if (readFileSync48(unitPath, "utf8") === wanted) return false;
|
|
27069
|
+
writeFileSync22(unitPath, wanted);
|
|
26295
27070
|
execSync5("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
26296
27071
|
return true;
|
|
26297
27072
|
} catch {
|
|
@@ -26300,7 +27075,7 @@ function refreshServiceDefinition() {
|
|
|
26300
27075
|
}
|
|
26301
27076
|
function installService() {
|
|
26302
27077
|
const { watchJs, node, logFile, dataDir } = resolveDaemonPaths();
|
|
26303
|
-
|
|
27078
|
+
mkdirSync22(dataDir, { recursive: true });
|
|
26304
27079
|
if (process.platform === "linux") {
|
|
26305
27080
|
return installSystemd(watchJs, node, logFile);
|
|
26306
27081
|
}
|
|
@@ -26316,11 +27091,11 @@ function installService() {
|
|
|
26316
27091
|
);
|
|
26317
27092
|
}
|
|
26318
27093
|
function installSystemd(watchJs, node, logFile) {
|
|
26319
|
-
const unitDir =
|
|
26320
|
-
const unitPath =
|
|
27094
|
+
const unitDir = join60(homedir22(), ".config", "systemd", "user");
|
|
27095
|
+
const unitPath = join60(unitDir, "chat-recall-watch.service");
|
|
26321
27096
|
const unit = renderSystemdUnit(watchJs, node, logFile);
|
|
26322
|
-
|
|
26323
|
-
|
|
27097
|
+
mkdirSync22(unitDir, { recursive: true });
|
|
27098
|
+
writeFileSync22(unitPath, unit);
|
|
26324
27099
|
try {
|
|
26325
27100
|
execSync5("systemctl --user daemon-reload && systemctl --user enable chat-recall-watch.service && systemctl --user restart chat-recall-watch.service", { stdio: "inherit" });
|
|
26326
27101
|
try {
|
|
@@ -26391,11 +27166,11 @@ function renderSystemdUnit(watchJs, node, logFile) {
|
|
|
26391
27166
|
}
|
|
26392
27167
|
function installLaunchd(watchJs, node, logFile) {
|
|
26393
27168
|
const label = "com.chat-recall.watch";
|
|
26394
|
-
const plistDir =
|
|
26395
|
-
const plistPath =
|
|
27169
|
+
const plistDir = join60(homedir22(), "Library", "LaunchAgents");
|
|
27170
|
+
const plistPath = join60(plistDir, `${label}.plist`);
|
|
26396
27171
|
const plist = renderLaunchdPlist(watchJs, node, logFile, label);
|
|
26397
|
-
|
|
26398
|
-
|
|
27172
|
+
mkdirSync22(plistDir, { recursive: true });
|
|
27173
|
+
writeFileSync22(plistPath, plist);
|
|
26399
27174
|
try {
|
|
26400
27175
|
try {
|
|
26401
27176
|
execSync5(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
|
|
@@ -26448,8 +27223,8 @@ function uninstallService() {
|
|
|
26448
27223
|
removed = true;
|
|
26449
27224
|
} catch {
|
|
26450
27225
|
}
|
|
26451
|
-
const unitPath =
|
|
26452
|
-
if (
|
|
27226
|
+
const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
|
|
27227
|
+
if (existsSync55(unitPath)) {
|
|
26453
27228
|
rmSync9(unitPath, { force: true });
|
|
26454
27229
|
removed = true;
|
|
26455
27230
|
}
|
|
@@ -26460,13 +27235,13 @@ function uninstallService() {
|
|
|
26460
27235
|
return removed;
|
|
26461
27236
|
}
|
|
26462
27237
|
if (process.platform === "darwin") {
|
|
26463
|
-
const plistPath =
|
|
27238
|
+
const plistPath = join60(homedir22(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
|
|
26464
27239
|
try {
|
|
26465
27240
|
execSync5(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
|
|
26466
27241
|
} catch {
|
|
26467
27242
|
}
|
|
26468
27243
|
let removed = false;
|
|
26469
|
-
if (
|
|
27244
|
+
if (existsSync55(plistPath)) {
|
|
26470
27245
|
rmSync9(plistPath, { force: true });
|
|
26471
27246
|
removed = true;
|
|
26472
27247
|
}
|
|
@@ -26879,9 +27654,9 @@ init_project_resolver();
|
|
|
26879
27654
|
import { config } from "dotenv";
|
|
26880
27655
|
import { Command } from "commander";
|
|
26881
27656
|
import chalk2 from "chalk";
|
|
26882
|
-
import { existsSync as
|
|
26883
|
-
import { homedir as
|
|
26884
|
-
import { join as
|
|
27657
|
+
import { existsSync as existsSync56, readFileSync as readFileSync49, writeFileSync as writeFileSync23, realpathSync as realpathSync5 } from "fs";
|
|
27658
|
+
import { homedir as homedir23 } from "os";
|
|
27659
|
+
import { join as join61, resolve as resolve2, dirname as dirname29, basename as basename27 } from "path";
|
|
26885
27660
|
|
|
26886
27661
|
// ../engine/dist/core/score-tier.js
|
|
26887
27662
|
var STRONG_RATIO = 0.85;
|
|
@@ -26986,6 +27761,47 @@ function renderEnvNeeds(needs, installedCount) {
|
|
|
26986
27761
|
return lines;
|
|
26987
27762
|
}
|
|
26988
27763
|
|
|
27764
|
+
// src/star-nudge.ts
|
|
27765
|
+
init_paths();
|
|
27766
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "node:fs";
|
|
27767
|
+
import { dirname as dirname20, join as join45 } from "node:path";
|
|
27768
|
+
var REPO = "https://github.com/munhq/chat-recall";
|
|
27769
|
+
function markerPath() {
|
|
27770
|
+
return join45(getDataDir(), ".star-asked");
|
|
27771
|
+
}
|
|
27772
|
+
function suppressed() {
|
|
27773
|
+
const env = process.env;
|
|
27774
|
+
if (env.CHAT_RECALL_NO_STAR_PROMPT) return true;
|
|
27775
|
+
return Boolean(env.CI || env.GITHUB_ACTIONS || env.GITLAB_CI || env.BUILDKITE || env.TEAMCITY_VERSION);
|
|
27776
|
+
}
|
|
27777
|
+
function shouldAskForStar(resultCount) {
|
|
27778
|
+
if (resultCount <= 0) return false;
|
|
27779
|
+
if (suppressed()) return false;
|
|
27780
|
+
if (!process.stderr.isTTY) return false;
|
|
27781
|
+
try {
|
|
27782
|
+
return !existsSync42(markerPath());
|
|
27783
|
+
} catch {
|
|
27784
|
+
return false;
|
|
27785
|
+
}
|
|
27786
|
+
}
|
|
27787
|
+
function markStarAsked() {
|
|
27788
|
+
try {
|
|
27789
|
+
const p = markerPath();
|
|
27790
|
+
mkdirSync11(dirname20(p), { recursive: true });
|
|
27791
|
+
writeFileSync11(p, `${(/* @__PURE__ */ new Date()).toISOString()}
|
|
27792
|
+
`);
|
|
27793
|
+
} catch {
|
|
27794
|
+
}
|
|
27795
|
+
}
|
|
27796
|
+
function askForStar(resultCount, dim) {
|
|
27797
|
+
if (!shouldAskForStar(resultCount)) return;
|
|
27798
|
+
markStarAsked();
|
|
27799
|
+
process.stderr.write(`${dim(`If this saved you time, a star helps other people find it: ${REPO}`)}
|
|
27800
|
+
`);
|
|
27801
|
+
process.stderr.write(`${dim("You will not be asked again. CHAT_RECALL_NO_STAR_PROMPT=1 disables it everywhere.")}
|
|
27802
|
+
`);
|
|
27803
|
+
}
|
|
27804
|
+
|
|
26989
27805
|
// src/cli.ts
|
|
26990
27806
|
function matchLabel(tier) {
|
|
26991
27807
|
switch (tier) {
|
|
@@ -27004,8 +27820,8 @@ function claudeHookConfigFiles() {
|
|
|
27004
27820
|
const out = [];
|
|
27005
27821
|
const seen = /* @__PURE__ */ new Set();
|
|
27006
27822
|
for (const home of claudeHomeDirs()) {
|
|
27007
|
-
if (!
|
|
27008
|
-
const file =
|
|
27823
|
+
if (!existsSync56(join61(home, "projects"))) continue;
|
|
27824
|
+
const file = join61(home, "hooks.json");
|
|
27009
27825
|
let key2;
|
|
27010
27826
|
try {
|
|
27011
27827
|
key2 = realpathSync5(file);
|
|
@@ -27100,13 +27916,13 @@ async function serverDelete(path, body) {
|
|
|
27100
27916
|
}
|
|
27101
27917
|
var pkgEngines = (() => {
|
|
27102
27918
|
try {
|
|
27103
|
-
return JSON.parse(
|
|
27919
|
+
return JSON.parse(readFileSync49(new URL("../package.json", import.meta.url), "utf-8")).engines?.node ?? "";
|
|
27104
27920
|
} catch {
|
|
27105
27921
|
return "";
|
|
27106
27922
|
}
|
|
27107
27923
|
})();
|
|
27108
27924
|
var pkgVersion = JSON.parse(
|
|
27109
|
-
|
|
27925
|
+
readFileSync49(new URL("../package.json", import.meta.url), "utf-8")
|
|
27110
27926
|
).version;
|
|
27111
27927
|
var DEFAULT_SERVER = (process.env.CHAT_RECALL_DEFAULT_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
|
|
27112
27928
|
var DEFAULT_ALLOW = [
|
|
@@ -27194,7 +28010,7 @@ process.env.CHAT_RECALL_CLI_VERSION = pkgVersion;
|
|
|
27194
28010
|
enforceNodeVersion();
|
|
27195
28011
|
var program = new Command();
|
|
27196
28012
|
program.name("chat-recall").description("Semantic search for Claude Code sessions - recall and resume past conversations").version(pkgVersion);
|
|
27197
|
-
program.command("init").description("Set up chat-recall: connect to your server, configure MCP, ship your local sessions").option("--server <url>", "chat-recall server URL to connect this machine to").option("--token <token>", "Self-host device token (skips the interactive OIDC login)").option("--skip-mcp", "Skip MCP server configuration").option("--skip-sync", "Skip the first session sync").option("--with-codeindex", "Force-download the codeindex binary during init. Default behavior is to detect an already-installed codeindex on PATH and register it as an MCP server.").option("--skip-codeindex", "Skip the codeindex companion entirely (no detection, no registration).").option("--skip-service", "Skip installing the per-user background sync service (Linux/macOS/Windows). By default init installs it so new conversations ship automatically.").option("--yes", "Do not pause before the first upload. The scope summary still prints.", false).option("--prompt-json", "Print the device prompt as one JSON line on stdout, then continue", false).action(async (options) => {
|
|
28013
|
+
program.command("init").description("Set up chat-recall: connect to your server, configure MCP, ship your local sessions").option("--server <url>", "chat-recall server URL to connect this machine to").option("--token <token>", "Self-host device token (skips the interactive OIDC login)").option("--skip-mcp", "Skip MCP server configuration").option("--skip-sync", "Skip the first session sync").option("--with-codeindex", "Force-download the codeindex binary during init. Default behavior is to detect an already-installed codeindex on PATH and register it as an MCP server.").option("--skip-codeindex", "Skip the codeindex companion entirely (no detection, no registration).").option("--skip-service", "Skip installing the per-user background sync service (Linux/macOS/Windows). By default init installs it so new conversations ship automatically.").option("--skip-hooks", "Skip registering the task-board hooks").option("--yes", "Do not pause before the first upload. The scope summary still prints.", false).option("--prompt-json", "Print the device prompt as one JSON line on stdout, then continue", false).action(async (options) => {
|
|
27198
28014
|
try {
|
|
27199
28015
|
console.log(chalk2.bold("chat-recall init") + chalk2.dim(` v${pkgVersion}`));
|
|
27200
28016
|
const { createInitReporter: createInitReporter2 } = await Promise.resolve().then(() => (init_init_report(), init_report_exports));
|
|
@@ -27220,6 +28036,18 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27220
28036
|
}
|
|
27221
28037
|
const foundTools = clis.filter((c) => c.available).map((c) => c.name.replace(/ (CLI|Code)$/, ""));
|
|
27222
28038
|
if (!foundTools.length) rep.warn(chalk2.yellow("No AI tools detected \u2014 chat-recall indexes their transcripts, so there is nothing to ship yet."));
|
|
28039
|
+
else {
|
|
28040
|
+
try {
|
|
28041
|
+
const { listAvailableBackends: listAvailableBackends2 } = await Promise.resolve().then(() => (init_tool_backend(), tool_backend_exports));
|
|
28042
|
+
const transcripts = listAvailableBackends2().reduce((n, b) => n + (b.listSessions?.({ limit: 1 })?.length ?? 0), 0);
|
|
28043
|
+
if (transcripts === 0) {
|
|
28044
|
+
rep.warn(chalk2.yellow(
|
|
28045
|
+
`Found ${foundTools.join(", ")} but no conversations yet \u2014 chat-recall indexes the transcripts your AI tools write as you work. Use one for a while, then run ${chalk2.bold("chat-recall sync")}.`
|
|
28046
|
+
));
|
|
28047
|
+
}
|
|
28048
|
+
} catch {
|
|
28049
|
+
}
|
|
28050
|
+
}
|
|
27223
28051
|
const serverBad = (text) => rep.warn(`${chalk2.yellow("Not connected")} \u2014 ${text}`);
|
|
27224
28052
|
let target = firstTarget();
|
|
27225
28053
|
let deadTarget = false;
|
|
@@ -27267,10 +28095,10 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27267
28095
|
} else if (target) {
|
|
27268
28096
|
}
|
|
27269
28097
|
if (!options.skipMcp) {
|
|
27270
|
-
const projectRoot =
|
|
28098
|
+
const projectRoot = join61(import.meta.dirname, "..");
|
|
27271
28099
|
let mcpBinOnPath = false;
|
|
27272
28100
|
mcpBinOnPath = isOnPath("chat-recall-mcp");
|
|
27273
|
-
const launch = mcpBinOnPath ? { command: "chat-recall-mcp" } : { command: "node", args: [
|
|
28101
|
+
const launch = mcpBinOnPath ? { command: "chat-recall-mcp" } : { command: "node", args: [join61(projectRoot, "dist", "mcp-relay.js")] };
|
|
27274
28102
|
const MCP_ENV = { NODE_OPTIONS: "--max-old-space-size=1024" };
|
|
27275
28103
|
const { registerMcpEverywhere: registerMcpEverywhere2 } = await Promise.resolve().then(() => (init_mcp_clients(), mcp_clients_exports));
|
|
27276
28104
|
const MCP_CLIENT_FOR_BIN = {
|
|
@@ -27310,7 +28138,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27310
28138
|
const detected = checkCodeindexStatus2();
|
|
27311
28139
|
if (detected.installed && !forceInstall) {
|
|
27312
28140
|
if (!options.skipMcp) {
|
|
27313
|
-
const mcpJsonPath =
|
|
28141
|
+
const mcpJsonPath = join61(homedir23(), ".mcp.json");
|
|
27314
28142
|
const reg = registerCodeindexMcp2(mcpJsonPath, detected.path);
|
|
27315
28143
|
}
|
|
27316
28144
|
} else if (forceInstall) {
|
|
@@ -27319,7 +28147,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27319
28147
|
if (result.installed) {
|
|
27320
28148
|
const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
|
|
27321
28149
|
if (!options.skipMcp) {
|
|
27322
|
-
const mcpJsonPath =
|
|
28150
|
+
const mcpJsonPath = join61(homedir23(), ".mcp.json");
|
|
27323
28151
|
const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
|
|
27324
28152
|
}
|
|
27325
28153
|
} else if (!result.prebuiltAvailable) {
|
|
@@ -27438,6 +28266,15 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27438
28266
|
} else {
|
|
27439
28267
|
}
|
|
27440
28268
|
void options.skipService;
|
|
28269
|
+
if (!options.skipHooks) {
|
|
28270
|
+
try {
|
|
28271
|
+
const { registerBoardHooks: registerBoardHooks2, resolveCliEntry: resolveCliEntry2 } = await Promise.resolve().then(() => (init_board_hooks(), board_hooks_exports));
|
|
28272
|
+
const { fileURLToPath: toPath } = await import("url");
|
|
28273
|
+
const distDir = toPath(new URL(".", import.meta.url));
|
|
28274
|
+
registerBoardHooks2(claudeHookConfigFiles(), process.execPath, resolveCliEntry2(distDir));
|
|
28275
|
+
} catch {
|
|
28276
|
+
}
|
|
28277
|
+
}
|
|
27441
28278
|
rep.done(chalk2.green.bold("\u2713 Done"));
|
|
27442
28279
|
} catch (err) {
|
|
27443
28280
|
console.error(chalk2.red("Error:"), err);
|
|
@@ -27574,6 +28411,7 @@ program.command("search <query>").description("Search your server for relevant s
|
|
|
27574
28411
|
}
|
|
27575
28412
|
console.log();
|
|
27576
28413
|
}
|
|
28414
|
+
askForStar(results.length, chalk2.dim);
|
|
27577
28415
|
} catch (err) {
|
|
27578
28416
|
console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
|
|
27579
28417
|
process.exit(1);
|
|
@@ -27767,8 +28605,8 @@ memory.command("wake-up").description("Generate wake-up context (high-importance
|
|
|
27767
28605
|
try {
|
|
27768
28606
|
const identityFile = getIdentityFilePath();
|
|
27769
28607
|
let identity = "AI coding assistant";
|
|
27770
|
-
if (
|
|
27771
|
-
identity =
|
|
28608
|
+
if (existsSync56(identityFile)) {
|
|
28609
|
+
identity = readFileSync49(identityFile, "utf-8").trim();
|
|
27772
28610
|
}
|
|
27773
28611
|
const lines = [
|
|
27774
28612
|
chalk2.bold("# Wake-Up Context"),
|
|
@@ -27868,7 +28706,7 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
|
|
|
27868
28706
|
}
|
|
27869
28707
|
});
|
|
27870
28708
|
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) => {
|
|
27871
|
-
const { existsSync:
|
|
28709
|
+
const { existsSync: existsSync57, readFileSync: readFileSync50, statSync: statSync45 } = await import("fs");
|
|
27872
28710
|
const { execSync: execSync6 } = await import("child_process");
|
|
27873
28711
|
const rows = [];
|
|
27874
28712
|
const note = (ok, label, detail) => rows.push({ ok, label, detail });
|
|
@@ -27931,8 +28769,8 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
27931
28769
|
}
|
|
27932
28770
|
}
|
|
27933
28771
|
}
|
|
27934
|
-
const credFile =
|
|
27935
|
-
if (!
|
|
28772
|
+
const credFile = join61(getDataDir(), "credentials.json");
|
|
28773
|
+
if (!existsSync57(credFile)) {
|
|
27936
28774
|
note(false, "Credentials file", `missing \u2014 run \`chat-recall login <server-url>\``);
|
|
27937
28775
|
} else {
|
|
27938
28776
|
try {
|
|
@@ -27945,23 +28783,23 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
27945
28783
|
}
|
|
27946
28784
|
}
|
|
27947
28785
|
for (const hooksJson of claudeHookConfigFiles()) {
|
|
27948
|
-
const profile = basename27(
|
|
28786
|
+
const profile = basename27(dirname29(hooksJson));
|
|
27949
28787
|
const label = `Claude hooks (${profile})`;
|
|
27950
|
-
if (!
|
|
28788
|
+
if (!existsSync57(hooksJson)) {
|
|
27951
28789
|
note(false, label, `no ${hooksJson} \u2014 run \`chat-recall install-hooks\``);
|
|
27952
28790
|
continue;
|
|
27953
28791
|
}
|
|
27954
28792
|
try {
|
|
27955
28793
|
const readHooks = (file) => {
|
|
27956
|
-
if (!
|
|
28794
|
+
if (!existsSync57(file)) return {};
|
|
27957
28795
|
try {
|
|
27958
|
-
return JSON.parse(
|
|
28796
|
+
return JSON.parse(readFileSync50(file, "utf-8")).hooks || {};
|
|
27959
28797
|
} catch {
|
|
27960
28798
|
return {};
|
|
27961
28799
|
}
|
|
27962
28800
|
};
|
|
27963
28801
|
const cfg = readHooks(hooksJson);
|
|
27964
|
-
const settings2 = readHooks(
|
|
28802
|
+
const settings2 = readHooks(join61(dirname29(hooksJson), "settings.json"));
|
|
27965
28803
|
const all = ["SessionStart", "UserPromptSubmit", "Stop", "PreCompact", "SessionEnd"];
|
|
27966
28804
|
const hits = [];
|
|
27967
28805
|
for (const ev of all) {
|
|
@@ -27976,16 +28814,54 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
27976
28814
|
label,
|
|
27977
28815
|
hits.length === 0 ? "none \u2014 run `chat-recall install-hooks`" : `${hits.join(", ")}${hits.length < all.length ? ` (missing ${all.filter((e) => !hits.includes(e)).join(", ")} \u2014 run install-hooks)` : ""}`
|
|
27978
28816
|
);
|
|
28817
|
+
const boardEvents = ["UserPromptSubmit", "SessionEnd"];
|
|
28818
|
+
const boardHits = boardEvents.filter((ev) => [
|
|
28819
|
+
...Array.isArray(cfg[ev]) ? cfg[ev] : [],
|
|
28820
|
+
...Array.isArray(settings2[ev]) ? settings2[ev] : []
|
|
28821
|
+
].some((h) => {
|
|
28822
|
+
const cmd = h.hooks?.[0]?.command || "";
|
|
28823
|
+
return cmd.includes("task-hook") && (cmd.includes("--claim") || cmd.includes("--close"));
|
|
28824
|
+
}));
|
|
28825
|
+
note(
|
|
28826
|
+
boardHits.length === boardEvents.length,
|
|
28827
|
+
`Task board hooks (${profile})`,
|
|
28828
|
+
boardHits.length === 0 ? "not registered \u2014 run `chat-recall install-hooks`" : boardHits.length === boardEvents.length ? "claim on a prompt, close on a commit" : `only ${boardHits.join(", ")} \u2014 run \`chat-recall install-hooks\``
|
|
28829
|
+
);
|
|
27979
28830
|
} catch {
|
|
27980
28831
|
note(false, label, `${hooksJson} unparseable`);
|
|
27981
28832
|
}
|
|
27982
28833
|
}
|
|
28834
|
+
try {
|
|
28835
|
+
const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
|
|
28836
|
+
for (const target2 of reg.guardTargets()) {
|
|
28837
|
+
if (!existsSync57(target2.homeDir)) continue;
|
|
28838
|
+
const label = `Decision guard \u2014 ${target2.label}`;
|
|
28839
|
+
if (!existsSync57(target2.configPath)) {
|
|
28840
|
+
note(false, label, `not registered \u2014 run \`chat-recall install-hooks\``);
|
|
28841
|
+
continue;
|
|
28842
|
+
}
|
|
28843
|
+
if (target2.tool === "opencode") {
|
|
28844
|
+
note(true, label, target2.configPath);
|
|
28845
|
+
continue;
|
|
28846
|
+
}
|
|
28847
|
+
let registered = false;
|
|
28848
|
+
try {
|
|
28849
|
+
const cfg = JSON.parse(readFileSync50(target2.configPath, "utf-8"));
|
|
28850
|
+
registered = JSON.stringify(cfg).includes("chat_recall_guard");
|
|
28851
|
+
} catch {
|
|
28852
|
+
}
|
|
28853
|
+
const detail = registered && target2.tool === "codex" ? `${target2.configPath} \u2014 approve it in Codex with /hooks` : registered ? target2.configPath : `${target2.configPath} has no chat-recall entry \u2014 run \`chat-recall install-hooks\``;
|
|
28854
|
+
note(registered, label, detail);
|
|
28855
|
+
}
|
|
28856
|
+
} catch (err) {
|
|
28857
|
+
note(false, "Decision guard", `check failed: ${err}`);
|
|
28858
|
+
}
|
|
27983
28859
|
try {
|
|
27984
28860
|
const { skillTargets: skillTargets2, bundledSkillNames: bundledSkillNames2 } = await Promise.resolve().then(() => (init_install_skills(), install_skills_exports));
|
|
27985
28861
|
const want = bundledSkillNames2().length;
|
|
27986
28862
|
for (const t of skillTargets2()) {
|
|
27987
28863
|
if (!t.available) continue;
|
|
27988
|
-
const have = want === 0 ? 0 : bundledSkillNames2().filter((n) =>
|
|
28864
|
+
const have = want === 0 ? 0 : bundledSkillNames2().filter((n) => existsSync57(join61(t.dir, n, ".chat-recall-managed"))).length;
|
|
27989
28865
|
note(
|
|
27990
28866
|
have === want,
|
|
27991
28867
|
`Skills \u2014 ${t.label}`,
|
|
@@ -28060,14 +28936,14 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28060
28936
|
note(watchRunning, "Watch daemon", watchRunning ? "running" : "not running (run `chat-recall watch --install-service` to keep collection running)");
|
|
28061
28937
|
const dataDir = getDataDir();
|
|
28062
28938
|
const legacyPaths = [
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
|
|
28939
|
+
join61(dataDir, "cache.db"),
|
|
28940
|
+
join61(dataDir, "cache.db-wal"),
|
|
28941
|
+
join61(dataDir, "cache.db-shm"),
|
|
28942
|
+
join61(dataDir, "index", "lancedb"),
|
|
28943
|
+
join61(dataDir, "index", "knowledge_graph.db"),
|
|
28944
|
+
join61(dataDir, "index", "metadata.db")
|
|
28069
28945
|
];
|
|
28070
|
-
const legacyPresent = legacyPaths.filter((p) =>
|
|
28946
|
+
const legacyPresent = legacyPaths.filter((p) => existsSync57(p));
|
|
28071
28947
|
if (opts.purgeLocal) {
|
|
28072
28948
|
if (!target) {
|
|
28073
28949
|
note(false, "Purge legacy index", "refused \u2014 log in and `chat-recall sync` first, so your history is on the server before removing local copies");
|
|
@@ -28138,7 +29014,7 @@ companions.command("install").description("Install codeindex (downloads from mun
|
|
|
28138
29014
|
if (result.installed) {
|
|
28139
29015
|
const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
|
|
28140
29016
|
console.log(chalk2.green(`\u2713 codeindex installed (${sizeMb}) \u2192 ${result.path}`));
|
|
28141
|
-
const mcpJsonPath =
|
|
29017
|
+
const mcpJsonPath = join61(homedir23(), ".mcp.json");
|
|
28142
29018
|
const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
|
|
28143
29019
|
if (reg.added) console.log(chalk2.dim(` Registered as MCP server in ${mcpJsonPath}`));
|
|
28144
29020
|
else console.log(chalk2.dim(` Already registered as MCP server`));
|
|
@@ -28160,7 +29036,7 @@ companions.command("uninstall").description("Remove the codeindex binary chat-re
|
|
|
28160
29036
|
const r = uninstallCodeindex2();
|
|
28161
29037
|
if (r.removed) console.log(chalk2.green(`\u2713 Removed ${r.path}`));
|
|
28162
29038
|
else console.log(chalk2.dim(` Nothing to remove at ${r.path}`));
|
|
28163
|
-
const mcpJsonPath =
|
|
29039
|
+
const mcpJsonPath = join61(homedir23(), ".mcp.json");
|
|
28164
29040
|
const u = unregisterCodeindexMcp2(mcpJsonPath);
|
|
28165
29041
|
if (u.removed) console.log(chalk2.dim(` Unregistered MCP server from ${mcpJsonPath}`));
|
|
28166
29042
|
});
|
|
@@ -28711,10 +29587,10 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
|
|
|
28711
29587
|
try {
|
|
28712
29588
|
const { resolveProjectId: resolveProjectId2 } = await Promise.resolve().then(() => (init_project_resolver(), project_resolver_exports));
|
|
28713
29589
|
const { renderTeamTasksMd: renderTeamTasksMd2, parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
|
|
28714
|
-
const { writeFileSync:
|
|
28715
|
-
const { join:
|
|
28716
|
-
const file =
|
|
28717
|
-
if (!opts.force &&
|
|
29590
|
+
const { writeFileSync: writeFileSync24, readFileSync: readFileSync50, existsSync: existsSync57 } = await import("node:fs");
|
|
29591
|
+
const { join: join62 } = await import("node:path");
|
|
29592
|
+
const file = join62(cwd, "TEAM_TASKS.md");
|
|
29593
|
+
if (!opts.force && existsSync57(file) && parseTeamTasksFile2(readFileSync50(file, "utf8")).length > 0) {
|
|
28718
29594
|
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.");
|
|
28719
29595
|
process.exit(1);
|
|
28720
29596
|
}
|
|
@@ -28722,7 +29598,7 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
|
|
|
28722
29598
|
const pid = resolved && resolved.source !== "ignored" ? resolved.id : "";
|
|
28723
29599
|
const { tasks: tasks2 } = await serverGet("/api/tasks?assignee=@me");
|
|
28724
29600
|
const mine = tasks2.filter((t) => !pid || !t.projectId || t.projectId === pid);
|
|
28725
|
-
|
|
29601
|
+
writeFileSync24(file, renderTeamTasksMd2(mine));
|
|
28726
29602
|
console.log(chalk2.green("\u2713 wrote"), "TEAM_TASKS.md", chalk2.gray(`(${mine.length} task(s))`), "\u2014 edit, then", chalk2.bold("chat-recall tasks push"));
|
|
28727
29603
|
} catch (e) {
|
|
28728
29604
|
console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
|
|
@@ -28733,14 +29609,14 @@ tasks.command("push [dir]").description("Push TEAM_TASKS.md status edits back to
|
|
|
28733
29609
|
const cwd = dir || process.cwd();
|
|
28734
29610
|
try {
|
|
28735
29611
|
const { parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
|
|
28736
|
-
const { readFileSync:
|
|
28737
|
-
const { join:
|
|
28738
|
-
const file =
|
|
28739
|
-
if (!
|
|
29612
|
+
const { readFileSync: readFileSync50, existsSync: existsSync57 } = await import("node:fs");
|
|
29613
|
+
const { join: join62 } = await import("node:path");
|
|
29614
|
+
const file = join62(cwd, "TEAM_TASKS.md");
|
|
29615
|
+
if (!existsSync57(file)) {
|
|
28740
29616
|
console.error(chalk2.red("no TEAM_TASKS.md here \u2014"), "run", chalk2.bold("chat-recall tasks pull"), "first");
|
|
28741
29617
|
process.exit(1);
|
|
28742
29618
|
}
|
|
28743
|
-
const edits = parseTeamTasksFile2(
|
|
29619
|
+
const edits = parseTeamTasksFile2(readFileSync50(file, "utf8"));
|
|
28744
29620
|
let n = 0;
|
|
28745
29621
|
for (const e of edits) {
|
|
28746
29622
|
await serverPatch(`/api/tasks/${encodeURIComponent(e.id)}`, { status: e.intent });
|
|
@@ -28846,8 +29722,8 @@ team.command("publish <type> <name> <file>").description("Publish a local file a
|
|
|
28846
29722
|
console.error(chalk2.red(`Invalid tool: ${opts.tool}. Must be one of: ${validTools.join(", ")}`));
|
|
28847
29723
|
process.exit(1);
|
|
28848
29724
|
}
|
|
28849
|
-
const { readFileSync:
|
|
28850
|
-
const body =
|
|
29725
|
+
const { readFileSync: readFileSync50 } = await import("fs");
|
|
29726
|
+
const body = readFileSync50(file);
|
|
28851
29727
|
const { teamPublish: teamPublish2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
|
|
28852
29728
|
const a = await teamPublish2({
|
|
28853
29729
|
type,
|
|
@@ -29033,18 +29909,201 @@ vault.command("forget").description("Remove the stored passphrase from this mach
|
|
|
29033
29909
|
const { keyringDelete: keyringDelete2 } = await Promise.resolve().then(() => (init_keyring(), keyring_exports));
|
|
29034
29910
|
console.log(keyringDelete2() ? chalk2.green("\u2713 Passphrase removed from the OS keyring.") : chalk2.dim("Nothing stored in the OS keyring."));
|
|
29035
29911
|
}));
|
|
29036
|
-
program.command("
|
|
29037
|
-
|
|
29912
|
+
program.command("guard").description("Check a pending tool call against the decision register (used by the pre-execution hooks)").option("--harness <name>", "claude | codex | agy | cursor | opencode", "claude").option("--project <id>", "Resolve decisions for this project").option("--enforce", "Return a blocking verdict instead of a warning").action(async (opts) => {
|
|
29913
|
+
try {
|
|
29914
|
+
const { namesFrom: namesFrom2, writeVerdict: writeVerdict2, explain: explain2 } = await Promise.resolve().then(() => (init_guard_adapters(), guard_adapters_exports));
|
|
29915
|
+
const harness = opts.harness ?? "claude";
|
|
29916
|
+
const raw = await new Promise((resolve3) => {
|
|
29917
|
+
let buf = "";
|
|
29918
|
+
const t = setTimeout(() => resolve3(buf), 2e3);
|
|
29919
|
+
process.stdin.setEncoding("utf8");
|
|
29920
|
+
process.stdin.on("data", (c) => {
|
|
29921
|
+
buf += c;
|
|
29922
|
+
});
|
|
29923
|
+
process.stdin.on("end", () => {
|
|
29924
|
+
clearTimeout(t);
|
|
29925
|
+
resolve3(buf);
|
|
29926
|
+
});
|
|
29927
|
+
process.stdin.on("error", () => {
|
|
29928
|
+
clearTimeout(t);
|
|
29929
|
+
resolve3(buf);
|
|
29930
|
+
});
|
|
29931
|
+
});
|
|
29932
|
+
if (!raw.trim()) return;
|
|
29933
|
+
let payload;
|
|
29934
|
+
try {
|
|
29935
|
+
payload = JSON.parse(raw);
|
|
29936
|
+
} catch {
|
|
29937
|
+
return;
|
|
29938
|
+
}
|
|
29939
|
+
const { names, via } = namesFrom2(harness, payload);
|
|
29940
|
+
if (!names.length) return;
|
|
29941
|
+
const target = firstTarget();
|
|
29942
|
+
if (!target) return;
|
|
29943
|
+
const { fetchWithTimeout: fetchWithTimeout2 } = await Promise.resolve().then(() => (init_http(), http_exports));
|
|
29944
|
+
const headers = { "content-type": "application/json" };
|
|
29945
|
+
if (target.token) headers.authorization = `Bearer ${target.token}`;
|
|
29946
|
+
const res = await fetchWithTimeout2(
|
|
29947
|
+
`${target.base}/api/decisions/check`,
|
|
29948
|
+
{ method: "POST", headers, body: JSON.stringify({ names, via, project: opts.project }) },
|
|
29949
|
+
4e3
|
|
29950
|
+
);
|
|
29951
|
+
if (!res.ok) return;
|
|
29952
|
+
const out = await res.json();
|
|
29953
|
+
if (!out.findings?.length) return;
|
|
29954
|
+
const message = explain2(out.findings);
|
|
29955
|
+
const verdict = opts.enforce ? "deny" : out.verdict === "ask" ? "ask" : "warn";
|
|
29956
|
+
const written = writeVerdict2(harness, verdict, message);
|
|
29957
|
+
if (written.json) process.stdout.write(JSON.stringify(written.json));
|
|
29958
|
+
} catch {
|
|
29959
|
+
}
|
|
29960
|
+
});
|
|
29961
|
+
program.command("task-hook").description("Move a board card when a prompt or a commit names its id (used by the task hooks)").option("--claim", "UserPromptSubmit: claim every open card the prompt names").option("--close", "SessionEnd: close every open card a recent commit names").option("--since <hours>", "How far back --close reads commits", "24").action(async (opts) => {
|
|
29962
|
+
if (process.env.CHAT_RECALL_TASK_HOOK === "0") return;
|
|
29963
|
+
const deadline = setTimeout(() => process.exit(0), opts.close ? 2e4 : 1e4);
|
|
29964
|
+
deadline.unref();
|
|
29965
|
+
try {
|
|
29966
|
+
const {
|
|
29967
|
+
extractTaskIds: extractTaskIds2,
|
|
29968
|
+
parseCommitLog: parseCommitLog2,
|
|
29969
|
+
planCloses: planCloses2,
|
|
29970
|
+
parseChangedFiles: parseChangedFiles2,
|
|
29971
|
+
mayClaim: mayClaim2,
|
|
29972
|
+
mayClose: mayClose2,
|
|
29973
|
+
COMMIT_LOG_FORMAT: COMMIT_LOG_FORMAT2
|
|
29974
|
+
} = await Promise.resolve().then(() => (init_task_hook(), task_hook_exports));
|
|
29975
|
+
const raw = await new Promise((resolve3) => {
|
|
29976
|
+
let buf = "";
|
|
29977
|
+
const t = setTimeout(() => resolve3(buf), 2e3);
|
|
29978
|
+
process.stdin.setEncoding("utf8");
|
|
29979
|
+
process.stdin.on("data", (c) => {
|
|
29980
|
+
buf += c;
|
|
29981
|
+
});
|
|
29982
|
+
process.stdin.on("end", () => {
|
|
29983
|
+
clearTimeout(t);
|
|
29984
|
+
resolve3(buf);
|
|
29985
|
+
});
|
|
29986
|
+
process.stdin.on("error", () => {
|
|
29987
|
+
clearTimeout(t);
|
|
29988
|
+
resolve3(buf);
|
|
29989
|
+
});
|
|
29990
|
+
});
|
|
29991
|
+
if (!raw.trim()) return;
|
|
29992
|
+
let payload;
|
|
29993
|
+
try {
|
|
29994
|
+
payload = JSON.parse(raw);
|
|
29995
|
+
} catch {
|
|
29996
|
+
return;
|
|
29997
|
+
}
|
|
29998
|
+
const sessionId = typeof payload.session_id === "string" ? payload.session_id : "";
|
|
29999
|
+
if (!sessionId) return;
|
|
30000
|
+
const cwd = typeof payload.cwd === "string" && payload.cwd ? payload.cwd : process.cwd();
|
|
30001
|
+
const target = firstTarget();
|
|
30002
|
+
if (!target) return;
|
|
30003
|
+
const { fetchWithTimeout: fetchWithTimeout2 } = await Promise.resolve().then(() => (init_http(), http_exports));
|
|
30004
|
+
const headers = { "content-type": "application/json" };
|
|
30005
|
+
if (target.token) headers.authorization = `Bearer ${target.token}`;
|
|
30006
|
+
const readCard = async (id) => {
|
|
30007
|
+
const res = await fetchWithTimeout2(
|
|
30008
|
+
`${target.base}/api/tasks/${encodeURIComponent(id)}`,
|
|
30009
|
+
{ headers },
|
|
30010
|
+
4e3
|
|
30011
|
+
);
|
|
30012
|
+
if (!res.ok) return null;
|
|
30013
|
+
const body = await res.json().catch(() => ({}));
|
|
30014
|
+
return body.task ?? null;
|
|
30015
|
+
};
|
|
30016
|
+
const patchCard = async (id, body) => {
|
|
30017
|
+
const res = await fetchWithTimeout2(
|
|
30018
|
+
`${target.base}/api/tasks/${encodeURIComponent(id)}`,
|
|
30019
|
+
{ method: "PATCH", headers, body: JSON.stringify(body) },
|
|
30020
|
+
6e3
|
|
30021
|
+
);
|
|
30022
|
+
return res.ok;
|
|
30023
|
+
};
|
|
30024
|
+
if (opts.claim) {
|
|
30025
|
+
const ids = extractTaskIds2(payload.prompt);
|
|
30026
|
+
if (!ids.length) return;
|
|
30027
|
+
const claimed = [];
|
|
30028
|
+
for (const id of ids) {
|
|
30029
|
+
const card = await readCard(id);
|
|
30030
|
+
if (!card) continue;
|
|
30031
|
+
if (!mayClaim2(card.status, card.linkedSessionId, sessionId)) continue;
|
|
30032
|
+
if (await patchCard(id, { status: "in_progress", linkedSessionId: sessionId })) {
|
|
30033
|
+
claimed.push(`${id} \u2014 ${card.title}`);
|
|
30034
|
+
}
|
|
30035
|
+
}
|
|
30036
|
+
if (claimed.length) {
|
|
30037
|
+
process.stdout.write("<!-- chat-recall-task-claim -->\n");
|
|
30038
|
+
process.stdout.write(
|
|
30039
|
+
"**Claimed on the chat-recall board**, linked to this session:\n" + claimed.map((c) => `- \u{1F7E1} ${c}`).join("\n") + "\n\nWrite the card id in the commit message and the card closes itself at session end.\n"
|
|
30040
|
+
);
|
|
30041
|
+
}
|
|
30042
|
+
return;
|
|
30043
|
+
}
|
|
30044
|
+
if (!opts.close) return;
|
|
30045
|
+
const { execFileSync: execFileSync4 } = await import("node:child_process");
|
|
30046
|
+
const git2 = (args) => {
|
|
30047
|
+
try {
|
|
30048
|
+
return execFileSync4("git", args, {
|
|
30049
|
+
cwd,
|
|
30050
|
+
encoding: "utf8",
|
|
30051
|
+
timeout: 5e3,
|
|
30052
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
30053
|
+
});
|
|
30054
|
+
} catch {
|
|
30055
|
+
return "";
|
|
30056
|
+
}
|
|
30057
|
+
};
|
|
30058
|
+
if (!git2(["rev-parse", "--is-inside-work-tree"]).trim().startsWith("true")) return;
|
|
30059
|
+
const hours = Math.max(1, Math.min(720, Number(opts.since) || 24));
|
|
30060
|
+
const intents = planCloses2(parseCommitLog2(git2([
|
|
30061
|
+
"log",
|
|
30062
|
+
`--since=${hours}.hours.ago`,
|
|
30063
|
+
"--max-count=200",
|
|
30064
|
+
"--no-merges",
|
|
30065
|
+
`--pretty=format:${COMMIT_LOG_FORMAT2}`
|
|
30066
|
+
])));
|
|
30067
|
+
if (!intents.length) return;
|
|
30068
|
+
for (const intent of intents) {
|
|
30069
|
+
const card = await readCard(intent.id);
|
|
30070
|
+
if (!card) continue;
|
|
30071
|
+
if (!mayClose2(card.status)) continue;
|
|
30072
|
+
const files = parseChangedFiles2(
|
|
30073
|
+
intent.commits.map((sha) => git2(["show", "--name-only", "--pretty=format:", sha])).join("\n")
|
|
30074
|
+
);
|
|
30075
|
+
const shortShas = intent.commits.map((s) => s.slice(0, 8)).join(", ");
|
|
30076
|
+
await patchCard(intent.id, {
|
|
30077
|
+
status: "done",
|
|
30078
|
+
linkedSessionId: sessionId,
|
|
30079
|
+
doneEvidence: {
|
|
30080
|
+
commits: intent.commits,
|
|
30081
|
+
files,
|
|
30082
|
+
summary: `Closed by ${intent.commits.length === 1 ? "commit" : "commits"} ${shortShas}, which named this card.`
|
|
30083
|
+
}
|
|
30084
|
+
});
|
|
30085
|
+
}
|
|
30086
|
+
} catch {
|
|
30087
|
+
}
|
|
30088
|
+
});
|
|
30089
|
+
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) => {
|
|
30090
|
+
const { mkdirSync: mkdirSync23, copyFileSync: copyFileSync4, chmodSync: chmodSync5, existsSync: existsSync57, readFileSync: readFileSync50, writeFileSync: writeFileSync24, statSync: statSync45, unlinkSync: unlinkSync4 } = await import("fs");
|
|
29038
30091
|
const { fileURLToPath: fileURLToPath6 } = await import("url");
|
|
30092
|
+
const { boardHookEntries: boardHookEntries2, isBoardHookEntry: isBoardHookEntry2, registerBoardHooks: registerBoardHooks2, resolveCliEntry: resolveCliEntry2 } = await Promise.resolve().then(() => (init_board_hooks(), board_hooks_exports));
|
|
29039
30093
|
const here = fileURLToPath6(new URL(".", import.meta.url));
|
|
29040
30094
|
const findHook = (name) => {
|
|
29041
30095
|
const candidates2 = [
|
|
29042
|
-
|
|
29043
|
-
|
|
30096
|
+
join61(here, "..", "hooks", name),
|
|
30097
|
+
join61(here, "..", "..", "hooks", name),
|
|
30098
|
+
// The monorepo root, for a run straight from the checkout. The copy
|
|
30099
|
+
// under packages/cli/hooks is written by the package build, so a
|
|
30100
|
+
// freshly added script is only at the root until that build runs.
|
|
30101
|
+
join61(here, "..", "..", "..", "hooks", name)
|
|
29044
30102
|
];
|
|
29045
|
-
return candidates2.find((p) =>
|
|
30103
|
+
return candidates2.find((p) => existsSync57(p));
|
|
29046
30104
|
};
|
|
29047
30105
|
const sourceSaveHook = findHook("chat_recall_save_hook.sh");
|
|
30106
|
+
const sourceGuardHook = findHook("chat_recall_guard_hook.sh");
|
|
29048
30107
|
const sourceResumeHook = findHook("chat_recall_resume_hook.sh");
|
|
29049
30108
|
const sourceEscalateHook = findHook("chat_recall_escalate_hook.sh");
|
|
29050
30109
|
const sourceWakeupHook = findHook("chat_recall_wakeup_hook.sh");
|
|
@@ -29053,15 +30112,16 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29053
30112
|
process.exit(1);
|
|
29054
30113
|
}
|
|
29055
30114
|
const hooksDir = getHooksDir();
|
|
29056
|
-
const installedSaveHook =
|
|
29057
|
-
const
|
|
29058
|
-
const
|
|
29059
|
-
const
|
|
30115
|
+
const installedSaveHook = join61(hooksDir, "chat_recall_save_hook.sh");
|
|
30116
|
+
const installedGuardHook = join61(hooksDir, "chat_recall_guard_hook.sh");
|
|
30117
|
+
const installedResumeHook = join61(hooksDir, "chat_recall_resume_hook.sh");
|
|
30118
|
+
const installedEscalateHook = join61(hooksDir, "chat_recall_escalate_hook.sh");
|
|
30119
|
+
const installedWakeupHook = join61(hooksDir, "chat_recall_wakeup_hook.sh");
|
|
29060
30120
|
const wakeupWiredInSettings = (home) => {
|
|
29061
|
-
const settings2 =
|
|
29062
|
-
if (!
|
|
30121
|
+
const settings2 = join61(home, "settings.json");
|
|
30122
|
+
if (!existsSync57(settings2)) return false;
|
|
29063
30123
|
try {
|
|
29064
|
-
const cfg = JSON.parse(
|
|
30124
|
+
const cfg = JSON.parse(readFileSync50(settings2, "utf-8"));
|
|
29065
30125
|
const arr = Array.isArray(cfg.hooks?.SessionStart) ? cfg.hooks.SessionStart : [];
|
|
29066
30126
|
return arr.some((h) => (h?.hooks?.[0]?.command || "").includes("chat_recall_wakeup_hook"));
|
|
29067
30127
|
} catch {
|
|
@@ -29070,9 +30130,9 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29070
30130
|
};
|
|
29071
30131
|
const hookConfigFiles = claudeHookConfigFiles();
|
|
29072
30132
|
const readHookConfig = (file) => {
|
|
29073
|
-
if (!
|
|
30133
|
+
if (!existsSync57(file)) return { hooks: {} };
|
|
29074
30134
|
try {
|
|
29075
|
-
const c = JSON.parse(
|
|
30135
|
+
const c = JSON.parse(readFileSync50(file, "utf-8"));
|
|
29076
30136
|
if (!c.hooks || typeof c.hooks !== "object") c.hooks = {};
|
|
29077
30137
|
return c;
|
|
29078
30138
|
} catch (err) {
|
|
@@ -29083,9 +30143,58 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29083
30143
|
};
|
|
29084
30144
|
const matchesOurs = (h) => {
|
|
29085
30145
|
const cmd = h?.hooks?.[0]?.command;
|
|
29086
|
-
return typeof cmd === "string" && (cmd.includes("chat_recall_save_hook.sh") || cmd.includes("chat_recall_resume_hook.sh") || cmd.includes("chat_recall_escalate_hook.sh") || cmd.includes("chat_recall_wakeup_hook.sh"));
|
|
30146
|
+
return typeof cmd === "string" && (cmd.includes("chat_recall_save_hook.sh") || cmd.includes("chat_recall_resume_hook.sh") || cmd.includes("chat_recall_escalate_hook.sh") || cmd.includes("chat_recall_wakeup_hook.sh") || cmd.includes("chat_recall_guard_hook.sh")) || isBoardHookEntry2(h);
|
|
30147
|
+
};
|
|
30148
|
+
const HOOK_EVENTS = ["Stop", "PreCompact", "UserPromptSubmit", "SessionEnd", "SessionStart", "PreToolUse"];
|
|
30149
|
+
const readJson4 = (file) => {
|
|
30150
|
+
if (!existsSync57(file)) return {};
|
|
30151
|
+
try {
|
|
30152
|
+
return JSON.parse(readFileSync50(file, "utf-8"));
|
|
30153
|
+
} catch {
|
|
30154
|
+
return {};
|
|
30155
|
+
}
|
|
30156
|
+
};
|
|
30157
|
+
const addGuardToOtherTools = async (hookPath) => {
|
|
30158
|
+
const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
|
|
30159
|
+
const notes = [];
|
|
30160
|
+
for (const target of reg.guardTargets()) {
|
|
30161
|
+
if (!existsSync57(target.homeDir)) continue;
|
|
30162
|
+
const wrapper = join61(hooksDir, reg.guardWrapperName(target.tool));
|
|
30163
|
+
writeFileSync24(wrapper, reg.guardWrapperSource(target.tool, hookPath));
|
|
30164
|
+
chmodSync5(wrapper, 493);
|
|
30165
|
+
mkdirSync23(dirname29(target.configPath), { recursive: true });
|
|
30166
|
+
if (target.tool === "opencode") {
|
|
30167
|
+
writeFileSync24(target.configPath, reg.opencodePluginSource(wrapper));
|
|
30168
|
+
} else {
|
|
30169
|
+
const merged = reg.registerGuard(target.tool, readJson4(target.configPath), reg.guardCommand(wrapper));
|
|
30170
|
+
writeFileSync24(target.configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
30171
|
+
}
|
|
30172
|
+
console.log(chalk2.green(`\u2713 Registered the guard for ${target.label}`));
|
|
30173
|
+
console.log(chalk2.dim(` ${target.configPath}`));
|
|
30174
|
+
if (target.note) notes.push(`${target.label}: ${target.note}`);
|
|
30175
|
+
}
|
|
30176
|
+
for (const n of notes) console.log(chalk2.yellow(` ! ${n}`));
|
|
30177
|
+
};
|
|
30178
|
+
const removeGuardFromOtherTools = async () => {
|
|
30179
|
+
const reg = await Promise.resolve().then(() => (init_guard_registration(), guard_registration_exports));
|
|
30180
|
+
let removed = 0;
|
|
30181
|
+
for (const target of reg.guardTargets()) {
|
|
30182
|
+
if (!existsSync57(target.configPath)) continue;
|
|
30183
|
+
if (target.tool === "opencode") {
|
|
30184
|
+
const body = readFileSync50(target.configPath, "utf-8");
|
|
30185
|
+
if (!body.includes("chat-recall install-hooks")) continue;
|
|
30186
|
+
unlinkSync4(target.configPath);
|
|
30187
|
+
removed += 1;
|
|
30188
|
+
} else {
|
|
30189
|
+
const r = reg.unregisterGuard(target.tool, readJson4(target.configPath));
|
|
30190
|
+
if (r.removed === 0) continue;
|
|
30191
|
+
writeFileSync24(target.configPath, JSON.stringify(r.config, null, 2) + "\n");
|
|
30192
|
+
removed += r.removed;
|
|
30193
|
+
}
|
|
30194
|
+
console.log(chalk2.green(`\u2713 Removed the guard from ${target.label} (${target.configPath})`));
|
|
30195
|
+
}
|
|
30196
|
+
return removed;
|
|
29087
30197
|
};
|
|
29088
|
-
const HOOK_EVENTS = ["Stop", "PreCompact", "UserPromptSubmit", "SessionEnd", "SessionStart"];
|
|
29089
30198
|
if (opts.uninstall) {
|
|
29090
30199
|
let total = 0;
|
|
29091
30200
|
for (const hooksJson of hookConfigFiles) {
|
|
@@ -29098,29 +30207,39 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29098
30207
|
if (filtered.length) config2.hooks[event] = filtered;
|
|
29099
30208
|
else delete config2.hooks[event];
|
|
29100
30209
|
}
|
|
29101
|
-
if (removed === 0 && !
|
|
29102
|
-
|
|
30210
|
+
if (removed === 0 && !existsSync57(hooksJson)) continue;
|
|
30211
|
+
writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
|
|
29103
30212
|
total += removed;
|
|
29104
30213
|
console.log(chalk2.green(`\u2713 Removed ${removed} chat-recall hook entr${removed === 1 ? "y" : "ies"} from ${hooksJson}`));
|
|
29105
30214
|
}
|
|
30215
|
+
total += await removeGuardFromOtherTools();
|
|
29106
30216
|
if (total === 0) console.log(chalk2.dim("No chat-recall hook entries were registered."));
|
|
29107
30217
|
console.log(chalk2.dim(` (Hook scripts left in ${hooksDir} \u2014 delete manually if you want.)`));
|
|
29108
30218
|
return;
|
|
29109
30219
|
}
|
|
29110
30220
|
if (process.platform === "win32") {
|
|
29111
|
-
|
|
30221
|
+
if (opts.taskBoard !== false && !opts.uninstall) {
|
|
30222
|
+
const written = registerBoardHooks2(hookConfigFiles, process.execPath, resolveCliEntry2(here));
|
|
30223
|
+
if (written.length) {
|
|
30224
|
+
console.log(chalk2.green("\u2713 Registered the chat-recall task-board hooks"));
|
|
30225
|
+
console.log(chalk2.dim(" A prompt naming a card id claims it; a commit message naming one closes it."));
|
|
30226
|
+
for (const f of written) console.log(chalk2.dim(` ${f}`));
|
|
30227
|
+
console.log();
|
|
30228
|
+
}
|
|
30229
|
+
}
|
|
30230
|
+
console.log(chalk2.yellow("The five shell hooks are not installed on Windows."));
|
|
29112
30231
|
console.log(chalk2.dim(" They are POSIX shell scripts, and Claude Code runs hook commands through"));
|
|
29113
30232
|
console.log(chalk2.dim(" cmd.exe, which cannot execute them. Registering them would put an error on"));
|
|
29114
30233
|
console.log(chalk2.dim(" every turn instead of saving anything."));
|
|
29115
30234
|
console.log(chalk2.dim(""));
|
|
29116
|
-
console.log(chalk2.dim(" Everything else works: indexing, sync, search
|
|
29117
|
-
console.log(chalk2.dim("
|
|
29118
|
-
console.log(chalk2.dim("
|
|
30235
|
+
console.log(chalk2.dim(" Everything else works: indexing, sync, search, the MCP server and the"));
|
|
30236
|
+
console.log(chalk2.dim(" board hooks above. The five scripts add automatic fact-saving and the"));
|
|
30237
|
+
console.log(chalk2.dim(" wake-up bundle; recall_wake_up gives an agent the same context on demand."));
|
|
29119
30238
|
console.log(chalk2.dim(""));
|
|
29120
30239
|
console.log(chalk2.dim(" Under WSL, run this from inside the WSL shell and it installs normally."));
|
|
29121
30240
|
return;
|
|
29122
30241
|
}
|
|
29123
|
-
|
|
30242
|
+
mkdirSync23(hooksDir, { recursive: true });
|
|
29124
30243
|
copyFileSync4(sourceSaveHook, installedSaveHook);
|
|
29125
30244
|
chmodSync5(installedSaveHook, 493);
|
|
29126
30245
|
const installResume = opts.resumeHint !== false && !!sourceResumeHook;
|
|
@@ -29133,40 +30252,56 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29133
30252
|
copyFileSync4(sourceEscalateHook, installedEscalateHook);
|
|
29134
30253
|
chmodSync5(installedEscalateHook, 493);
|
|
29135
30254
|
}
|
|
30255
|
+
const installGuard = opts.guard !== false && !!sourceGuardHook;
|
|
30256
|
+
if (opts.guard !== false && !sourceGuardHook) {
|
|
30257
|
+
console.error(chalk2.yellow("! chat_recall_guard_hook.sh is not in this package \u2014 the decision guard is not installed."));
|
|
30258
|
+
}
|
|
30259
|
+
if (installGuard) {
|
|
30260
|
+
copyFileSync4(sourceGuardHook, installedGuardHook);
|
|
30261
|
+
chmodSync5(installedGuardHook, 493);
|
|
30262
|
+
}
|
|
29136
30263
|
const installWakeup = opts.wakeup !== false && !!sourceWakeupHook;
|
|
29137
30264
|
if (installWakeup) {
|
|
29138
30265
|
copyFileSync4(sourceWakeupHook, installedWakeupHook);
|
|
29139
30266
|
chmodSync5(installedWakeupHook, 493);
|
|
29140
30267
|
}
|
|
30268
|
+
const installTaskBoard = opts.taskBoard !== false;
|
|
29141
30269
|
const stopEntry = { matcher: "", hooks: [{ type: "command", command: installedSaveHook }] };
|
|
29142
30270
|
const precompactEntry = { matcher: "", hooks: [{ type: "command", command: `"${installedSaveHook}" --precompact` }] };
|
|
29143
30271
|
const resumeEntry = { matcher: "", hooks: [{ type: "command", command: installedResumeHook }] };
|
|
29144
30272
|
const escalateEntry = { matcher: "", hooks: [{ type: "command", command: installedEscalateHook }] };
|
|
29145
30273
|
const wakeupEntry = { matcher: "startup|clear", hooks: [{ type: "command", command: installedWakeupHook }] };
|
|
30274
|
+
const guardEntry = {
|
|
30275
|
+
matcher: "Bash|Edit|Write|MultiEdit|NotebookEdit",
|
|
30276
|
+
hooks: [{ type: "command", command: installedGuardHook }]
|
|
30277
|
+
};
|
|
30278
|
+
const boardEntries = boardHookEntries2(process.execPath, resolveCliEntry2(here));
|
|
29146
30279
|
const events = [
|
|
29147
30280
|
["Stop", stopEntry],
|
|
29148
30281
|
["PreCompact", precompactEntry]
|
|
29149
30282
|
];
|
|
29150
30283
|
if (installResume) events.push(["UserPromptSubmit", resumeEntry]);
|
|
29151
30284
|
if (installEscalate) events.push(["SessionEnd", escalateEntry]);
|
|
30285
|
+
if (installGuard) events.push(["PreToolUse", guardEntry]);
|
|
30286
|
+
if (installTaskBoard) for (const pair of boardEntries) events.push(pair);
|
|
29152
30287
|
const wakeupSkipped = [];
|
|
29153
30288
|
for (const hooksJson of hookConfigFiles) {
|
|
29154
30289
|
const config2 = readHookConfig(hooksJson);
|
|
29155
|
-
const home =
|
|
30290
|
+
const home = dirname29(hooksJson);
|
|
29156
30291
|
const alreadyWired = wakeupWiredInSettings(home);
|
|
29157
|
-
if (alreadyWired) wakeupSkipped.push(
|
|
30292
|
+
if (alreadyWired) wakeupSkipped.push(join61(home, "settings.json"));
|
|
29158
30293
|
const perProfile = installWakeup && !alreadyWired ? [...events, ["SessionStart", wakeupEntry]] : events;
|
|
29159
30294
|
for (const event of HOOK_EVENTS) {
|
|
29160
30295
|
const arr = Array.isArray(config2.hooks[event]) ? config2.hooks[event] : [];
|
|
29161
30296
|
const without = arr.filter((h) => !matchesOurs(h));
|
|
29162
|
-
const
|
|
29163
|
-
if (wanted) without.push(wanted[1]);
|
|
30297
|
+
for (const [, entry] of perProfile.filter(([e]) => e === event)) without.push(entry);
|
|
29164
30298
|
if (without.length) config2.hooks[event] = without;
|
|
29165
30299
|
else delete config2.hooks[event];
|
|
29166
30300
|
}
|
|
29167
|
-
|
|
29168
|
-
|
|
30301
|
+
mkdirSync23(dirname29(hooksJson), { recursive: true });
|
|
30302
|
+
writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
|
|
29169
30303
|
}
|
|
30304
|
+
if (installGuard) await addGuardToOtherTools(installedGuardHook);
|
|
29170
30305
|
const saveSz = statSync45(installedSaveHook).size;
|
|
29171
30306
|
console.log(chalk2.green(`\u2713 Installed chat-recall save hook (${saveSz} bytes)`));
|
|
29172
30307
|
if (installResume) {
|
|
@@ -29177,6 +30312,14 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29177
30312
|
const escalateSz = statSync45(installedEscalateHook).size;
|
|
29178
30313
|
console.log(chalk2.green(`\u2713 Installed chat-recall learnings-escalation hook (${escalateSz} bytes)`));
|
|
29179
30314
|
}
|
|
30315
|
+
if (installGuard) {
|
|
30316
|
+
const guardSz = statSync45(installedGuardHook).size;
|
|
30317
|
+
console.log(chalk2.green(`\u2713 Installed chat-recall decision guard (${guardSz} bytes)`));
|
|
30318
|
+
}
|
|
30319
|
+
if (installTaskBoard) {
|
|
30320
|
+
console.log(chalk2.green("\u2713 Registered the chat-recall task-board hooks"));
|
|
30321
|
+
console.log(chalk2.dim(" A prompt naming a card id claims it; a commit message naming one closes it."));
|
|
30322
|
+
}
|
|
29180
30323
|
if (installWakeup) {
|
|
29181
30324
|
const wakeupSz = statSync45(installedWakeupHook).size;
|
|
29182
30325
|
console.log(chalk2.green(`\u2713 Installed chat-recall wake-up hook (${wakeupSz} bytes)`));
|
|
@@ -29188,7 +30331,12 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29188
30331
|
for (const [i, f] of hookConfigFiles.entries()) {
|
|
29189
30332
|
console.log(chalk2.dim(` ${i === 0 ? "config: " : " "} ${f}`));
|
|
29190
30333
|
}
|
|
29191
|
-
|
|
30334
|
+
const registeredEvents = ["Stop", "PreCompact"];
|
|
30335
|
+
if (installResume || installTaskBoard) registeredEvents.push("UserPromptSubmit");
|
|
30336
|
+
if (installEscalate || installTaskBoard) registeredEvents.push("SessionEnd");
|
|
30337
|
+
if (installWakeup) registeredEvents.push("SessionStart");
|
|
30338
|
+
if (installGuard) registeredEvents.push("PreToolUse");
|
|
30339
|
+
console.log(chalk2.dim(` events: ${registeredEvents.join(", ")}`));
|
|
29192
30340
|
console.log();
|
|
29193
30341
|
console.log(chalk2.dim("Run `chat-recall install-hooks --uninstall` to remove later."));
|
|
29194
30342
|
if (!installResume) {
|
|
@@ -29210,7 +30358,7 @@ program.command("dossier <project>").description("Generate a project dossier (ov
|
|
|
29210
30358
|
);
|
|
29211
30359
|
const md = dossier.markdown;
|
|
29212
30360
|
if (options.out) {
|
|
29213
|
-
|
|
30361
|
+
writeFileSync23(options.out, md);
|
|
29214
30362
|
console.log(chalk2.green(`Wrote dossier to ${options.out} (${md.length} chars)`));
|
|
29215
30363
|
} else {
|
|
29216
30364
|
console.log(md);
|
|
@@ -29468,8 +30616,8 @@ include.command("list", { isDefault: true }).description("Show paths opted back
|
|
|
29468
30616
|
include.command("project <path>").description("Re-permit a path under a personal folder (substring match; e.g. ~/Documents/code)").action(async (path) => {
|
|
29469
30617
|
const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
29470
30618
|
const { resolve: resolve3 } = await import("node:path");
|
|
29471
|
-
const { homedir:
|
|
29472
|
-
const abs = resolve3(path.replace(/^~(?=\/|$)/,
|
|
30619
|
+
const { homedir: homedir24 } = await import("node:os");
|
|
30620
|
+
const abs = resolve3(path.replace(/^~(?=\/|$)/, homedir24()));
|
|
29473
30621
|
const s = loadSettings2();
|
|
29474
30622
|
s.sync.includeProjects = s.sync.includeProjects || [];
|
|
29475
30623
|
if (s.sync.includeProjects.includes(abs)) {
|
|
@@ -29483,8 +30631,8 @@ include.command("project <path>").description("Re-permit a path under a personal
|
|
|
29483
30631
|
include.command("remove <path>").description("Undo an opt-in (personal folder goes back to skipped)").action(async (path) => {
|
|
29484
30632
|
const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
29485
30633
|
const { resolve: resolve3 } = await import("node:path");
|
|
29486
|
-
const { homedir:
|
|
29487
|
-
const abs = resolve3(path.replace(/^~(?=\/|$)/,
|
|
30634
|
+
const { homedir: homedir24 } = await import("node:os");
|
|
30635
|
+
const abs = resolve3(path.replace(/^~(?=\/|$)/, homedir24()));
|
|
29488
30636
|
const s = loadSettings2();
|
|
29489
30637
|
const before = (s.sync.includeProjects || []).length;
|
|
29490
30638
|
s.sync.includeProjects = (s.sync.includeProjects || []).filter((p) => p !== path && p !== abs);
|
|
@@ -29623,8 +30771,8 @@ program.command("projects").description("List distinct project ids discovered on
|
|
|
29623
30771
|
exclude.command("project <path>").description("Exclude a project path from syncing (substring match on the project path; e.g. ~/.claude-pr-bot)").action(async (path) => {
|
|
29624
30772
|
const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
29625
30773
|
const { resolve: resolve3 } = await import("node:path");
|
|
29626
|
-
const { homedir:
|
|
29627
|
-
const abs = resolve3(path.replace(/^~(?=\/|$)/,
|
|
30774
|
+
const { homedir: homedir24 } = await import("node:os");
|
|
30775
|
+
const abs = resolve3(path.replace(/^~(?=\/|$)/, homedir24()));
|
|
29628
30776
|
const s = loadSettings2();
|
|
29629
30777
|
if (s.sync.excludeProjects.includes(abs)) {
|
|
29630
30778
|
console.log(chalk2.dim(`Already excluded: ${abs}`));
|
|
@@ -29653,8 +30801,8 @@ exclude.command("tool <tool>").description("Exclude an AI tool entirely (claude
|
|
|
29653
30801
|
exclude.command("remove <value>").description("Remove an exclusion (tool name or project path)").action(async (value) => {
|
|
29654
30802
|
const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
29655
30803
|
const { resolve: resolve3 } = await import("node:path");
|
|
29656
|
-
const { homedir:
|
|
29657
|
-
const abs = resolve3(value.replace(/^~(?=\/|$)/,
|
|
30804
|
+
const { homedir: homedir24 } = await import("node:os");
|
|
30805
|
+
const abs = resolve3(value.replace(/^~(?=\/|$)/, homedir24()));
|
|
29658
30806
|
const s = loadSettings2();
|
|
29659
30807
|
const beforeTools = s.sync.excludeTools.length;
|
|
29660
30808
|
const beforeProjects = s.sync.excludeProjects.length;
|