chat-recall 0.6.4 → 0.6.6
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 +48 -3
- 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 +1508 -358
- package/dist/cli.js.map +1 -1
- package/dist/mcp-relay.js +1 -1
- package/dist/mcp.js +295 -42
- 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 +117 -61
- package/hooks/chat_recall_guard_hook.sh +54 -0
- package/package.json +1 -1
- package/skills/chat-recall/SKILL.md +2 -1
- package/skills/chat-recall-memory/SKILL.md +18 -2
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);
|
|
@@ -14322,12 +14322,12 @@ function extractEntities(text, context3 = {}) {
|
|
|
14322
14322
|
if (chosen && looksLikeStrictTool(chosen) && looksLikeStrictTool(rejected)) {
|
|
14323
14323
|
const c = canonTool(chosen), r = canonTool(rejected);
|
|
14324
14324
|
if (projectName)
|
|
14325
|
-
addTriple(projectName, "chose", c,
|
|
14326
|
-
addTriple(projectName || "project", "rejected", r,
|
|
14327
|
-
addTriple(c, "chosen_over", r,
|
|
14325
|
+
addTriple(projectName, "chose", c, DECISION_GUESS_CONFIDENCE);
|
|
14326
|
+
addTriple(projectName || "project", "rejected", r, DECISION_GUESS_CONFIDENCE);
|
|
14327
|
+
addTriple(c, "chosen_over", r, DECISION_GUESS_CONFIDENCE);
|
|
14328
14328
|
}
|
|
14329
14329
|
} else if (chosen && looksLikeDecisionObject(chosen) && projectName) {
|
|
14330
|
-
addTriple(projectName, "chose", canonTool(chosen),
|
|
14330
|
+
addTriple(projectName, "chose", canonTool(chosen), DECISION_GUESS_CONFIDENCE);
|
|
14331
14331
|
}
|
|
14332
14332
|
}
|
|
14333
14333
|
}
|
|
@@ -14364,7 +14364,7 @@ function extractEntities(text, context3 = {}) {
|
|
|
14364
14364
|
}
|
|
14365
14365
|
return triples;
|
|
14366
14366
|
}
|
|
14367
|
-
var KNOWN_TOOLS, AMBIGUOUS_TOOLS, toolContextRe, TOOL_PATTERNS, DECISION_PATTERNS2, DECISION_OBJECT_STOPWORDS, KNOWN_TOOL_NAMES, CANONICAL_TOOL, canonTool, PROJECT_PATH_PATTERN, PEOPLE_PATTERNS, NOT_PEOPLE;
|
|
14367
|
+
var KNOWN_TOOLS, AMBIGUOUS_TOOLS, toolContextRe, TOOL_PATTERNS, DECISION_PATTERNS2, DECISION_OBJECT_STOPWORDS, KNOWN_TOOL_NAMES, CANONICAL_TOOL, canonTool, DECISION_GUESS_CONFIDENCE, PROJECT_PATH_PATTERN, PEOPLE_PATTERNS, NOT_PEOPLE;
|
|
14368
14368
|
var init_entity_extractor = __esm({
|
|
14369
14369
|
"../engine/dist/core/entity-extractor.js"() {
|
|
14370
14370
|
"use strict";
|
|
@@ -14585,6 +14585,7 @@ var init_entity_extractor = __esm({
|
|
|
14585
14585
|
"nextjs": "next.js"
|
|
14586
14586
|
};
|
|
14587
14587
|
canonTool = (n) => CANONICAL_TOOL[n.toLowerCase()] ?? n;
|
|
14588
|
+
DECISION_GUESS_CONFIDENCE = 0.6;
|
|
14588
14589
|
PROJECT_PATH_PATTERN = /\/(?:home|Users)\/\w+\/(?:code|projects|work|dev|src)\/(?:\w+\/)?([a-zA-Z][a-zA-Z0-9_-]{1,40})/g;
|
|
14589
14590
|
PEOPLE_PATTERNS = [
|
|
14590
14591
|
/\b(?:my name is|I'm|I am)\s+([A-Z][a-z]{1,15}(?:\s+[A-Z][a-z]{1,15})?)/g,
|
|
@@ -18816,7 +18817,7 @@ var init_http = __esm({
|
|
|
18816
18817
|
CLI_VERSION = JSON.parse(readFileSync31(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
18817
18818
|
} catch {
|
|
18818
18819
|
try {
|
|
18819
|
-
if (true) CLI_VERSION = "0.6.
|
|
18820
|
+
if (true) CLI_VERSION = "0.6.6";
|
|
18820
18821
|
} catch {
|
|
18821
18822
|
}
|
|
18822
18823
|
}
|
|
@@ -19336,7 +19337,7 @@ var init_telemetry = __esm({
|
|
|
19336
19337
|
init_sync_client();
|
|
19337
19338
|
cliVersion2 = () => {
|
|
19338
19339
|
try {
|
|
19339
|
-
return true ? "0.6.
|
|
19340
|
+
return true ? "0.6.6" : "dev";
|
|
19340
19341
|
} catch {
|
|
19341
19342
|
return "dev";
|
|
19342
19343
|
}
|
|
@@ -19665,12 +19666,16 @@ var init_client_events = __esm({
|
|
|
19665
19666
|
// src/auto-update.ts
|
|
19666
19667
|
var auto_update_exports = {};
|
|
19667
19668
|
__export(auto_update_exports, {
|
|
19669
|
+
clearUpdateFailures: () => clearUpdateFailures,
|
|
19668
19670
|
compareVersions: () => compareVersions,
|
|
19669
19671
|
executeAutoUpdate: () => executeAutoUpdate,
|
|
19672
|
+
hasGivenUpOn: () => hasGivenUpOn,
|
|
19670
19673
|
installedVersion: () => installedVersion,
|
|
19671
19674
|
isAutoUpdateEnabled: () => isAutoUpdateEnabled,
|
|
19672
19675
|
newerOf: () => newerOf,
|
|
19676
|
+
noteUpdateFailure: () => noteUpdateFailure,
|
|
19673
19677
|
planAutoUpdate: () => planAutoUpdate,
|
|
19678
|
+
readUpdateState: () => readUpdateState,
|
|
19674
19679
|
runAutoUpdate: () => runAutoUpdate,
|
|
19675
19680
|
runningPrefix: () => runningPrefix,
|
|
19676
19681
|
sweepStaleStaging: () => sweepStaleStaging,
|
|
@@ -19679,9 +19684,9 @@ __export(auto_update_exports, {
|
|
|
19679
19684
|
import { execSync as execSync2 } from "node:child_process";
|
|
19680
19685
|
import { readFileSync as readFileSync34 } from "node:fs";
|
|
19681
19686
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19682
|
-
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";
|
|
19683
19688
|
import { join as join41, dirname as dirname18, sep as sep2 } from "node:path";
|
|
19684
|
-
import { tmpdir as tmpdir2 } from "node:os";
|
|
19689
|
+
import { tmpdir as tmpdir2, homedir as homedir15 } from "node:os";
|
|
19685
19690
|
import { createHash as createHash8 } from "node:crypto";
|
|
19686
19691
|
function compareVersions(a, b) {
|
|
19687
19692
|
const pa = String(a).split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
@@ -19795,16 +19800,25 @@ function runningPrefix(moduleUrl = import.meta.url) {
|
|
|
19795
19800
|
const beforeNodeModules = here.slice(0, at);
|
|
19796
19801
|
return beforeNodeModules.endsWith(`${sep2}lib`) ? beforeNodeModules.slice(0, -4) : beforeNodeModules;
|
|
19797
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
|
+
}
|
|
19798
19812
|
function realInstall(tgz) {
|
|
19799
19813
|
const prefix = runningPrefix();
|
|
19800
19814
|
if (prefix) {
|
|
19801
|
-
|
|
19815
|
+
npmInstall(`npm install -g --prefix "${prefix}" "${tgz}"`);
|
|
19802
19816
|
return;
|
|
19803
19817
|
}
|
|
19804
19818
|
try {
|
|
19805
|
-
|
|
19819
|
+
npmInstall(`npm install -g "${tgz}"`);
|
|
19806
19820
|
} catch {
|
|
19807
|
-
|
|
19821
|
+
npmInstall(`npm install -g --prefix "${process.env.HOME}/.local" "${tgz}"`);
|
|
19808
19822
|
}
|
|
19809
19823
|
}
|
|
19810
19824
|
function realRestart(platform2) {
|
|
@@ -19812,6 +19826,37 @@ function realRestart(platform2) {
|
|
|
19812
19826
|
else if (platform2 === "darwin") execSync2("launchctl kickstart -k gui/$(id -u)/com.chat-recall.watch", { stdio: "ignore", shell: "/bin/bash" });
|
|
19813
19827
|
else if (platform2 === "win32") execSync2("schtasks /End /TN chat-recall-watch & schtasks /Run /TN chat-recall-watch", { stdio: "ignore" });
|
|
19814
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
|
+
}
|
|
19815
19860
|
async function runAutoUpdate(base3, authHeaders, ownVersion3, deps) {
|
|
19816
19861
|
let caps;
|
|
19817
19862
|
try {
|
|
@@ -19834,6 +19879,10 @@ async function runAutoUpdate(base3, authHeaders, ownVersion3, deps) {
|
|
|
19834
19879
|
}
|
|
19835
19880
|
return { updated: false, reason: plan.reason };
|
|
19836
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
|
+
}
|
|
19837
19886
|
const result = await executeAutoUpdate(plan, {
|
|
19838
19887
|
download: deps?.download ?? realDownload,
|
|
19839
19888
|
install: deps?.install ?? realInstall,
|
|
@@ -19841,20 +19890,27 @@ async function runAutoUpdate(base3, authHeaders, ownVersion3, deps) {
|
|
|
19841
19890
|
platform: deps?.platform
|
|
19842
19891
|
});
|
|
19843
19892
|
if (!result.updated) {
|
|
19844
|
-
|
|
19845
|
-
|
|
19846
|
-
|
|
19847
|
-
|
|
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
|
+
}
|
|
19848
19901
|
}
|
|
19902
|
+
} else {
|
|
19903
|
+
clearUpdateFailures();
|
|
19849
19904
|
}
|
|
19850
19905
|
return result;
|
|
19851
19906
|
}
|
|
19852
|
-
var tarballUrl, reportedStandingSkip;
|
|
19907
|
+
var tarballUrl, GIVE_UP_AFTER, reportedStandingSkip;
|
|
19853
19908
|
var init_auto_update = __esm({
|
|
19854
19909
|
"src/auto-update.ts"() {
|
|
19855
19910
|
"use strict";
|
|
19856
19911
|
init_http();
|
|
19857
19912
|
tarballUrl = (base3) => `${base3.replace(/\/+$/, "")}/install/chat-recall.tgz`;
|
|
19913
|
+
GIVE_UP_AFTER = 3;
|
|
19858
19914
|
reportedStandingSkip = /* @__PURE__ */ new Set();
|
|
19859
19915
|
}
|
|
19860
19916
|
});
|
|
@@ -19889,7 +19945,7 @@ __export(sync_client_exports, {
|
|
|
19889
19945
|
syncSessions: () => syncSessions
|
|
19890
19946
|
});
|
|
19891
19947
|
import { join as join42, dirname as dirname19, basename as basename20 } from "node:path";
|
|
19892
|
-
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";
|
|
19893
19949
|
import { tmpdir as tmpdir3, hostname as hostname2 } from "node:os";
|
|
19894
19950
|
import { createHash as createHash9 } from "node:crypto";
|
|
19895
19951
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -19924,7 +19980,7 @@ function classifyError(msg, err) {
|
|
|
19924
19980
|
}
|
|
19925
19981
|
function saveCredentials(c) {
|
|
19926
19982
|
assertTransportSafe(c.serverUrl);
|
|
19927
|
-
|
|
19983
|
+
mkdirSync10(dirname19(credPath()), { recursive: true });
|
|
19928
19984
|
const targets = loadAllCredentials().filter((t) => t.serverUrl !== c.serverUrl);
|
|
19929
19985
|
targets.unshift(c);
|
|
19930
19986
|
writeFileSync9(credPath(), JSON.stringify({ targets }, null, 2));
|
|
@@ -20019,7 +20075,7 @@ function persistSyncConfig(base3, config2) {
|
|
|
20019
20075
|
try {
|
|
20020
20076
|
const all = readPersistedSyncConfigs();
|
|
20021
20077
|
all[base3] = { fetchedAt: Date.now(), config: config2 };
|
|
20022
|
-
|
|
20078
|
+
mkdirSync10(dirname19(syncConfigCachePath()), { recursive: true });
|
|
20023
20079
|
writeFileSync9(syncConfigCachePath(), JSON.stringify(all, null, 2));
|
|
20024
20080
|
} catch {
|
|
20025
20081
|
}
|
|
@@ -21425,7 +21481,7 @@ var init_sync_client = __esm({
|
|
|
21425
21481
|
ownVersion = JSON.parse(readFileSync35(join42(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
21426
21482
|
} catch {
|
|
21427
21483
|
try {
|
|
21428
|
-
if (true) ownVersion = "0.6.
|
|
21484
|
+
if (true) ownVersion = "0.6.6";
|
|
21429
21485
|
} catch {
|
|
21430
21486
|
}
|
|
21431
21487
|
}
|
|
@@ -21677,16 +21733,16 @@ __export(mcp_clients_exports, {
|
|
|
21677
21733
|
inspectMcpClients: () => inspectMcpClients,
|
|
21678
21734
|
registerMcpEverywhere: () => registerMcpEverywhere
|
|
21679
21735
|
});
|
|
21680
|
-
import { existsSync as
|
|
21681
|
-
import { homedir as
|
|
21682
|
-
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";
|
|
21683
21739
|
function envHome(varName, fallback) {
|
|
21684
21740
|
const v = process.env[varName];
|
|
21685
21741
|
return v && v.trim() ? v.trim() : fallback;
|
|
21686
21742
|
}
|
|
21687
21743
|
function writeFileMkdir(path, body) {
|
|
21688
|
-
|
|
21689
|
-
|
|
21744
|
+
mkdirSync12(dirname21(path), { recursive: true });
|
|
21745
|
+
writeFileSync12(path, body);
|
|
21690
21746
|
}
|
|
21691
21747
|
function readJsonObject(path) {
|
|
21692
21748
|
const raw = readFileSync37(path, "utf-8");
|
|
@@ -21696,7 +21752,7 @@ function readJsonObject(path) {
|
|
|
21696
21752
|
}
|
|
21697
21753
|
function registerMcpServersJson(target, path, spec) {
|
|
21698
21754
|
let cfg = {};
|
|
21699
|
-
if (
|
|
21755
|
+
if (existsSync43(path)) {
|
|
21700
21756
|
try {
|
|
21701
21757
|
cfg = readJsonObject(path);
|
|
21702
21758
|
} catch (err) {
|
|
@@ -21725,7 +21781,7 @@ function registerOpencodeJson(path, spec) {
|
|
|
21725
21781
|
const id = "opencode";
|
|
21726
21782
|
const label = "OpenCode";
|
|
21727
21783
|
let cfg = {};
|
|
21728
|
-
if (
|
|
21784
|
+
if (existsSync43(path)) {
|
|
21729
21785
|
try {
|
|
21730
21786
|
cfg = readJsonObject(path);
|
|
21731
21787
|
} catch (err) {
|
|
@@ -21764,7 +21820,7 @@ function registerCodexToml(path, spec) {
|
|
|
21764
21820
|
const id = "codex";
|
|
21765
21821
|
const label = "Codex";
|
|
21766
21822
|
const block = codexBlock(spec);
|
|
21767
|
-
const prev =
|
|
21823
|
+
const prev = existsSync43(path) ? readFileSync37(path, "utf-8") : "";
|
|
21768
21824
|
const lines = prev.length ? prev.split("\n") : [];
|
|
21769
21825
|
const start = lines.findIndex((l) => l.trim() === CODEX_HEADER);
|
|
21770
21826
|
if (start === -1) {
|
|
@@ -21796,16 +21852,16 @@ function getMcpClient(id) {
|
|
|
21796
21852
|
return t;
|
|
21797
21853
|
}
|
|
21798
21854
|
function registerMcpEverywhere(spec, opts = {}) {
|
|
21799
|
-
const home = opts.home ??
|
|
21855
|
+
const home = opts.home ?? homedir16();
|
|
21800
21856
|
const forced = new Set(opts.extraIds ?? []);
|
|
21801
21857
|
return MCP_CLIENTS.filter((c) => c.isPresent(home) || forced.has(c.id)).map((c) => c.register(spec, home));
|
|
21802
21858
|
}
|
|
21803
21859
|
function inspectMcpClients(opts = {}) {
|
|
21804
|
-
const home = opts.home ??
|
|
21860
|
+
const home = opts.home ?? homedir16();
|
|
21805
21861
|
return MCP_CLIENTS.map((c) => {
|
|
21806
21862
|
const path = c.configPath(home);
|
|
21807
21863
|
let registered = false;
|
|
21808
|
-
if (
|
|
21864
|
+
if (existsSync43(path)) {
|
|
21809
21865
|
try {
|
|
21810
21866
|
const raw = readFileSync37(path, "utf-8");
|
|
21811
21867
|
registered = c.id === "codex" ? raw.includes(CODEX_HEADER) : !!(JSON.parse(raw).mcpServers?.[MCP_SERVER_NAME] ?? JSON.parse(raw).mcp?.[MCP_SERVER_NAME]);
|
|
@@ -21827,7 +21883,7 @@ var init_mcp_clients = __esm({
|
|
|
21827
21883
|
id: "claude",
|
|
21828
21884
|
label: "Claude Code",
|
|
21829
21885
|
bin: "claude",
|
|
21830
|
-
configPath: (home) =>
|
|
21886
|
+
configPath: (home) => join46(home, ".mcp.json"),
|
|
21831
21887
|
// Claude Code is the reference client and `init`'s historical target, so we
|
|
21832
21888
|
// always write it — a fresh machine has no ~/.claude yet on first install.
|
|
21833
21889
|
isPresent: () => true,
|
|
@@ -21839,10 +21895,10 @@ var init_mcp_clients = __esm({
|
|
|
21839
21895
|
id: "codex",
|
|
21840
21896
|
label: "Codex",
|
|
21841
21897
|
bin: "codex",
|
|
21842
|
-
configPath: (home) =>
|
|
21898
|
+
configPath: (home) => join46(envHome("CHAT_RECALL_CODEX_HOME", join46(home, ".codex")), "config.toml"),
|
|
21843
21899
|
isPresent(home) {
|
|
21844
21900
|
const p = this.configPath(home);
|
|
21845
|
-
return
|
|
21901
|
+
return existsSync43(p) || existsSync43(dirname21(p));
|
|
21846
21902
|
},
|
|
21847
21903
|
register(spec, home) {
|
|
21848
21904
|
return registerCodexToml(this.configPath(home), spec);
|
|
@@ -21852,10 +21908,10 @@ var init_mcp_clients = __esm({
|
|
|
21852
21908
|
id: "opencode",
|
|
21853
21909
|
label: "OpenCode",
|
|
21854
21910
|
bin: "opencode",
|
|
21855
|
-
configPath: (home) =>
|
|
21911
|
+
configPath: (home) => join46(envHome("XDG_CONFIG_HOME", join46(home, ".config")), "opencode", "opencode.json"),
|
|
21856
21912
|
isPresent(home) {
|
|
21857
21913
|
const p = this.configPath(home);
|
|
21858
|
-
return
|
|
21914
|
+
return existsSync43(p) || existsSync43(dirname21(p));
|
|
21859
21915
|
},
|
|
21860
21916
|
register(spec, home) {
|
|
21861
21917
|
return registerOpencodeJson(this.configPath(home), spec);
|
|
@@ -21867,9 +21923,9 @@ var init_mcp_clients = __esm({
|
|
|
21867
21923
|
id: "agy",
|
|
21868
21924
|
label: "Antigravity",
|
|
21869
21925
|
bin: "agy",
|
|
21870
|
-
configPath: (home) =>
|
|
21926
|
+
configPath: (home) => join46(home, ".gemini", "config", "mcp_config.json"),
|
|
21871
21927
|
isPresent(home) {
|
|
21872
|
-
return
|
|
21928
|
+
return existsSync43(join46(home, ".gemini", "antigravity-cli")) || existsSync43(this.configPath(home));
|
|
21873
21929
|
},
|
|
21874
21930
|
register(spec, home) {
|
|
21875
21931
|
return registerMcpServersJson(this, this.configPath(home), spec);
|
|
@@ -21879,10 +21935,10 @@ var init_mcp_clients = __esm({
|
|
|
21879
21935
|
id: "cursor",
|
|
21880
21936
|
label: "Cursor",
|
|
21881
21937
|
bin: "cursor",
|
|
21882
|
-
configPath: (home) =>
|
|
21938
|
+
configPath: (home) => join46(home, ".cursor", "mcp.json"),
|
|
21883
21939
|
isPresent(home) {
|
|
21884
21940
|
const p = this.configPath(home);
|
|
21885
|
-
return
|
|
21941
|
+
return existsSync43(p) || existsSync43(dirname21(p));
|
|
21886
21942
|
},
|
|
21887
21943
|
register(spec, home) {
|
|
21888
21944
|
return registerMcpServersJson(this, this.configPath(home), spec);
|
|
@@ -21906,9 +21962,9 @@ __export(companions_exports, {
|
|
|
21906
21962
|
uninstallCodeindex: () => uninstallCodeindex,
|
|
21907
21963
|
unregisterCodeindexMcp: () => unregisterCodeindexMcp
|
|
21908
21964
|
});
|
|
21909
|
-
import { existsSync as
|
|
21910
|
-
import { homedir as
|
|
21911
|
-
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";
|
|
21912
21968
|
import { execSync as execSync3 } from "node:child_process";
|
|
21913
21969
|
import { createHash as createHash10 } from "node:crypto";
|
|
21914
21970
|
function expectedCodeindexSha256(artifact) {
|
|
@@ -22010,7 +22066,7 @@ async function installCodeindex(opts = {}) {
|
|
|
22010
22066
|
return status;
|
|
22011
22067
|
if (!status.prebuiltAvailable)
|
|
22012
22068
|
return status;
|
|
22013
|
-
|
|
22069
|
+
mkdirSync13(CODEINDEX_INSTALL_DIR, { recursive: true });
|
|
22014
22070
|
const tmpPath = `${CODEINDEX_BIN_PATH}.partial`;
|
|
22015
22071
|
try {
|
|
22016
22072
|
await downloadArtifact(status.artifactName, tmpPath);
|
|
@@ -22029,7 +22085,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
|
|
|
22029
22085
|
renameSync5(tmpPath, CODEINDEX_BIN_PATH);
|
|
22030
22086
|
} catch (err) {
|
|
22031
22087
|
try {
|
|
22032
|
-
if (
|
|
22088
|
+
if (existsSync44(tmpPath))
|
|
22033
22089
|
unlinkSync3(tmpPath);
|
|
22034
22090
|
} catch {
|
|
22035
22091
|
}
|
|
@@ -22038,7 +22094,7 @@ Refusing to install. The release may have been re-uploaded, or something is serv
|
|
|
22038
22094
|
return checkCodeindexStatus();
|
|
22039
22095
|
}
|
|
22040
22096
|
function uninstallCodeindex() {
|
|
22041
|
-
if (
|
|
22097
|
+
if (existsSync44(CODEINDEX_BIN_PATH)) {
|
|
22042
22098
|
unlinkSync3(CODEINDEX_BIN_PATH);
|
|
22043
22099
|
return { removed: true, path: CODEINDEX_BIN_PATH };
|
|
22044
22100
|
}
|
|
@@ -22046,7 +22102,7 @@ function uninstallCodeindex() {
|
|
|
22046
22102
|
}
|
|
22047
22103
|
function registerCodeindexMcp(mcpJsonPath, binaryPath) {
|
|
22048
22104
|
let cfg = {};
|
|
22049
|
-
if (
|
|
22105
|
+
if (existsSync44(mcpJsonPath)) {
|
|
22050
22106
|
try {
|
|
22051
22107
|
cfg = JSON.parse(readFileSync38(mcpJsonPath, "utf-8"));
|
|
22052
22108
|
} catch {
|
|
@@ -22057,11 +22113,11 @@ function registerCodeindexMcp(mcpJsonPath, binaryPath) {
|
|
|
22057
22113
|
if (cfg.mcpServers["codeindex"])
|
|
22058
22114
|
return { added: false };
|
|
22059
22115
|
cfg.mcpServers["codeindex"] = { command: binaryPath, args: ["--mcp"] };
|
|
22060
|
-
|
|
22116
|
+
writeFileSync13(mcpJsonPath, JSON.stringify(cfg, null, 2));
|
|
22061
22117
|
return { added: true };
|
|
22062
22118
|
}
|
|
22063
22119
|
function unregisterCodeindexMcp(mcpJsonPath) {
|
|
22064
|
-
if (!
|
|
22120
|
+
if (!existsSync44(mcpJsonPath))
|
|
22065
22121
|
return { removed: false };
|
|
22066
22122
|
let cfg;
|
|
22067
22123
|
try {
|
|
@@ -22072,7 +22128,7 @@ function unregisterCodeindexMcp(mcpJsonPath) {
|
|
|
22072
22128
|
if (!cfg.mcpServers?.["codeindex"])
|
|
22073
22129
|
return { removed: false };
|
|
22074
22130
|
delete cfg.mcpServers["codeindex"];
|
|
22075
|
-
|
|
22131
|
+
writeFileSync13(mcpJsonPath, JSON.stringify(cfg, null, 2));
|
|
22076
22132
|
return { removed: true };
|
|
22077
22133
|
}
|
|
22078
22134
|
var CODEINDEX_REPO, CODEINDEX_VERSION, CODEINDEX_SHA256, CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME, CODEINDEX_BIN_PATH;
|
|
@@ -22090,9 +22146,9 @@ var init_companions = __esm({
|
|
|
22090
22146
|
"x86_64-windows": "a96fc8c8949bb399a44cc0615b12e458460a529c69f01e8829cc39a20f45ab29",
|
|
22091
22147
|
"aarch64-windows": "42620d56272cd9ba9d785569d8d754d6b4a3e80f36f28db3e0c483fd600ed117"
|
|
22092
22148
|
};
|
|
22093
|
-
CODEINDEX_INSTALL_DIR =
|
|
22149
|
+
CODEINDEX_INSTALL_DIR = join47(homedir17(), ".local", "bin");
|
|
22094
22150
|
CODEINDEX_BIN_NAME = process.platform === "win32" ? "codeindex.exe" : "codeindex";
|
|
22095
|
-
CODEINDEX_BIN_PATH =
|
|
22151
|
+
CODEINDEX_BIN_PATH = join47(CODEINDEX_INSTALL_DIR, CODEINDEX_BIN_NAME);
|
|
22096
22152
|
}
|
|
22097
22153
|
});
|
|
22098
22154
|
|
|
@@ -22107,11 +22163,11 @@ __export(install_skills_exports, {
|
|
|
22107
22163
|
skillsSourceDir: () => skillsSourceDir,
|
|
22108
22164
|
uninstallSkills: () => uninstallSkills
|
|
22109
22165
|
});
|
|
22110
|
-
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";
|
|
22111
22167
|
import { createHash as createHash11 } from "crypto";
|
|
22112
|
-
import { join as
|
|
22168
|
+
import { join as join48, basename as basename21 } from "path";
|
|
22113
22169
|
function skillsSourceDir() {
|
|
22114
|
-
return
|
|
22170
|
+
return join48(import.meta.dirname, "..", "skills");
|
|
22115
22171
|
}
|
|
22116
22172
|
function version() {
|
|
22117
22173
|
try {
|
|
@@ -22124,9 +22180,9 @@ function skillsFingerprint() {
|
|
|
22124
22180
|
const src = skillsSourceDir();
|
|
22125
22181
|
const h = createHash11("sha256");
|
|
22126
22182
|
for (const name of bundledSkillNames()) {
|
|
22127
|
-
for (const rel of filesUnder(
|
|
22183
|
+
for (const rel of filesUnder(join48(src, name)).sort()) {
|
|
22128
22184
|
h.update(name + "/" + rel);
|
|
22129
|
-
h.update(readFileSync39(
|
|
22185
|
+
h.update(readFileSync39(join48(src, name, rel)));
|
|
22130
22186
|
}
|
|
22131
22187
|
}
|
|
22132
22188
|
return h.digest("hex").slice(0, 12);
|
|
@@ -22136,7 +22192,7 @@ function filesUnder(dir, prefix = "") {
|
|
|
22136
22192
|
for (const e of readdirSync33(dir, { withFileTypes: true })) {
|
|
22137
22193
|
if (e.name === MARKER) continue;
|
|
22138
22194
|
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
22139
|
-
if (e.isDirectory()) out.push(...filesUnder(
|
|
22195
|
+
if (e.isDirectory()) out.push(...filesUnder(join48(dir, e.name), rel));
|
|
22140
22196
|
else out.push(rel);
|
|
22141
22197
|
}
|
|
22142
22198
|
return out;
|
|
@@ -22146,8 +22202,8 @@ function skillStamp() {
|
|
|
22146
22202
|
}
|
|
22147
22203
|
function bundledSkillNames() {
|
|
22148
22204
|
const src = skillsSourceDir();
|
|
22149
|
-
if (!
|
|
22150
|
-
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();
|
|
22151
22207
|
}
|
|
22152
22208
|
function realPathOrSelf(p) {
|
|
22153
22209
|
try {
|
|
@@ -22160,7 +22216,7 @@ function claudeSkillTargets() {
|
|
|
22160
22216
|
const targets = [];
|
|
22161
22217
|
const seen = /* @__PURE__ */ new Set();
|
|
22162
22218
|
for (const home of claudeHomeDirs()) {
|
|
22163
|
-
const dir =
|
|
22219
|
+
const dir = join48(home, "skills");
|
|
22164
22220
|
const key2 = realPathOrSelf(dir);
|
|
22165
22221
|
if (seen.has(key2)) continue;
|
|
22166
22222
|
seen.add(key2);
|
|
@@ -22171,7 +22227,7 @@ function claudeSkillTargets() {
|
|
|
22171
22227
|
dir,
|
|
22172
22228
|
// Judge each profile on ITS OWN transcripts, not the primary's: a home
|
|
22173
22229
|
// with no projects/ is a directory someone made, not a Claude install.
|
|
22174
|
-
available:
|
|
22230
|
+
available: existsSync45(join48(home, "projects"))
|
|
22175
22231
|
});
|
|
22176
22232
|
}
|
|
22177
22233
|
if (targets.length === 0) {
|
|
@@ -22186,16 +22242,16 @@ function skillTargets() {
|
|
|
22186
22242
|
// both produces "skill conflict" duplicate warnings. Every tool we support
|
|
22187
22243
|
// has its own dir, so per-tool coverage is complete.
|
|
22188
22244
|
...claudeSkillTargets(),
|
|
22189
|
-
{ id: "agy", label: "Antigravity", dir:
|
|
22245
|
+
{ id: "agy", label: "Antigravity", dir: join48(agyBackend.homeDir(), "skills"), available: agyBackend.isAvailable() },
|
|
22190
22246
|
{ id: "codex", label: "Codex", dir: codexBackend.skillsDir(), available: codexBackend.isAvailable() },
|
|
22191
22247
|
{ id: "opencode", label: "OpenCode", dir: opencodeBackend.skillsDir(), available: opencodeBackend.isAvailable() },
|
|
22192
22248
|
// Cursor DOES have its own dir (~/.cursor/skills). `skills-cursor` next to
|
|
22193
22249
|
// it is Cursor's bundled set — read-only, never an install target.
|
|
22194
|
-
{ id: "cursor", label: "Cursor", dir:
|
|
22250
|
+
{ id: "cursor", label: "Cursor", dir: join48(cursorHomeDir(), "skills"), available: cursorBackend.isAvailable() }
|
|
22195
22251
|
];
|
|
22196
22252
|
}
|
|
22197
22253
|
function isManaged(dir) {
|
|
22198
|
-
return
|
|
22254
|
+
return existsSync45(join48(dir, MARKER));
|
|
22199
22255
|
}
|
|
22200
22256
|
function installSkills(opts = {}) {
|
|
22201
22257
|
const onlyAvailable = opts.onlyAvailable ?? true;
|
|
@@ -22212,15 +22268,15 @@ function installSkills(opts = {}) {
|
|
|
22212
22268
|
continue;
|
|
22213
22269
|
}
|
|
22214
22270
|
for (const name of names) {
|
|
22215
|
-
const dest =
|
|
22216
|
-
if (
|
|
22271
|
+
const dest = join48(t.dir, name);
|
|
22272
|
+
if (existsSync45(dest) && !isManaged(dest)) {
|
|
22217
22273
|
skippedUserOwned.push(name);
|
|
22218
22274
|
continue;
|
|
22219
22275
|
}
|
|
22220
|
-
|
|
22276
|
+
mkdirSync14(t.dir, { recursive: true });
|
|
22221
22277
|
rmSync7(dest, { recursive: true, force: true });
|
|
22222
|
-
cpSync(
|
|
22223
|
-
|
|
22278
|
+
cpSync(join48(src, name), dest, { recursive: true });
|
|
22279
|
+
writeFileSync14(join48(dest, MARKER), `${stamp}
|
|
22224
22280
|
`);
|
|
22225
22281
|
installed.push(name);
|
|
22226
22282
|
}
|
|
@@ -22233,13 +22289,13 @@ function uninstallSkills() {
|
|
|
22233
22289
|
const perTarget = [];
|
|
22234
22290
|
for (const t of skillTargets()) {
|
|
22235
22291
|
const removed = [];
|
|
22236
|
-
if (!
|
|
22292
|
+
if (!existsSync45(t.dir)) {
|
|
22237
22293
|
perTarget.push({ id: t.id, label: t.label, removed });
|
|
22238
22294
|
continue;
|
|
22239
22295
|
}
|
|
22240
22296
|
for (const entry of readdirSync33(t.dir, { withFileTypes: true })) {
|
|
22241
22297
|
if (!entry.isDirectory()) continue;
|
|
22242
|
-
const dir =
|
|
22298
|
+
const dir = join48(t.dir, entry.name);
|
|
22243
22299
|
if (isManaged(dir) && (names.has(entry.name) || entry.name.startsWith("chat-recall"))) {
|
|
22244
22300
|
rmSync7(dir, { recursive: true, force: true });
|
|
22245
22301
|
removed.push(entry.name);
|
|
@@ -22256,8 +22312,8 @@ function skillsNeedRefresh() {
|
|
|
22256
22312
|
for (const t of skillTargets()) {
|
|
22257
22313
|
if (!t.available) continue;
|
|
22258
22314
|
for (const name of names) {
|
|
22259
|
-
const marker =
|
|
22260
|
-
if (!
|
|
22315
|
+
const marker = join48(t.dir, name, MARKER);
|
|
22316
|
+
if (!existsSync45(marker)) return true;
|
|
22261
22317
|
try {
|
|
22262
22318
|
if (readFileSync39(marker, "utf-8").trim() !== cur) return true;
|
|
22263
22319
|
} catch {
|
|
@@ -22274,8 +22330,8 @@ function skillStatus() {
|
|
|
22274
22330
|
let installed = 0;
|
|
22275
22331
|
let ver = null;
|
|
22276
22332
|
for (const name of names) {
|
|
22277
|
-
const marker =
|
|
22278
|
-
if (
|
|
22333
|
+
const marker = join48(t.dir, name, MARKER);
|
|
22334
|
+
if (existsSync45(marker)) {
|
|
22279
22335
|
installed++;
|
|
22280
22336
|
try {
|
|
22281
22337
|
ver = readFileSync39(marker, "utf-8").trim();
|
|
@@ -22303,8 +22359,8 @@ var init_install_skills = __esm({
|
|
|
22303
22359
|
|
|
22304
22360
|
// ../engine/dist/core/artifact-codec.js
|
|
22305
22361
|
import { readFileSync as readFileSync40 } from "fs";
|
|
22306
|
-
import { basename as basename22, join as
|
|
22307
|
-
import { homedir as
|
|
22362
|
+
import { basename as basename22, join as join49 } from "path";
|
|
22363
|
+
import { homedir as homedir18 } from "os";
|
|
22308
22364
|
function parseCommandText(content, format, fallbackName) {
|
|
22309
22365
|
if (format === "toml") {
|
|
22310
22366
|
const t = parseScalarToml(content);
|
|
@@ -22338,29 +22394,29 @@ function readInstructions(filePath, name) {
|
|
|
22338
22394
|
function commandPath(tool, name) {
|
|
22339
22395
|
switch (tool) {
|
|
22340
22396
|
case "claude":
|
|
22341
|
-
return
|
|
22397
|
+
return join49(claudeBackend.commandsDir(), `${name}.md`);
|
|
22342
22398
|
case "agy":
|
|
22343
|
-
return
|
|
22399
|
+
return join49(join49(agyBackend.homeDir(), "commands"), `${name}.toml`);
|
|
22344
22400
|
case "opencode":
|
|
22345
|
-
return
|
|
22401
|
+
return join49(opencodeBackend.commandsDir(), `${name}.md`);
|
|
22346
22402
|
case "codex":
|
|
22347
|
-
return
|
|
22403
|
+
return join49(codexBackend.promptsDir(), `${name}.md`);
|
|
22348
22404
|
case "cursor":
|
|
22349
|
-
return
|
|
22405
|
+
return join49(cursorHomeDir(), "commands", `${name}.md`);
|
|
22350
22406
|
}
|
|
22351
22407
|
}
|
|
22352
22408
|
function agentPath(tool, name) {
|
|
22353
22409
|
switch (tool) {
|
|
22354
22410
|
case "claude":
|
|
22355
|
-
return
|
|
22411
|
+
return join49(claudeBackend.agentsDir(), `${name}.md`);
|
|
22356
22412
|
case "agy":
|
|
22357
|
-
return
|
|
22413
|
+
return join49(join49(agyBackend.homeDir(), "agents"), `${name}.md`);
|
|
22358
22414
|
case "opencode":
|
|
22359
|
-
return
|
|
22415
|
+
return join49(opencodeBackend.agentsDir(), `${name}.md`);
|
|
22360
22416
|
case "codex":
|
|
22361
|
-
return
|
|
22417
|
+
return join49(codexBackend.agentsDir(), `${name}.toml`);
|
|
22362
22418
|
case "cursor":
|
|
22363
|
-
return
|
|
22419
|
+
return join49(cursorHomeDir(), "agents", `${name}.md`);
|
|
22364
22420
|
}
|
|
22365
22421
|
}
|
|
22366
22422
|
function instructionsFilename(tool) {
|
|
@@ -22385,21 +22441,21 @@ function instructionsFilename(tool) {
|
|
|
22385
22441
|
function instructionsPath(tool, projectPath) {
|
|
22386
22442
|
const file = instructionsFilename(tool);
|
|
22387
22443
|
if (projectPath)
|
|
22388
|
-
return
|
|
22444
|
+
return join49(projectPath, file);
|
|
22389
22445
|
switch (tool) {
|
|
22390
22446
|
case "claude":
|
|
22391
|
-
return
|
|
22447
|
+
return join49(claudeBackend.homeDir(), file);
|
|
22392
22448
|
case "agy":
|
|
22393
|
-
return
|
|
22449
|
+
return join49(agyBackend.homeDir(), file);
|
|
22394
22450
|
case "codex":
|
|
22395
|
-
return
|
|
22451
|
+
return join49(codexBackend.homeDir(), file);
|
|
22396
22452
|
case "opencode":
|
|
22397
|
-
return
|
|
22453
|
+
return join49(homedir18(), ".config", "opencode", file);
|
|
22398
22454
|
// Cursor has no single user-scope instructions FILE — user-level guidance
|
|
22399
22455
|
// is User Rules plus ~/.cursor/rules/*.mdc. AGENTS.md at the home root is
|
|
22400
22456
|
// the closest equivalent and is what a mirror can actually write.
|
|
22401
22457
|
case "cursor":
|
|
22402
|
-
return
|
|
22458
|
+
return join49(cursorHomeDir(), file);
|
|
22403
22459
|
}
|
|
22404
22460
|
}
|
|
22405
22461
|
function emit(type, art, toTool, projectPath) {
|
|
@@ -22463,9 +22519,9 @@ __export(toolkit_sync_exports, {
|
|
|
22463
22519
|
supportedTargetsFor: () => supportedTargetsFor,
|
|
22464
22520
|
writeMcpEntry: () => writeMcpEntry
|
|
22465
22521
|
});
|
|
22466
|
-
import { existsSync as
|
|
22467
|
-
import { dirname as
|
|
22468
|
-
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";
|
|
22469
22525
|
function supportedTargetsFor(type) {
|
|
22470
22526
|
return SUPPORTED_TARGETS[type];
|
|
22471
22527
|
}
|
|
@@ -22506,26 +22562,26 @@ function skillsDirFor(tool) {
|
|
|
22506
22562
|
case "claude":
|
|
22507
22563
|
return claudeBackend.skillsDir();
|
|
22508
22564
|
case "agy":
|
|
22509
|
-
return
|
|
22565
|
+
return join50(agyBackend.homeDir(), "skills");
|
|
22510
22566
|
case "opencode":
|
|
22511
22567
|
return opencodeBackend.skillsDir();
|
|
22512
22568
|
case "codex":
|
|
22513
22569
|
return codexBackend.skillsDir();
|
|
22514
22570
|
// user skills, NOT .system
|
|
22515
22571
|
case "cursor":
|
|
22516
|
-
return
|
|
22572
|
+
return join50(cursorHomeDir(), "skills");
|
|
22517
22573
|
}
|
|
22518
22574
|
}
|
|
22519
22575
|
function copySkill(row, toTool) {
|
|
22520
|
-
const skillDir = row.extra.skillDir ||
|
|
22521
|
-
if (!
|
|
22576
|
+
const skillDir = row.extra.skillDir || dirname22(row.filePath);
|
|
22577
|
+
if (!existsSync46(skillDir))
|
|
22522
22578
|
return { ok: false, status: 404, error: `Source dir missing: ${skillDir}` };
|
|
22523
22579
|
const name = row.extra.skillName || basename23(skillDir);
|
|
22524
|
-
const targetDir =
|
|
22525
|
-
if (
|
|
22580
|
+
const targetDir = join50(skillsDirFor(toTool), name);
|
|
22581
|
+
if (existsSync46(targetDir))
|
|
22526
22582
|
return { ok: false, status: 409, error: `Already exists: ${targetDir}` };
|
|
22527
22583
|
try {
|
|
22528
|
-
|
|
22584
|
+
mkdirSync15(dirname22(targetDir), { recursive: true });
|
|
22529
22585
|
cpSync2(skillDir, targetDir, { recursive: true });
|
|
22530
22586
|
return { ok: true, targetPath: targetDir };
|
|
22531
22587
|
} catch (e) {
|
|
@@ -22533,28 +22589,28 @@ function copySkill(row, toTool) {
|
|
|
22533
22589
|
}
|
|
22534
22590
|
}
|
|
22535
22591
|
function readMcpEntry(tool, name) {
|
|
22536
|
-
const home =
|
|
22592
|
+
const home = homedir19();
|
|
22537
22593
|
const tries = [];
|
|
22538
22594
|
if (tool === "claude") {
|
|
22539
|
-
tries.push({ path:
|
|
22540
|
-
tries.push({ path:
|
|
22595
|
+
tries.push({ path: join50(home, ".mcp.json"), key: "mcpServers" });
|
|
22596
|
+
tries.push({ path: join50(home, ".claude.json"), key: "mcpServers" });
|
|
22541
22597
|
} else if (tool === "gemini") {
|
|
22542
|
-
tries.push({ path:
|
|
22598
|
+
tries.push({ path: join50(home, ".gemini", "settings.json"), key: "mcpServers" });
|
|
22543
22599
|
} else if (tool === "agy") {
|
|
22544
|
-
tries.push({ path:
|
|
22600
|
+
tries.push({ path: join50(home, ".gemini", "config", "mcp_config.json"), key: "mcpServers" });
|
|
22545
22601
|
} else if (tool === "cursor") {
|
|
22546
|
-
tries.push({ path:
|
|
22602
|
+
tries.push({ path: join50(cursorHomeDir(), "mcp.json"), key: "mcpServers" });
|
|
22547
22603
|
} else if (tool === "opencode") {
|
|
22548
|
-
tries.push({ path:
|
|
22549
|
-
tries.push({ path:
|
|
22550
|
-
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" });
|
|
22551
22607
|
} else if (tool === "codex") {
|
|
22552
|
-
const entry = readCodexMcpEntry(
|
|
22608
|
+
const entry = readCodexMcpEntry(join50(home, ".codex", "config.toml"), name);
|
|
22553
22609
|
if (entry)
|
|
22554
22610
|
return entry;
|
|
22555
22611
|
}
|
|
22556
22612
|
for (const t of tries) {
|
|
22557
|
-
if (!
|
|
22613
|
+
if (!existsSync46(t.path))
|
|
22558
22614
|
continue;
|
|
22559
22615
|
try {
|
|
22560
22616
|
const cfg = JSON.parse(readFileSync41(t.path, "utf-8"));
|
|
@@ -22567,7 +22623,7 @@ function readMcpEntry(tool, name) {
|
|
|
22567
22623
|
return null;
|
|
22568
22624
|
}
|
|
22569
22625
|
function readCodexMcpEntry(path, target) {
|
|
22570
|
-
if (!
|
|
22626
|
+
if (!existsSync46(path))
|
|
22571
22627
|
return null;
|
|
22572
22628
|
let content;
|
|
22573
22629
|
try {
|
|
@@ -22621,7 +22677,7 @@ function readCodexMcpEntry(path, target) {
|
|
|
22621
22677
|
}
|
|
22622
22678
|
function writeCodexMcpEntry(path, name, entry) {
|
|
22623
22679
|
let content = "";
|
|
22624
|
-
if (
|
|
22680
|
+
if (existsSync46(path))
|
|
22625
22681
|
content = readFileSync41(path, "utf-8");
|
|
22626
22682
|
const header = `[mcp_servers.${name}]`;
|
|
22627
22683
|
if (content.includes(header))
|
|
@@ -22651,14 +22707,14 @@ function writeCodexMcpEntry(path, name, entry) {
|
|
|
22651
22707
|
for (const [k, v] of Object.entries(entry.env))
|
|
22652
22708
|
lines.push(`${k} = ${escape(String(v))}`);
|
|
22653
22709
|
}
|
|
22654
|
-
|
|
22655
|
-
|
|
22710
|
+
mkdirSync15(dirname22(path), { recursive: true });
|
|
22711
|
+
writeFileSync15(path, content + lines.join("\n") + "\n");
|
|
22656
22712
|
return true;
|
|
22657
22713
|
}
|
|
22658
22714
|
function writeMcpEntry(toTool, name, entry) {
|
|
22659
|
-
const home =
|
|
22715
|
+
const home = homedir19();
|
|
22660
22716
|
if (toTool === "codex") {
|
|
22661
|
-
const path2 =
|
|
22717
|
+
const path2 = join50(home, ".codex", "config.toml");
|
|
22662
22718
|
if (!writeCodexMcpEntry(path2, name, entry))
|
|
22663
22719
|
return { ok: false, status: 409, error: `MCP "${name}" already exists in ${path2}.` };
|
|
22664
22720
|
return { ok: true, targetPath: path2 };
|
|
@@ -22666,27 +22722,27 @@ function writeMcpEntry(toTool, name, entry) {
|
|
|
22666
22722
|
let path;
|
|
22667
22723
|
let key2;
|
|
22668
22724
|
if (toTool === "claude") {
|
|
22669
|
-
path =
|
|
22725
|
+
path = join50(home, ".mcp.json");
|
|
22670
22726
|
key2 = "mcpServers";
|
|
22671
22727
|
} else if (toTool === "agy") {
|
|
22672
|
-
path =
|
|
22728
|
+
path = join50(home, ".gemini", "config", "mcp_config.json");
|
|
22673
22729
|
key2 = "mcpServers";
|
|
22674
22730
|
} else if (toTool === "cursor") {
|
|
22675
|
-
path =
|
|
22731
|
+
path = join50(cursorHomeDir(), "mcp.json");
|
|
22676
22732
|
key2 = "mcpServers";
|
|
22677
22733
|
} else {
|
|
22678
|
-
path =
|
|
22734
|
+
path = join50(home, ".config", "opencode", "opencode.json");
|
|
22679
22735
|
key2 = "mcp";
|
|
22680
22736
|
}
|
|
22681
22737
|
let cfg = {};
|
|
22682
|
-
if (
|
|
22738
|
+
if (existsSync46(path)) {
|
|
22683
22739
|
try {
|
|
22684
22740
|
cfg = JSON.parse(readFileSync41(path, "utf-8"));
|
|
22685
22741
|
} catch {
|
|
22686
22742
|
return { ok: false, status: 500, error: `Target config is malformed: ${path}` };
|
|
22687
22743
|
}
|
|
22688
22744
|
} else {
|
|
22689
|
-
|
|
22745
|
+
mkdirSync15(dirname22(path), { recursive: true });
|
|
22690
22746
|
}
|
|
22691
22747
|
cfg[key2] = cfg[key2] || {};
|
|
22692
22748
|
if (cfg[key2][name])
|
|
@@ -22713,7 +22769,7 @@ function writeMcpEntry(toTool, name, entry) {
|
|
|
22713
22769
|
}
|
|
22714
22770
|
cfg[key2][name] = normalized;
|
|
22715
22771
|
try {
|
|
22716
|
-
|
|
22772
|
+
writeFileSync15(path, JSON.stringify(cfg, null, 2));
|
|
22717
22773
|
return { ok: true, targetPath: path };
|
|
22718
22774
|
} catch (e) {
|
|
22719
22775
|
return { ok: false, status: 500, error: e instanceof Error ? e.message : "write failed" };
|
|
@@ -22743,11 +22799,11 @@ function copyCommandOrAgent(type, row, toTool) {
|
|
|
22743
22799
|
return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
|
|
22744
22800
|
}
|
|
22745
22801
|
const out = emit(type, art, toTool);
|
|
22746
|
-
if (
|
|
22802
|
+
if (existsSync46(out.path))
|
|
22747
22803
|
return { ok: false, status: 409, error: `Already exists: ${out.path}` };
|
|
22748
22804
|
try {
|
|
22749
|
-
|
|
22750
|
-
|
|
22805
|
+
mkdirSync15(dirname22(out.path), { recursive: true });
|
|
22806
|
+
writeFileSync15(out.path, out.content);
|
|
22751
22807
|
return { ok: true, targetPath: out.path };
|
|
22752
22808
|
} catch (e) {
|
|
22753
22809
|
return { ok: false, status: 500, error: e instanceof Error ? e.message : "write failed" };
|
|
@@ -22763,11 +22819,11 @@ function copyInstructions(row, toTool) {
|
|
|
22763
22819
|
return { ok: false, status: 404, error: `Source unreadable: ${e instanceof Error ? e.message : "failed"}` };
|
|
22764
22820
|
}
|
|
22765
22821
|
const out = emit("instructions", art, toTool, row.projectPath || void 0);
|
|
22766
|
-
if (
|
|
22822
|
+
if (existsSync46(out.path))
|
|
22767
22823
|
return { ok: false, status: 409, error: `Already exists: ${out.path}` };
|
|
22768
22824
|
try {
|
|
22769
|
-
|
|
22770
|
-
|
|
22825
|
+
mkdirSync15(dirname22(out.path), { recursive: true });
|
|
22826
|
+
writeFileSync15(out.path, out.content);
|
|
22771
22827
|
return { ok: true, targetPath: out.path };
|
|
22772
22828
|
} catch (e) {
|
|
22773
22829
|
return { ok: false, status: 500, error: e instanceof Error ? e.message : "write failed" };
|
|
@@ -22913,8 +22969,8 @@ __export(toolkit_pull_exports, {
|
|
|
22913
22969
|
planPull: () => planPull,
|
|
22914
22970
|
portableCommand: () => portableCommand
|
|
22915
22971
|
});
|
|
22916
|
-
import { existsSync as
|
|
22917
|
-
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";
|
|
22918
22974
|
function isExecutableFile(p) {
|
|
22919
22975
|
try {
|
|
22920
22976
|
return statSync41(p).isFile();
|
|
@@ -23064,9 +23120,9 @@ function executePull(rows, opts = {}) {
|
|
|
23064
23120
|
const outcomes = [];
|
|
23065
23121
|
for (const { name, body, truncated, redacted, tools } of skills) {
|
|
23066
23122
|
for (const tool of tools) {
|
|
23067
|
-
const dir =
|
|
23068
|
-
const file =
|
|
23069
|
-
if (
|
|
23123
|
+
const dir = join51(skillsDirFor(tool), name);
|
|
23124
|
+
const file = join51(dir, "SKILL.md");
|
|
23125
|
+
if (existsSync47(file)) {
|
|
23070
23126
|
outcomes.push({ type: "skill", name, tool, status: "present" });
|
|
23071
23127
|
continue;
|
|
23072
23128
|
}
|
|
@@ -23079,8 +23135,8 @@ function executePull(rows, opts = {}) {
|
|
|
23079
23135
|
continue;
|
|
23080
23136
|
}
|
|
23081
23137
|
try {
|
|
23082
|
-
|
|
23083
|
-
|
|
23138
|
+
mkdirSync16(dir, { recursive: true });
|
|
23139
|
+
writeFileSync16(file, body);
|
|
23084
23140
|
outcomes.push({ type: "skill", name, tool, status: "written", path: file, redactedContent: redacted });
|
|
23085
23141
|
} catch (e) {
|
|
23086
23142
|
outcomes.push({ type: "skill", name, tool, status: "failed", reason: e instanceof Error ? e.message : "write failed" });
|
|
@@ -23111,7 +23167,7 @@ function executePull(rows, opts = {}) {
|
|
|
23111
23167
|
outcomes.push({ type, name, tool, status: "failed", reason: e instanceof Error ? e.message : "emit failed" });
|
|
23112
23168
|
continue;
|
|
23113
23169
|
}
|
|
23114
|
-
if (
|
|
23170
|
+
if (existsSync47(out.path)) {
|
|
23115
23171
|
outcomes.push({ type, name, tool, status: "present" });
|
|
23116
23172
|
continue;
|
|
23117
23173
|
}
|
|
@@ -23120,8 +23176,8 @@ function executePull(rows, opts = {}) {
|
|
|
23120
23176
|
continue;
|
|
23121
23177
|
}
|
|
23122
23178
|
try {
|
|
23123
|
-
|
|
23124
|
-
|
|
23179
|
+
mkdirSync16(dirname23(out.path), { recursive: true });
|
|
23180
|
+
writeFileSync16(out.path, out.content);
|
|
23125
23181
|
outcomes.push({ type, name, tool, status: "written", path: out.path, redactedContent: redacted });
|
|
23126
23182
|
} catch (e) {
|
|
23127
23183
|
outcomes.push({ type, name, tool, status: "failed", reason: e instanceof Error ? e.message : "write failed" });
|
|
@@ -23257,6 +23313,103 @@ var init_init_progress = __esm({
|
|
|
23257
23313
|
}
|
|
23258
23314
|
});
|
|
23259
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
|
+
|
|
23260
23413
|
// src/repair.ts
|
|
23261
23414
|
var repair_exports = {};
|
|
23262
23415
|
__export(repair_exports, {
|
|
@@ -23612,6 +23765,222 @@ var init_escalate = __esm({
|
|
|
23612
23765
|
}
|
|
23613
23766
|
});
|
|
23614
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
|
+
|
|
23615
23984
|
// src/verify-deep.ts
|
|
23616
23985
|
var verify_deep_exports = {};
|
|
23617
23986
|
__export(verify_deep_exports, {
|
|
@@ -23680,11 +24049,11 @@ var verify_repair_exports = {};
|
|
|
23680
24049
|
__export(verify_repair_exports, {
|
|
23681
24050
|
forceFullResync: () => forceFullResync
|
|
23682
24051
|
});
|
|
23683
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
24052
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync49 } from "fs";
|
|
23684
24053
|
function forceFullResync(server, sessionIds) {
|
|
23685
24054
|
if (sessionIds.length === 0) return 0;
|
|
23686
24055
|
const path = ledgerFilePath();
|
|
23687
|
-
if (
|
|
24056
|
+
if (existsSync49(path)) {
|
|
23688
24057
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace(/-/g, "");
|
|
23689
24058
|
try {
|
|
23690
24059
|
copyFileSync3(path, `${path}.bak-${stamp}`);
|
|
@@ -23874,8 +24243,8 @@ __export(collector_exports, {
|
|
|
23874
24243
|
});
|
|
23875
24244
|
import { spawn as spawn3 } from "node:child_process";
|
|
23876
24245
|
import { execSync as execSync4 } from "node:child_process";
|
|
23877
|
-
import { readFileSync as
|
|
23878
|
-
import { join as
|
|
24246
|
+
import { readFileSync as readFileSync43 } from "node:fs";
|
|
24247
|
+
import { join as join54, basename as basename26 } from "node:path";
|
|
23879
24248
|
async function resolveCodeindexBin(autoInstall = true) {
|
|
23880
24249
|
let st = checkCodeindexStatus();
|
|
23881
24250
|
if (!st.installed && autoInstall)
|
|
@@ -24054,7 +24423,7 @@ async function collectCode(opts) {
|
|
|
24054
24423
|
}
|
|
24055
24424
|
const complexity = (r) => {
|
|
24056
24425
|
try {
|
|
24057
|
-
const txt =
|
|
24426
|
+
const txt = readFileSync43(join54(ws, r), "utf8");
|
|
24058
24427
|
if (txt.length > 2e6)
|
|
24059
24428
|
return 1;
|
|
24060
24429
|
const m = txt.match(CF_RE);
|
|
@@ -24838,9 +25207,9 @@ __export(project_tasks_exports, {
|
|
|
24838
25207
|
pushProjectTaskStatuses: () => pushProjectTaskStatuses,
|
|
24839
25208
|
renderTeamTasksMd: () => renderTeamTasksMd
|
|
24840
25209
|
});
|
|
24841
|
-
import { existsSync as
|
|
24842
|
-
import { join as
|
|
24843
|
-
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";
|
|
24844
25213
|
function isTeamTaskStatus(v) {
|
|
24845
25214
|
return !!v && TEAM_STATUSES.has(v);
|
|
24846
25215
|
}
|
|
@@ -24890,22 +25259,22 @@ function parseTaskFile(content) {
|
|
|
24890
25259
|
return out;
|
|
24891
25260
|
}
|
|
24892
25261
|
function ledgerPath2() {
|
|
24893
|
-
return
|
|
25262
|
+
return join55(homedir21(), ".chat-recall", "project-tasks-ledger.json");
|
|
24894
25263
|
}
|
|
24895
25264
|
function readLedger() {
|
|
24896
25265
|
try {
|
|
24897
|
-
return JSON.parse(
|
|
25266
|
+
return JSON.parse(readFileSync44(ledgerPath2(), "utf8"));
|
|
24898
25267
|
} catch {
|
|
24899
25268
|
return {};
|
|
24900
25269
|
}
|
|
24901
25270
|
}
|
|
24902
25271
|
function writeLedger(l) {
|
|
24903
25272
|
try {
|
|
24904
|
-
|
|
25273
|
+
mkdirSync18(dirname25(ledgerPath2()), { recursive: true });
|
|
24905
25274
|
} catch {
|
|
24906
25275
|
}
|
|
24907
25276
|
try {
|
|
24908
|
-
|
|
25277
|
+
writeFileSync18(ledgerPath2(), JSON.stringify(l));
|
|
24909
25278
|
} catch {
|
|
24910
25279
|
}
|
|
24911
25280
|
}
|
|
@@ -24923,8 +25292,8 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
24923
25292
|
continue;
|
|
24924
25293
|
}
|
|
24925
25294
|
for (const project of projects) {
|
|
24926
|
-
const file =
|
|
24927
|
-
if (!
|
|
25295
|
+
const file = join55(project, fk.filename);
|
|
25296
|
+
if (!existsSync50(file)) continue;
|
|
24928
25297
|
let mtime;
|
|
24929
25298
|
try {
|
|
24930
25299
|
mtime = statSync42(file).mtimeMs;
|
|
@@ -24934,7 +25303,7 @@ async function pushProjectTaskStatuses(base3, authHeaders, opts = {}) {
|
|
|
24934
25303
|
if (ledger[file] === mtime) continue;
|
|
24935
25304
|
let changed;
|
|
24936
25305
|
try {
|
|
24937
|
-
changed = parseTaskFile(
|
|
25306
|
+
changed = parseTaskFile(readFileSync44(file, "utf8")).filter((t) => t.kind === fk.kind);
|
|
24938
25307
|
} catch {
|
|
24939
25308
|
continue;
|
|
24940
25309
|
}
|
|
@@ -25006,10 +25375,10 @@ __export(intent_drain_exports, {
|
|
|
25006
25375
|
drainSyncIntents: () => drainSyncIntents,
|
|
25007
25376
|
instructionTargets: () => instructionTargets
|
|
25008
25377
|
});
|
|
25009
|
-
import { existsSync as
|
|
25010
|
-
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";
|
|
25011
25380
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
25012
|
-
import { dirname as
|
|
25381
|
+
import { dirname as dirname26 } from "node:path";
|
|
25013
25382
|
function instructionTargets(rootPath, isGlobal) {
|
|
25014
25383
|
const installed = INSTRUCTION_TOOLS.filter((t) => {
|
|
25015
25384
|
try {
|
|
@@ -25021,17 +25390,17 @@ function instructionTargets(rootPath, isGlobal) {
|
|
|
25021
25390
|
const tools = installed.length ? installed : [INSTRUCTION_TOOLS[0]];
|
|
25022
25391
|
if (isGlobal) return [...new Set(tools.map((t) => instructionsPath(t.id)))];
|
|
25023
25392
|
if (!rootPath) return [];
|
|
25024
|
-
const present = INSTRUCTION_FILENAMES.map((f) =>
|
|
25393
|
+
const present = INSTRUCTION_FILENAMES.map((f) => join56(rootPath, f)).filter((p) => existsSync51(p));
|
|
25025
25394
|
if (present.length) return present;
|
|
25026
|
-
return [...new Set(tools.map((t) =>
|
|
25395
|
+
return [...new Set(tools.map((t) => join56(rootPath, instructionsFilename(t.id))))];
|
|
25027
25396
|
}
|
|
25028
25397
|
function applyCodeRecommendation(intent) {
|
|
25029
25398
|
try {
|
|
25030
25399
|
const meta = JSON.parse(intent.name || "{}");
|
|
25031
25400
|
if (intent.artifact_type === "write_tasks_file") {
|
|
25032
25401
|
if (!meta.rootPath || !meta.content) return { status: "error", result: JSON.stringify({ error: "missing rootPath or content" }) };
|
|
25033
|
-
const file =
|
|
25034
|
-
|
|
25402
|
+
const file = join56(meta.rootPath, meta.filename || "CODE_TASKS.md");
|
|
25403
|
+
writeFileSync19(file, meta.content);
|
|
25035
25404
|
return { status: "done", result: JSON.stringify({ wrote: file }) };
|
|
25036
25405
|
}
|
|
25037
25406
|
if (intent.artifact_type === "append_claude_md") {
|
|
@@ -25046,10 +25415,10 @@ function applyCodeRecommendation(intent) {
|
|
|
25046
25415
|
for (const file of targets) {
|
|
25047
25416
|
const dir = file.slice(0, file.lastIndexOf("/"));
|
|
25048
25417
|
try {
|
|
25049
|
-
|
|
25418
|
+
mkdirSync19(dir, { recursive: true });
|
|
25050
25419
|
} catch {
|
|
25051
25420
|
}
|
|
25052
|
-
const existing =
|
|
25421
|
+
const existing = existsSync51(file) ? readFileSync45(file, "utf8") : "";
|
|
25053
25422
|
if (existing.includes(text)) {
|
|
25054
25423
|
skipped.push(file);
|
|
25055
25424
|
continue;
|
|
@@ -25059,7 +25428,7 @@ function applyCodeRecommendation(intent) {
|
|
|
25059
25428
|
${text}
|
|
25060
25429
|
`;
|
|
25061
25430
|
if (existing) appendFileSync(file, block);
|
|
25062
|
-
else
|
|
25431
|
+
else writeFileSync19(file, `# ${meta.rootPath?.split("/").pop() || "Global"} instructions
|
|
25063
25432
|
${block}`);
|
|
25064
25433
|
appended.push(file);
|
|
25065
25434
|
}
|
|
@@ -25223,11 +25592,11 @@ var init_intent_drain = __esm({
|
|
|
25223
25592
|
];
|
|
25224
25593
|
ownVersion2 = "0.0.0";
|
|
25225
25594
|
try {
|
|
25226
|
-
const dir =
|
|
25227
|
-
ownVersion2 = JSON.parse(
|
|
25595
|
+
const dir = dirname26(fileURLToPath4(import.meta.url));
|
|
25596
|
+
ownVersion2 = JSON.parse(readFileSync45(join56(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
25228
25597
|
} catch {
|
|
25229
25598
|
try {
|
|
25230
|
-
if (true) ownVersion2 = "0.6.
|
|
25599
|
+
if (true) ownVersion2 = "0.6.6";
|
|
25231
25600
|
} catch {
|
|
25232
25601
|
}
|
|
25233
25602
|
}
|
|
@@ -25428,48 +25797,48 @@ var team_merge_exports = {};
|
|
|
25428
25797
|
__export(team_merge_exports, {
|
|
25429
25798
|
mergePullResult: () => mergePullResult
|
|
25430
25799
|
});
|
|
25431
|
-
import { existsSync as
|
|
25432
|
-
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";
|
|
25433
25802
|
import { createHash as createHash14 } from "crypto";
|
|
25434
25803
|
function installPathFor(art, tool) {
|
|
25435
25804
|
switch (art.type) {
|
|
25436
25805
|
case "skill":
|
|
25437
25806
|
switch (tool) {
|
|
25438
25807
|
case "claude":
|
|
25439
|
-
return
|
|
25808
|
+
return join57(claudeBackend.skillsDir(), art.name, "SKILL.md");
|
|
25440
25809
|
// Antigravity's OWN skills dir. It used to resolve to the shared
|
|
25441
25810
|
// ~/.gemini/skills while toolkit-sync installed to
|
|
25442
25811
|
// ~/.gemini/antigravity-cli/skills, so a team skill landed in one place
|
|
25443
25812
|
// and a synced skill in the other.
|
|
25444
25813
|
case "agy":
|
|
25445
|
-
return
|
|
25814
|
+
return join57(agyBackend.homeDir(), "skills", art.name, "SKILL.md");
|
|
25446
25815
|
case "opencode":
|
|
25447
|
-
return
|
|
25816
|
+
return join57(opencodeBackend.skillsDir(), art.name, "SKILL.md");
|
|
25448
25817
|
// User skills go to ~/.codex/skills (NOT .system, which is OpenAI's bundle).
|
|
25449
25818
|
case "codex":
|
|
25450
|
-
return
|
|
25819
|
+
return join57(codexBackend.skillsDir(), art.name, "SKILL.md");
|
|
25451
25820
|
case "cursor":
|
|
25452
|
-
return
|
|
25821
|
+
return join57(cursorHomeDir(), "skills", art.name, "SKILL.md");
|
|
25453
25822
|
}
|
|
25454
25823
|
break;
|
|
25455
25824
|
case "agent":
|
|
25456
25825
|
if (tool === "claude")
|
|
25457
|
-
return
|
|
25826
|
+
return join57(claudeBackend.agentsDir(), `${art.name}.md`);
|
|
25458
25827
|
return null;
|
|
25459
25828
|
case "command":
|
|
25460
25829
|
if (tool === "claude")
|
|
25461
|
-
return
|
|
25830
|
+
return join57(claudeBackend.commandsDir(), `${art.name}.md`);
|
|
25462
25831
|
return null;
|
|
25463
25832
|
case "hook":
|
|
25464
25833
|
if (tool === "claude")
|
|
25465
|
-
return
|
|
25834
|
+
return join57(claudeBackend.homeDir(), "chat-recall-hooks", `${art.name}.json`);
|
|
25466
25835
|
return null;
|
|
25467
25836
|
case "plan":
|
|
25468
25837
|
switch (tool) {
|
|
25469
25838
|
case "claude":
|
|
25470
|
-
return
|
|
25839
|
+
return join57(claudeBackend.plansDir(), `${art.name}.md`);
|
|
25471
25840
|
case "opencode":
|
|
25472
|
-
return
|
|
25841
|
+
return join57(opencodeBackend.plansDir(), `${art.name}.md`);
|
|
25473
25842
|
case "agy":
|
|
25474
25843
|
return null;
|
|
25475
25844
|
// Antigravity plans live per-session
|
|
@@ -25480,11 +25849,11 @@ function installPathFor(art, tool) {
|
|
|
25480
25849
|
}
|
|
25481
25850
|
break;
|
|
25482
25851
|
case "instructions":
|
|
25483
|
-
return
|
|
25852
|
+
return join57(getDataDir(), "team-instructions", tool, `${art.name}.md`);
|
|
25484
25853
|
case "mcp":
|
|
25485
|
-
return
|
|
25854
|
+
return join57(getDataDir(), "team-mcps", `${art.name}.json`);
|
|
25486
25855
|
case "plugin":
|
|
25487
|
-
return
|
|
25856
|
+
return join57(getDataDir(), "team-plugins", tool, art.name, "manifest.json");
|
|
25488
25857
|
}
|
|
25489
25858
|
return null;
|
|
25490
25859
|
}
|
|
@@ -25523,8 +25892,8 @@ async function mergePullResult(opts) {
|
|
|
25523
25892
|
pending.push({ artifactId: a.id, tool, artifactType: a.type, artifactName: a.name, sha256: sha, installedAt: Date.now() });
|
|
25524
25893
|
continue;
|
|
25525
25894
|
}
|
|
25526
|
-
|
|
25527
|
-
|
|
25895
|
+
mkdirSync20(dirname27(path), { recursive: true });
|
|
25896
|
+
writeFileSync20(path, body);
|
|
25528
25897
|
pending.push({ artifactId: a.id, tool, artifactType: a.type, artifactName: a.name, sha256: sha, installedAt: Date.now() });
|
|
25529
25898
|
report.written.push({ artifactId: a.id, path });
|
|
25530
25899
|
} catch (err) {
|
|
@@ -25545,7 +25914,7 @@ async function mergePullResult(opts) {
|
|
|
25545
25914
|
if (!path)
|
|
25546
25915
|
continue;
|
|
25547
25916
|
try {
|
|
25548
|
-
if (
|
|
25917
|
+
if (existsSync52(path))
|
|
25549
25918
|
rmSync8(path, { force: true });
|
|
25550
25919
|
await teamInstallsForget(id, row.tool);
|
|
25551
25920
|
report.removed.push({ artifactId: id, path });
|
|
@@ -25563,12 +25932,12 @@ async function mergePullResult(opts) {
|
|
|
25563
25932
|
}
|
|
25564
25933
|
function readFileIfExists(path) {
|
|
25565
25934
|
try {
|
|
25566
|
-
if (!
|
|
25935
|
+
if (!existsSync52(path))
|
|
25567
25936
|
return null;
|
|
25568
25937
|
const s = statSync43(path);
|
|
25569
25938
|
if (!s.isFile())
|
|
25570
25939
|
return null;
|
|
25571
|
-
return
|
|
25940
|
+
return readFileSync46(path);
|
|
25572
25941
|
} catch {
|
|
25573
25942
|
return null;
|
|
25574
25943
|
}
|
|
@@ -25649,8 +26018,8 @@ var init_vault_crypto = __esm({
|
|
|
25649
26018
|
});
|
|
25650
26019
|
|
|
25651
26020
|
// ../engine/dist/core/vault-source.js
|
|
25652
|
-
import { existsSync as
|
|
25653
|
-
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";
|
|
25654
26023
|
function* walkVaultSources() {
|
|
25655
26024
|
const t = loadSettings().team.vault;
|
|
25656
26025
|
const include2 = new Set(t.syncTools);
|
|
@@ -25675,7 +26044,7 @@ function safeStat(path) {
|
|
|
25675
26044
|
}
|
|
25676
26045
|
function* walkClaude() {
|
|
25677
26046
|
const root = claudeBackend.projectsDir();
|
|
25678
|
-
if (!
|
|
26047
|
+
if (!existsSync53(root))
|
|
25679
26048
|
return;
|
|
25680
26049
|
let projectDirs = [];
|
|
25681
26050
|
try {
|
|
@@ -25687,7 +26056,7 @@ function* walkClaude() {
|
|
|
25687
26056
|
if (proj.startsWith("."))
|
|
25688
26057
|
continue;
|
|
25689
26058
|
const projectPath = resolveProjectDirName(proj);
|
|
25690
|
-
const projDir =
|
|
26059
|
+
const projDir = join58(root, proj);
|
|
25691
26060
|
let entries = [];
|
|
25692
26061
|
try {
|
|
25693
26062
|
entries = readdirSync34(projDir);
|
|
@@ -25697,7 +26066,7 @@ function* walkClaude() {
|
|
|
25697
26066
|
for (const f of entries) {
|
|
25698
26067
|
if (extname(f) !== ".jsonl")
|
|
25699
26068
|
continue;
|
|
25700
|
-
const path =
|
|
26069
|
+
const path = join58(projDir, f);
|
|
25701
26070
|
const s = safeStat(path);
|
|
25702
26071
|
if (!s)
|
|
25703
26072
|
continue;
|
|
@@ -25727,8 +26096,8 @@ function* walkCursor() {
|
|
|
25727
26096
|
}
|
|
25728
26097
|
}
|
|
25729
26098
|
function* walkCodex() {
|
|
25730
|
-
const sessionsRoot =
|
|
25731
|
-
if (!
|
|
26099
|
+
const sessionsRoot = join58(codexBackend.homeDir(), "sessions");
|
|
26100
|
+
if (!existsSync53(sessionsRoot))
|
|
25732
26101
|
return;
|
|
25733
26102
|
const stack = [sessionsRoot];
|
|
25734
26103
|
while (stack.length) {
|
|
@@ -25740,7 +26109,7 @@ function* walkCodex() {
|
|
|
25740
26109
|
continue;
|
|
25741
26110
|
}
|
|
25742
26111
|
for (const e of entries) {
|
|
25743
|
-
const path =
|
|
26112
|
+
const path = join58(dir, e.name);
|
|
25744
26113
|
if (e.isDirectory()) {
|
|
25745
26114
|
stack.push(path);
|
|
25746
26115
|
continue;
|
|
@@ -25756,8 +26125,8 @@ function* walkCodex() {
|
|
|
25756
26125
|
}
|
|
25757
26126
|
}
|
|
25758
26127
|
function* walkAgy() {
|
|
25759
|
-
const brainDir =
|
|
25760
|
-
if (!
|
|
26128
|
+
const brainDir = join58(agyBackend.homeDir(), "brain");
|
|
26129
|
+
if (!existsSync53(brainDir))
|
|
25761
26130
|
return;
|
|
25762
26131
|
let sessionDirs = [];
|
|
25763
26132
|
try {
|
|
@@ -25766,18 +26135,18 @@ function* walkAgy() {
|
|
|
25766
26135
|
return;
|
|
25767
26136
|
}
|
|
25768
26137
|
for (const rawId of sessionDirs) {
|
|
25769
|
-
const sessionPath =
|
|
26138
|
+
const sessionPath = join58(brainDir, rawId);
|
|
25770
26139
|
try {
|
|
25771
26140
|
if (!statSync44(sessionPath).isDirectory())
|
|
25772
26141
|
continue;
|
|
25773
26142
|
} catch {
|
|
25774
26143
|
continue;
|
|
25775
26144
|
}
|
|
25776
|
-
let filePath =
|
|
25777
|
-
if (!
|
|
25778
|
-
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");
|
|
25779
26148
|
}
|
|
25780
|
-
if (!
|
|
26149
|
+
if (!existsSync53(filePath))
|
|
25781
26150
|
continue;
|
|
25782
26151
|
const s = safeStat(filePath);
|
|
25783
26152
|
if (!s)
|
|
@@ -25813,8 +26182,8 @@ __export(vault_client_exports, {
|
|
|
25813
26182
|
vaultStatus: () => vaultStatus,
|
|
25814
26183
|
vaultSync: () => vaultSync
|
|
25815
26184
|
});
|
|
25816
|
-
import { mkdirSync as
|
|
25817
|
-
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";
|
|
25818
26187
|
function context2() {
|
|
25819
26188
|
const s = loadSettings().team;
|
|
25820
26189
|
if (!s.enabled)
|
|
@@ -25953,7 +26322,7 @@ async function vaultSync(passphrase) {
|
|
|
25953
26322
|
continue;
|
|
25954
26323
|
}
|
|
25955
26324
|
try {
|
|
25956
|
-
const plaintext =
|
|
26325
|
+
const plaintext = readFileSync47(src.path);
|
|
25957
26326
|
const sourceSha = sha256Hex(plaintext);
|
|
25958
26327
|
const priorSha = uploaded.get(uploadKey(src.sessionId, src.tool));
|
|
25959
26328
|
if (priorSha === sourceSha) {
|
|
@@ -26005,7 +26374,7 @@ async function vaultSync(passphrase) {
|
|
|
26005
26374
|
}
|
|
26006
26375
|
function importedPathFor(tool, sessionId) {
|
|
26007
26376
|
const base3 = sessionId.replace(/^gemini_/, "").replace(/^codex_/, "").replace(/^agy_/, "").replace(/^cursor_/, "").replace(/^opencode_/, "");
|
|
26008
|
-
return
|
|
26377
|
+
return join59(IMPORTED_ROOT(), tool, `${base3}.jsonl`);
|
|
26009
26378
|
}
|
|
26010
26379
|
async function vaultRestore(passphrase, opts = {}) {
|
|
26011
26380
|
const ctx = context2();
|
|
@@ -26026,7 +26395,7 @@ async function vaultRestore(passphrase, opts = {}) {
|
|
|
26026
26395
|
}
|
|
26027
26396
|
try {
|
|
26028
26397
|
const path = importedPathFor(blob.tool, blob.sessionId);
|
|
26029
|
-
if (
|
|
26398
|
+
if (existsSync54(path)) {
|
|
26030
26399
|
const localStat = await import("fs").then((m) => m.statSync(path));
|
|
26031
26400
|
if (Math.abs(localStat.mtimeMs - blob.mtimeSourceMs) < 1) {
|
|
26032
26401
|
report.skipped.push({ sessionId: blob.sessionId, tool: blob.tool, reason: "unchanged" });
|
|
@@ -26042,8 +26411,8 @@ async function vaultRestore(passphrase, opts = {}) {
|
|
|
26042
26411
|
throw new Error("cipher integrity check failed");
|
|
26043
26412
|
}
|
|
26044
26413
|
const plaintext = decryptBlob(key2, cipher);
|
|
26045
|
-
|
|
26046
|
-
|
|
26414
|
+
mkdirSync21(dirname28(path), { recursive: true });
|
|
26415
|
+
writeFileSync21(path, Buffer.from(plaintext));
|
|
26047
26416
|
const { utimesSync } = await import("fs");
|
|
26048
26417
|
const t = blob.mtimeSourceMs / 1e3;
|
|
26049
26418
|
utimesSync(path, t, t);
|
|
@@ -26094,7 +26463,7 @@ var init_vault_client = __esm({
|
|
|
26094
26463
|
uploadKey = (sessionId, tool) => `${sessionId}\0${tool}`;
|
|
26095
26464
|
VaultPassphraseMismatchError = class extends Error {
|
|
26096
26465
|
};
|
|
26097
|
-
IMPORTED_ROOT = () =>
|
|
26466
|
+
IMPORTED_ROOT = () => join59(getDataDir(), "imported");
|
|
26098
26467
|
}
|
|
26099
26468
|
});
|
|
26100
26469
|
|
|
@@ -26235,6 +26604,413 @@ var init_keyring = __esm({
|
|
|
26235
26604
|
}
|
|
26236
26605
|
});
|
|
26237
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
|
+
|
|
26238
27014
|
// src/service-installer.ts
|
|
26239
27015
|
var service_installer_exports = {};
|
|
26240
27016
|
__export(service_installer_exports, {
|
|
@@ -26249,15 +27025,15 @@ __export(service_installer_exports, {
|
|
|
26249
27025
|
uninstallService: () => uninstallService
|
|
26250
27026
|
});
|
|
26251
27027
|
import { execSync as execSync5 } from "child_process";
|
|
26252
|
-
import { existsSync as
|
|
26253
|
-
import { homedir as
|
|
26254
|
-
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";
|
|
26255
27031
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
26256
27032
|
function resolveDaemonPaths() {
|
|
26257
27033
|
const watchJs = fileURLToPath5(new URL("./watch.js", import.meta.url));
|
|
26258
27034
|
const node = process.execPath;
|
|
26259
|
-
const dataDir =
|
|
26260
|
-
const logFile =
|
|
27035
|
+
const dataDir = join60(homedir22(), ".chat-recall");
|
|
27036
|
+
const logFile = join60(dataDir, "watch.log");
|
|
26261
27037
|
return { watchJs, node, logFile, dataDir };
|
|
26262
27038
|
}
|
|
26263
27039
|
function tryCmd(cmd) {
|
|
@@ -26284,13 +27060,13 @@ function refreshServiceDefinition() {
|
|
|
26284
27060
|
if (process.platform !== "linux") return false;
|
|
26285
27061
|
try {
|
|
26286
27062
|
const { watchJs, node, logFile } = resolveDaemonPaths();
|
|
26287
|
-
const unitPath =
|
|
26288
|
-
if (!
|
|
26289
|
-
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");
|
|
26290
27066
|
if (!current.includes(watchJs)) return false;
|
|
26291
27067
|
const wanted = renderSystemdUnit(watchJs, node, logFile);
|
|
26292
|
-
if (
|
|
26293
|
-
|
|
27068
|
+
if (readFileSync48(unitPath, "utf8") === wanted) return false;
|
|
27069
|
+
writeFileSync22(unitPath, wanted);
|
|
26294
27070
|
execSync5("systemctl --user daemon-reload", { stdio: "ignore" });
|
|
26295
27071
|
return true;
|
|
26296
27072
|
} catch {
|
|
@@ -26299,7 +27075,7 @@ function refreshServiceDefinition() {
|
|
|
26299
27075
|
}
|
|
26300
27076
|
function installService() {
|
|
26301
27077
|
const { watchJs, node, logFile, dataDir } = resolveDaemonPaths();
|
|
26302
|
-
|
|
27078
|
+
mkdirSync22(dataDir, { recursive: true });
|
|
26303
27079
|
if (process.platform === "linux") {
|
|
26304
27080
|
return installSystemd(watchJs, node, logFile);
|
|
26305
27081
|
}
|
|
@@ -26315,11 +27091,11 @@ function installService() {
|
|
|
26315
27091
|
);
|
|
26316
27092
|
}
|
|
26317
27093
|
function installSystemd(watchJs, node, logFile) {
|
|
26318
|
-
const unitDir =
|
|
26319
|
-
const unitPath =
|
|
27094
|
+
const unitDir = join60(homedir22(), ".config", "systemd", "user");
|
|
27095
|
+
const unitPath = join60(unitDir, "chat-recall-watch.service");
|
|
26320
27096
|
const unit = renderSystemdUnit(watchJs, node, logFile);
|
|
26321
|
-
|
|
26322
|
-
|
|
27097
|
+
mkdirSync22(unitDir, { recursive: true });
|
|
27098
|
+
writeFileSync22(unitPath, unit);
|
|
26323
27099
|
try {
|
|
26324
27100
|
execSync5("systemctl --user daemon-reload && systemctl --user enable chat-recall-watch.service && systemctl --user restart chat-recall-watch.service", { stdio: "inherit" });
|
|
26325
27101
|
try {
|
|
@@ -26390,11 +27166,11 @@ function renderSystemdUnit(watchJs, node, logFile) {
|
|
|
26390
27166
|
}
|
|
26391
27167
|
function installLaunchd(watchJs, node, logFile) {
|
|
26392
27168
|
const label = "com.chat-recall.watch";
|
|
26393
|
-
const plistDir =
|
|
26394
|
-
const plistPath =
|
|
27169
|
+
const plistDir = join60(homedir22(), "Library", "LaunchAgents");
|
|
27170
|
+
const plistPath = join60(plistDir, `${label}.plist`);
|
|
26395
27171
|
const plist = renderLaunchdPlist(watchJs, node, logFile, label);
|
|
26396
|
-
|
|
26397
|
-
|
|
27172
|
+
mkdirSync22(plistDir, { recursive: true });
|
|
27173
|
+
writeFileSync22(plistPath, plist);
|
|
26398
27174
|
try {
|
|
26399
27175
|
try {
|
|
26400
27176
|
execSync5(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
|
|
@@ -26447,8 +27223,8 @@ function uninstallService() {
|
|
|
26447
27223
|
removed = true;
|
|
26448
27224
|
} catch {
|
|
26449
27225
|
}
|
|
26450
|
-
const unitPath =
|
|
26451
|
-
if (
|
|
27226
|
+
const unitPath = join60(homedir22(), ".config", "systemd", "user", "chat-recall-watch.service");
|
|
27227
|
+
if (existsSync55(unitPath)) {
|
|
26452
27228
|
rmSync9(unitPath, { force: true });
|
|
26453
27229
|
removed = true;
|
|
26454
27230
|
}
|
|
@@ -26459,13 +27235,13 @@ function uninstallService() {
|
|
|
26459
27235
|
return removed;
|
|
26460
27236
|
}
|
|
26461
27237
|
if (process.platform === "darwin") {
|
|
26462
|
-
const plistPath =
|
|
27238
|
+
const plistPath = join60(homedir22(), "Library", "LaunchAgents", "com.chat-recall.watch.plist");
|
|
26463
27239
|
try {
|
|
26464
27240
|
execSync5(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
|
|
26465
27241
|
} catch {
|
|
26466
27242
|
}
|
|
26467
27243
|
let removed = false;
|
|
26468
|
-
if (
|
|
27244
|
+
if (existsSync55(plistPath)) {
|
|
26469
27245
|
rmSync9(plistPath, { force: true });
|
|
26470
27246
|
removed = true;
|
|
26471
27247
|
}
|
|
@@ -26878,9 +27654,9 @@ init_project_resolver();
|
|
|
26878
27654
|
import { config } from "dotenv";
|
|
26879
27655
|
import { Command } from "commander";
|
|
26880
27656
|
import chalk2 from "chalk";
|
|
26881
|
-
import { existsSync as
|
|
26882
|
-
import { homedir as
|
|
26883
|
-
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";
|
|
26884
27660
|
|
|
26885
27661
|
// ../engine/dist/core/score-tier.js
|
|
26886
27662
|
var STRONG_RATIO = 0.85;
|
|
@@ -26985,6 +27761,47 @@ function renderEnvNeeds(needs, installedCount) {
|
|
|
26985
27761
|
return lines;
|
|
26986
27762
|
}
|
|
26987
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
|
+
|
|
26988
27805
|
// src/cli.ts
|
|
26989
27806
|
function matchLabel(tier) {
|
|
26990
27807
|
switch (tier) {
|
|
@@ -27003,8 +27820,8 @@ function claudeHookConfigFiles() {
|
|
|
27003
27820
|
const out = [];
|
|
27004
27821
|
const seen = /* @__PURE__ */ new Set();
|
|
27005
27822
|
for (const home of claudeHomeDirs()) {
|
|
27006
|
-
if (!
|
|
27007
|
-
const file =
|
|
27823
|
+
if (!existsSync56(join61(home, "projects"))) continue;
|
|
27824
|
+
const file = join61(home, "hooks.json");
|
|
27008
27825
|
let key2;
|
|
27009
27826
|
try {
|
|
27010
27827
|
key2 = realpathSync5(file);
|
|
@@ -27099,13 +27916,13 @@ async function serverDelete(path, body) {
|
|
|
27099
27916
|
}
|
|
27100
27917
|
var pkgEngines = (() => {
|
|
27101
27918
|
try {
|
|
27102
|
-
return JSON.parse(
|
|
27919
|
+
return JSON.parse(readFileSync49(new URL("../package.json", import.meta.url), "utf-8")).engines?.node ?? "";
|
|
27103
27920
|
} catch {
|
|
27104
27921
|
return "";
|
|
27105
27922
|
}
|
|
27106
27923
|
})();
|
|
27107
27924
|
var pkgVersion = JSON.parse(
|
|
27108
|
-
|
|
27925
|
+
readFileSync49(new URL("../package.json", import.meta.url), "utf-8")
|
|
27109
27926
|
).version;
|
|
27110
27927
|
var DEFAULT_SERVER = (process.env.CHAT_RECALL_DEFAULT_SERVER || "https://chatrecall.dev").replace(/\/+$/, "");
|
|
27111
27928
|
var DEFAULT_ALLOW = [
|
|
@@ -27150,6 +27967,7 @@ var DEFAULT_ALLOW = [
|
|
|
27150
27967
|
"recall_redundant_files",
|
|
27151
27968
|
"recall_user_prompts",
|
|
27152
27969
|
"recall_decision_record",
|
|
27970
|
+
"recall_decisions",
|
|
27153
27971
|
"recall_analytics_summary",
|
|
27154
27972
|
"recall_outcome_summary",
|
|
27155
27973
|
"recall_regenerate_summary",
|
|
@@ -27192,7 +28010,7 @@ process.env.CHAT_RECALL_CLI_VERSION = pkgVersion;
|
|
|
27192
28010
|
enforceNodeVersion();
|
|
27193
28011
|
var program = new Command();
|
|
27194
28012
|
program.name("chat-recall").description("Semantic search for Claude Code sessions - recall and resume past conversations").version(pkgVersion);
|
|
27195
|
-
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) => {
|
|
27196
28014
|
try {
|
|
27197
28015
|
console.log(chalk2.bold("chat-recall init") + chalk2.dim(` v${pkgVersion}`));
|
|
27198
28016
|
const { createInitReporter: createInitReporter2 } = await Promise.resolve().then(() => (init_init_report(), init_report_exports));
|
|
@@ -27218,6 +28036,18 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27218
28036
|
}
|
|
27219
28037
|
const foundTools = clis.filter((c) => c.available).map((c) => c.name.replace(/ (CLI|Code)$/, ""));
|
|
27220
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
|
+
}
|
|
27221
28051
|
const serverBad = (text) => rep.warn(`${chalk2.yellow("Not connected")} \u2014 ${text}`);
|
|
27222
28052
|
let target = firstTarget();
|
|
27223
28053
|
let deadTarget = false;
|
|
@@ -27265,10 +28095,10 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27265
28095
|
} else if (target) {
|
|
27266
28096
|
}
|
|
27267
28097
|
if (!options.skipMcp) {
|
|
27268
|
-
const projectRoot =
|
|
28098
|
+
const projectRoot = join61(import.meta.dirname, "..");
|
|
27269
28099
|
let mcpBinOnPath = false;
|
|
27270
28100
|
mcpBinOnPath = isOnPath("chat-recall-mcp");
|
|
27271
|
-
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")] };
|
|
27272
28102
|
const MCP_ENV = { NODE_OPTIONS: "--max-old-space-size=1024" };
|
|
27273
28103
|
const { registerMcpEverywhere: registerMcpEverywhere2 } = await Promise.resolve().then(() => (init_mcp_clients(), mcp_clients_exports));
|
|
27274
28104
|
const MCP_CLIENT_FOR_BIN = {
|
|
@@ -27308,7 +28138,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27308
28138
|
const detected = checkCodeindexStatus2();
|
|
27309
28139
|
if (detected.installed && !forceInstall) {
|
|
27310
28140
|
if (!options.skipMcp) {
|
|
27311
|
-
const mcpJsonPath =
|
|
28141
|
+
const mcpJsonPath = join61(homedir23(), ".mcp.json");
|
|
27312
28142
|
const reg = registerCodeindexMcp2(mcpJsonPath, detected.path);
|
|
27313
28143
|
}
|
|
27314
28144
|
} else if (forceInstall) {
|
|
@@ -27317,7 +28147,7 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27317
28147
|
if (result.installed) {
|
|
27318
28148
|
const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
|
|
27319
28149
|
if (!options.skipMcp) {
|
|
27320
|
-
const mcpJsonPath =
|
|
28150
|
+
const mcpJsonPath = join61(homedir23(), ".mcp.json");
|
|
27321
28151
|
const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
|
|
27322
28152
|
}
|
|
27323
28153
|
} else if (!result.prebuiltAvailable) {
|
|
@@ -27436,6 +28266,15 @@ program.command("init").description("Set up chat-recall: connect to your server,
|
|
|
27436
28266
|
} else {
|
|
27437
28267
|
}
|
|
27438
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
|
+
}
|
|
27439
28278
|
rep.done(chalk2.green.bold("\u2713 Done"));
|
|
27440
28279
|
} catch (err) {
|
|
27441
28280
|
console.error(chalk2.red("Error:"), err);
|
|
@@ -27572,6 +28411,7 @@ program.command("search <query>").description("Search your server for relevant s
|
|
|
27572
28411
|
}
|
|
27573
28412
|
console.log();
|
|
27574
28413
|
}
|
|
28414
|
+
askForStar(results.length, chalk2.dim);
|
|
27575
28415
|
} catch (err) {
|
|
27576
28416
|
console.error(chalk2.red("Error:"), err instanceof Error ? err.message : err);
|
|
27577
28417
|
process.exit(1);
|
|
@@ -27765,8 +28605,8 @@ memory.command("wake-up").description("Generate wake-up context (high-importance
|
|
|
27765
28605
|
try {
|
|
27766
28606
|
const identityFile = getIdentityFilePath();
|
|
27767
28607
|
let identity = "AI coding assistant";
|
|
27768
|
-
if (
|
|
27769
|
-
identity =
|
|
28608
|
+
if (existsSync56(identityFile)) {
|
|
28609
|
+
identity = readFileSync49(identityFile, "utf-8").trim();
|
|
27770
28610
|
}
|
|
27771
28611
|
const lines = [
|
|
27772
28612
|
chalk2.bold("# Wake-Up Context"),
|
|
@@ -27866,7 +28706,7 @@ program.command("feedback").argument("<message...>", "what you want to say \u201
|
|
|
27866
28706
|
}
|
|
27867
28707
|
});
|
|
27868
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) => {
|
|
27869
|
-
const { existsSync:
|
|
28709
|
+
const { existsSync: existsSync57, readFileSync: readFileSync50, statSync: statSync45 } = await import("fs");
|
|
27870
28710
|
const { execSync: execSync6 } = await import("child_process");
|
|
27871
28711
|
const rows = [];
|
|
27872
28712
|
const note = (ok, label, detail) => rows.push({ ok, label, detail });
|
|
@@ -27929,8 +28769,8 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
27929
28769
|
}
|
|
27930
28770
|
}
|
|
27931
28771
|
}
|
|
27932
|
-
const credFile =
|
|
27933
|
-
if (!
|
|
28772
|
+
const credFile = join61(getDataDir(), "credentials.json");
|
|
28773
|
+
if (!existsSync57(credFile)) {
|
|
27934
28774
|
note(false, "Credentials file", `missing \u2014 run \`chat-recall login <server-url>\``);
|
|
27935
28775
|
} else {
|
|
27936
28776
|
try {
|
|
@@ -27943,23 +28783,23 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
27943
28783
|
}
|
|
27944
28784
|
}
|
|
27945
28785
|
for (const hooksJson of claudeHookConfigFiles()) {
|
|
27946
|
-
const profile = basename27(
|
|
28786
|
+
const profile = basename27(dirname29(hooksJson));
|
|
27947
28787
|
const label = `Claude hooks (${profile})`;
|
|
27948
|
-
if (!
|
|
28788
|
+
if (!existsSync57(hooksJson)) {
|
|
27949
28789
|
note(false, label, `no ${hooksJson} \u2014 run \`chat-recall install-hooks\``);
|
|
27950
28790
|
continue;
|
|
27951
28791
|
}
|
|
27952
28792
|
try {
|
|
27953
28793
|
const readHooks = (file) => {
|
|
27954
|
-
if (!
|
|
28794
|
+
if (!existsSync57(file)) return {};
|
|
27955
28795
|
try {
|
|
27956
|
-
return JSON.parse(
|
|
28796
|
+
return JSON.parse(readFileSync50(file, "utf-8")).hooks || {};
|
|
27957
28797
|
} catch {
|
|
27958
28798
|
return {};
|
|
27959
28799
|
}
|
|
27960
28800
|
};
|
|
27961
28801
|
const cfg = readHooks(hooksJson);
|
|
27962
|
-
const settings2 = readHooks(
|
|
28802
|
+
const settings2 = readHooks(join61(dirname29(hooksJson), "settings.json"));
|
|
27963
28803
|
const all = ["SessionStart", "UserPromptSubmit", "Stop", "PreCompact", "SessionEnd"];
|
|
27964
28804
|
const hits = [];
|
|
27965
28805
|
for (const ev of all) {
|
|
@@ -27974,16 +28814,54 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
27974
28814
|
label,
|
|
27975
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)` : ""}`
|
|
27976
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
|
+
);
|
|
27977
28830
|
} catch {
|
|
27978
28831
|
note(false, label, `${hooksJson} unparseable`);
|
|
27979
28832
|
}
|
|
27980
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
|
+
}
|
|
27981
28859
|
try {
|
|
27982
28860
|
const { skillTargets: skillTargets2, bundledSkillNames: bundledSkillNames2 } = await Promise.resolve().then(() => (init_install_skills(), install_skills_exports));
|
|
27983
28861
|
const want = bundledSkillNames2().length;
|
|
27984
28862
|
for (const t of skillTargets2()) {
|
|
27985
28863
|
if (!t.available) continue;
|
|
27986
|
-
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;
|
|
27987
28865
|
note(
|
|
27988
28866
|
have === want,
|
|
27989
28867
|
`Skills \u2014 ${t.label}`,
|
|
@@ -28058,14 +28936,14 @@ program.command("doctor").description("Quick health check across login, server,
|
|
|
28058
28936
|
note(watchRunning, "Watch daemon", watchRunning ? "running" : "not running (run `chat-recall watch --install-service` to keep collection running)");
|
|
28059
28937
|
const dataDir = getDataDir();
|
|
28060
28938
|
const legacyPaths = [
|
|
28061
|
-
|
|
28062
|
-
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28066
|
-
|
|
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")
|
|
28067
28945
|
];
|
|
28068
|
-
const legacyPresent = legacyPaths.filter((p) =>
|
|
28946
|
+
const legacyPresent = legacyPaths.filter((p) => existsSync57(p));
|
|
28069
28947
|
if (opts.purgeLocal) {
|
|
28070
28948
|
if (!target) {
|
|
28071
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");
|
|
@@ -28136,7 +29014,7 @@ companions.command("install").description("Install codeindex (downloads from mun
|
|
|
28136
29014
|
if (result.installed) {
|
|
28137
29015
|
const sizeMb = result.size ? `${(result.size / 1024 / 1024).toFixed(1)} MB` : "?";
|
|
28138
29016
|
console.log(chalk2.green(`\u2713 codeindex installed (${sizeMb}) \u2192 ${result.path}`));
|
|
28139
|
-
const mcpJsonPath =
|
|
29017
|
+
const mcpJsonPath = join61(homedir23(), ".mcp.json");
|
|
28140
29018
|
const reg = registerCodeindexMcp2(mcpJsonPath, CODEINDEX_BIN_PATH2);
|
|
28141
29019
|
if (reg.added) console.log(chalk2.dim(` Registered as MCP server in ${mcpJsonPath}`));
|
|
28142
29020
|
else console.log(chalk2.dim(` Already registered as MCP server`));
|
|
@@ -28158,7 +29036,7 @@ companions.command("uninstall").description("Remove the codeindex binary chat-re
|
|
|
28158
29036
|
const r = uninstallCodeindex2();
|
|
28159
29037
|
if (r.removed) console.log(chalk2.green(`\u2713 Removed ${r.path}`));
|
|
28160
29038
|
else console.log(chalk2.dim(` Nothing to remove at ${r.path}`));
|
|
28161
|
-
const mcpJsonPath =
|
|
29039
|
+
const mcpJsonPath = join61(homedir23(), ".mcp.json");
|
|
28162
29040
|
const u = unregisterCodeindexMcp2(mcpJsonPath);
|
|
28163
29041
|
if (u.removed) console.log(chalk2.dim(` Unregistered MCP server from ${mcpJsonPath}`));
|
|
28164
29042
|
});
|
|
@@ -28709,10 +29587,10 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
|
|
|
28709
29587
|
try {
|
|
28710
29588
|
const { resolveProjectId: resolveProjectId2 } = await Promise.resolve().then(() => (init_project_resolver(), project_resolver_exports));
|
|
28711
29589
|
const { renderTeamTasksMd: renderTeamTasksMd2, parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
|
|
28712
|
-
const { writeFileSync:
|
|
28713
|
-
const { join:
|
|
28714
|
-
const file =
|
|
28715
|
-
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) {
|
|
28716
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.");
|
|
28717
29595
|
process.exit(1);
|
|
28718
29596
|
}
|
|
@@ -28720,7 +29598,7 @@ tasks.command("pull [dir]").description("Write TEAM_TASKS.md for your tasks in t
|
|
|
28720
29598
|
const pid = resolved && resolved.source !== "ignored" ? resolved.id : "";
|
|
28721
29599
|
const { tasks: tasks2 } = await serverGet("/api/tasks?assignee=@me");
|
|
28722
29600
|
const mine = tasks2.filter((t) => !pid || !t.projectId || t.projectId === pid);
|
|
28723
|
-
|
|
29601
|
+
writeFileSync24(file, renderTeamTasksMd2(mine));
|
|
28724
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"));
|
|
28725
29603
|
} catch (e) {
|
|
28726
29604
|
console.error(chalk2.red(e instanceof Error ? e.message : String(e)));
|
|
@@ -28731,14 +29609,14 @@ tasks.command("push [dir]").description("Push TEAM_TASKS.md status edits back to
|
|
|
28731
29609
|
const cwd = dir || process.cwd();
|
|
28732
29610
|
try {
|
|
28733
29611
|
const { parseTeamTasksFile: parseTeamTasksFile2 } = await Promise.resolve().then(() => (init_project_tasks(), project_tasks_exports));
|
|
28734
|
-
const { readFileSync:
|
|
28735
|
-
const { join:
|
|
28736
|
-
const file =
|
|
28737
|
-
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)) {
|
|
28738
29616
|
console.error(chalk2.red("no TEAM_TASKS.md here \u2014"), "run", chalk2.bold("chat-recall tasks pull"), "first");
|
|
28739
29617
|
process.exit(1);
|
|
28740
29618
|
}
|
|
28741
|
-
const edits = parseTeamTasksFile2(
|
|
29619
|
+
const edits = parseTeamTasksFile2(readFileSync50(file, "utf8"));
|
|
28742
29620
|
let n = 0;
|
|
28743
29621
|
for (const e of edits) {
|
|
28744
29622
|
await serverPatch(`/api/tasks/${encodeURIComponent(e.id)}`, { status: e.intent });
|
|
@@ -28844,8 +29722,8 @@ team.command("publish <type> <name> <file>").description("Publish a local file a
|
|
|
28844
29722
|
console.error(chalk2.red(`Invalid tool: ${opts.tool}. Must be one of: ${validTools.join(", ")}`));
|
|
28845
29723
|
process.exit(1);
|
|
28846
29724
|
}
|
|
28847
|
-
const { readFileSync:
|
|
28848
|
-
const body =
|
|
29725
|
+
const { readFileSync: readFileSync50 } = await import("fs");
|
|
29726
|
+
const body = readFileSync50(file);
|
|
28849
29727
|
const { teamPublish: teamPublish2 } = await Promise.resolve().then(() => (init_team_client(), team_client_exports));
|
|
28850
29728
|
const a = await teamPublish2({
|
|
28851
29729
|
type,
|
|
@@ -29031,18 +29909,201 @@ vault.command("forget").description("Remove the stored passphrase from this mach
|
|
|
29031
29909
|
const { keyringDelete: keyringDelete2 } = await Promise.resolve().then(() => (init_keyring(), keyring_exports));
|
|
29032
29910
|
console.log(keyringDelete2() ? chalk2.green("\u2713 Passphrase removed from the OS keyring.") : chalk2.dim("Nothing stored in the OS keyring."));
|
|
29033
29911
|
}));
|
|
29034
|
-
program.command("
|
|
29035
|
-
|
|
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");
|
|
29036
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));
|
|
29037
30093
|
const here = fileURLToPath6(new URL(".", import.meta.url));
|
|
29038
30094
|
const findHook = (name) => {
|
|
29039
30095
|
const candidates2 = [
|
|
29040
|
-
|
|
29041
|
-
|
|
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)
|
|
29042
30102
|
];
|
|
29043
|
-
return candidates2.find((p) =>
|
|
30103
|
+
return candidates2.find((p) => existsSync57(p));
|
|
29044
30104
|
};
|
|
29045
30105
|
const sourceSaveHook = findHook("chat_recall_save_hook.sh");
|
|
30106
|
+
const sourceGuardHook = findHook("chat_recall_guard_hook.sh");
|
|
29046
30107
|
const sourceResumeHook = findHook("chat_recall_resume_hook.sh");
|
|
29047
30108
|
const sourceEscalateHook = findHook("chat_recall_escalate_hook.sh");
|
|
29048
30109
|
const sourceWakeupHook = findHook("chat_recall_wakeup_hook.sh");
|
|
@@ -29051,15 +30112,16 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29051
30112
|
process.exit(1);
|
|
29052
30113
|
}
|
|
29053
30114
|
const hooksDir = getHooksDir();
|
|
29054
|
-
const installedSaveHook =
|
|
29055
|
-
const
|
|
29056
|
-
const
|
|
29057
|
-
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");
|
|
29058
30120
|
const wakeupWiredInSettings = (home) => {
|
|
29059
|
-
const settings2 =
|
|
29060
|
-
if (!
|
|
30121
|
+
const settings2 = join61(home, "settings.json");
|
|
30122
|
+
if (!existsSync57(settings2)) return false;
|
|
29061
30123
|
try {
|
|
29062
|
-
const cfg = JSON.parse(
|
|
30124
|
+
const cfg = JSON.parse(readFileSync50(settings2, "utf-8"));
|
|
29063
30125
|
const arr = Array.isArray(cfg.hooks?.SessionStart) ? cfg.hooks.SessionStart : [];
|
|
29064
30126
|
return arr.some((h) => (h?.hooks?.[0]?.command || "").includes("chat_recall_wakeup_hook"));
|
|
29065
30127
|
} catch {
|
|
@@ -29068,9 +30130,9 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29068
30130
|
};
|
|
29069
30131
|
const hookConfigFiles = claudeHookConfigFiles();
|
|
29070
30132
|
const readHookConfig = (file) => {
|
|
29071
|
-
if (!
|
|
30133
|
+
if (!existsSync57(file)) return { hooks: {} };
|
|
29072
30134
|
try {
|
|
29073
|
-
const c = JSON.parse(
|
|
30135
|
+
const c = JSON.parse(readFileSync50(file, "utf-8"));
|
|
29074
30136
|
if (!c.hooks || typeof c.hooks !== "object") c.hooks = {};
|
|
29075
30137
|
return c;
|
|
29076
30138
|
} catch (err) {
|
|
@@ -29081,9 +30143,58 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29081
30143
|
};
|
|
29082
30144
|
const matchesOurs = (h) => {
|
|
29083
30145
|
const cmd = h?.hooks?.[0]?.command;
|
|
29084
|
-
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;
|
|
29085
30197
|
};
|
|
29086
|
-
const HOOK_EVENTS = ["Stop", "PreCompact", "UserPromptSubmit", "SessionEnd", "SessionStart"];
|
|
29087
30198
|
if (opts.uninstall) {
|
|
29088
30199
|
let total = 0;
|
|
29089
30200
|
for (const hooksJson of hookConfigFiles) {
|
|
@@ -29096,29 +30207,39 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29096
30207
|
if (filtered.length) config2.hooks[event] = filtered;
|
|
29097
30208
|
else delete config2.hooks[event];
|
|
29098
30209
|
}
|
|
29099
|
-
if (removed === 0 && !
|
|
29100
|
-
|
|
30210
|
+
if (removed === 0 && !existsSync57(hooksJson)) continue;
|
|
30211
|
+
writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
|
|
29101
30212
|
total += removed;
|
|
29102
30213
|
console.log(chalk2.green(`\u2713 Removed ${removed} chat-recall hook entr${removed === 1 ? "y" : "ies"} from ${hooksJson}`));
|
|
29103
30214
|
}
|
|
30215
|
+
total += await removeGuardFromOtherTools();
|
|
29104
30216
|
if (total === 0) console.log(chalk2.dim("No chat-recall hook entries were registered."));
|
|
29105
30217
|
console.log(chalk2.dim(` (Hook scripts left in ${hooksDir} \u2014 delete manually if you want.)`));
|
|
29106
30218
|
return;
|
|
29107
30219
|
}
|
|
29108
30220
|
if (process.platform === "win32") {
|
|
29109
|
-
|
|
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."));
|
|
29110
30231
|
console.log(chalk2.dim(" They are POSIX shell scripts, and Claude Code runs hook commands through"));
|
|
29111
30232
|
console.log(chalk2.dim(" cmd.exe, which cannot execute them. Registering them would put an error on"));
|
|
29112
30233
|
console.log(chalk2.dim(" every turn instead of saving anything."));
|
|
29113
30234
|
console.log(chalk2.dim(""));
|
|
29114
|
-
console.log(chalk2.dim(" Everything else works: indexing, sync, search
|
|
29115
|
-
console.log(chalk2.dim("
|
|
29116
|
-
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."));
|
|
29117
30238
|
console.log(chalk2.dim(""));
|
|
29118
30239
|
console.log(chalk2.dim(" Under WSL, run this from inside the WSL shell and it installs normally."));
|
|
29119
30240
|
return;
|
|
29120
30241
|
}
|
|
29121
|
-
|
|
30242
|
+
mkdirSync23(hooksDir, { recursive: true });
|
|
29122
30243
|
copyFileSync4(sourceSaveHook, installedSaveHook);
|
|
29123
30244
|
chmodSync5(installedSaveHook, 493);
|
|
29124
30245
|
const installResume = opts.resumeHint !== false && !!sourceResumeHook;
|
|
@@ -29131,40 +30252,56 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29131
30252
|
copyFileSync4(sourceEscalateHook, installedEscalateHook);
|
|
29132
30253
|
chmodSync5(installedEscalateHook, 493);
|
|
29133
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
|
+
}
|
|
29134
30263
|
const installWakeup = opts.wakeup !== false && !!sourceWakeupHook;
|
|
29135
30264
|
if (installWakeup) {
|
|
29136
30265
|
copyFileSync4(sourceWakeupHook, installedWakeupHook);
|
|
29137
30266
|
chmodSync5(installedWakeupHook, 493);
|
|
29138
30267
|
}
|
|
30268
|
+
const installTaskBoard = opts.taskBoard !== false;
|
|
29139
30269
|
const stopEntry = { matcher: "", hooks: [{ type: "command", command: installedSaveHook }] };
|
|
29140
30270
|
const precompactEntry = { matcher: "", hooks: [{ type: "command", command: `"${installedSaveHook}" --precompact` }] };
|
|
29141
30271
|
const resumeEntry = { matcher: "", hooks: [{ type: "command", command: installedResumeHook }] };
|
|
29142
30272
|
const escalateEntry = { matcher: "", hooks: [{ type: "command", command: installedEscalateHook }] };
|
|
29143
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));
|
|
29144
30279
|
const events = [
|
|
29145
30280
|
["Stop", stopEntry],
|
|
29146
30281
|
["PreCompact", precompactEntry]
|
|
29147
30282
|
];
|
|
29148
30283
|
if (installResume) events.push(["UserPromptSubmit", resumeEntry]);
|
|
29149
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);
|
|
29150
30287
|
const wakeupSkipped = [];
|
|
29151
30288
|
for (const hooksJson of hookConfigFiles) {
|
|
29152
30289
|
const config2 = readHookConfig(hooksJson);
|
|
29153
|
-
const home =
|
|
30290
|
+
const home = dirname29(hooksJson);
|
|
29154
30291
|
const alreadyWired = wakeupWiredInSettings(home);
|
|
29155
|
-
if (alreadyWired) wakeupSkipped.push(
|
|
30292
|
+
if (alreadyWired) wakeupSkipped.push(join61(home, "settings.json"));
|
|
29156
30293
|
const perProfile = installWakeup && !alreadyWired ? [...events, ["SessionStart", wakeupEntry]] : events;
|
|
29157
30294
|
for (const event of HOOK_EVENTS) {
|
|
29158
30295
|
const arr = Array.isArray(config2.hooks[event]) ? config2.hooks[event] : [];
|
|
29159
30296
|
const without = arr.filter((h) => !matchesOurs(h));
|
|
29160
|
-
const
|
|
29161
|
-
if (wanted) without.push(wanted[1]);
|
|
30297
|
+
for (const [, entry] of perProfile.filter(([e]) => e === event)) without.push(entry);
|
|
29162
30298
|
if (without.length) config2.hooks[event] = without;
|
|
29163
30299
|
else delete config2.hooks[event];
|
|
29164
30300
|
}
|
|
29165
|
-
|
|
29166
|
-
|
|
30301
|
+
mkdirSync23(dirname29(hooksJson), { recursive: true });
|
|
30302
|
+
writeFileSync24(hooksJson, JSON.stringify(config2, null, 2) + "\n");
|
|
29167
30303
|
}
|
|
30304
|
+
if (installGuard) await addGuardToOtherTools(installedGuardHook);
|
|
29168
30305
|
const saveSz = statSync45(installedSaveHook).size;
|
|
29169
30306
|
console.log(chalk2.green(`\u2713 Installed chat-recall save hook (${saveSz} bytes)`));
|
|
29170
30307
|
if (installResume) {
|
|
@@ -29175,6 +30312,14 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29175
30312
|
const escalateSz = statSync45(installedEscalateHook).size;
|
|
29176
30313
|
console.log(chalk2.green(`\u2713 Installed chat-recall learnings-escalation hook (${escalateSz} bytes)`));
|
|
29177
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
|
+
}
|
|
29178
30323
|
if (installWakeup) {
|
|
29179
30324
|
const wakeupSz = statSync45(installedWakeupHook).size;
|
|
29180
30325
|
console.log(chalk2.green(`\u2713 Installed chat-recall wake-up hook (${wakeupSz} bytes)`));
|
|
@@ -29186,7 +30331,12 @@ program.command("install-hooks").description("Install Claude Code hooks (Stop +
|
|
|
29186
30331
|
for (const [i, f] of hookConfigFiles.entries()) {
|
|
29187
30332
|
console.log(chalk2.dim(` ${i === 0 ? "config: " : " "} ${f}`));
|
|
29188
30333
|
}
|
|
29189
|
-
|
|
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(", ")}`));
|
|
29190
30340
|
console.log();
|
|
29191
30341
|
console.log(chalk2.dim("Run `chat-recall install-hooks --uninstall` to remove later."));
|
|
29192
30342
|
if (!installResume) {
|
|
@@ -29208,7 +30358,7 @@ program.command("dossier <project>").description("Generate a project dossier (ov
|
|
|
29208
30358
|
);
|
|
29209
30359
|
const md = dossier.markdown;
|
|
29210
30360
|
if (options.out) {
|
|
29211
|
-
|
|
30361
|
+
writeFileSync23(options.out, md);
|
|
29212
30362
|
console.log(chalk2.green(`Wrote dossier to ${options.out} (${md.length} chars)`));
|
|
29213
30363
|
} else {
|
|
29214
30364
|
console.log(md);
|
|
@@ -29466,8 +30616,8 @@ include.command("list", { isDefault: true }).description("Show paths opted back
|
|
|
29466
30616
|
include.command("project <path>").description("Re-permit a path under a personal folder (substring match; e.g. ~/Documents/code)").action(async (path) => {
|
|
29467
30617
|
const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
29468
30618
|
const { resolve: resolve3 } = await import("node:path");
|
|
29469
|
-
const { homedir:
|
|
29470
|
-
const abs = resolve3(path.replace(/^~(?=\/|$)/,
|
|
30619
|
+
const { homedir: homedir24 } = await import("node:os");
|
|
30620
|
+
const abs = resolve3(path.replace(/^~(?=\/|$)/, homedir24()));
|
|
29471
30621
|
const s = loadSettings2();
|
|
29472
30622
|
s.sync.includeProjects = s.sync.includeProjects || [];
|
|
29473
30623
|
if (s.sync.includeProjects.includes(abs)) {
|
|
@@ -29481,8 +30631,8 @@ include.command("project <path>").description("Re-permit a path under a personal
|
|
|
29481
30631
|
include.command("remove <path>").description("Undo an opt-in (personal folder goes back to skipped)").action(async (path) => {
|
|
29482
30632
|
const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
29483
30633
|
const { resolve: resolve3 } = await import("node:path");
|
|
29484
|
-
const { homedir:
|
|
29485
|
-
const abs = resolve3(path.replace(/^~(?=\/|$)/,
|
|
30634
|
+
const { homedir: homedir24 } = await import("node:os");
|
|
30635
|
+
const abs = resolve3(path.replace(/^~(?=\/|$)/, homedir24()));
|
|
29486
30636
|
const s = loadSettings2();
|
|
29487
30637
|
const before = (s.sync.includeProjects || []).length;
|
|
29488
30638
|
s.sync.includeProjects = (s.sync.includeProjects || []).filter((p) => p !== path && p !== abs);
|
|
@@ -29621,8 +30771,8 @@ program.command("projects").description("List distinct project ids discovered on
|
|
|
29621
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) => {
|
|
29622
30772
|
const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
29623
30773
|
const { resolve: resolve3 } = await import("node:path");
|
|
29624
|
-
const { homedir:
|
|
29625
|
-
const abs = resolve3(path.replace(/^~(?=\/|$)/,
|
|
30774
|
+
const { homedir: homedir24 } = await import("node:os");
|
|
30775
|
+
const abs = resolve3(path.replace(/^~(?=\/|$)/, homedir24()));
|
|
29626
30776
|
const s = loadSettings2();
|
|
29627
30777
|
if (s.sync.excludeProjects.includes(abs)) {
|
|
29628
30778
|
console.log(chalk2.dim(`Already excluded: ${abs}`));
|
|
@@ -29651,8 +30801,8 @@ exclude.command("tool <tool>").description("Exclude an AI tool entirely (claude
|
|
|
29651
30801
|
exclude.command("remove <value>").description("Remove an exclusion (tool name or project path)").action(async (value) => {
|
|
29652
30802
|
const { loadSettings: loadSettings2, saveSettings: saveSettings2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
29653
30803
|
const { resolve: resolve3 } = await import("node:path");
|
|
29654
|
-
const { homedir:
|
|
29655
|
-
const abs = resolve3(value.replace(/^~(?=\/|$)/,
|
|
30804
|
+
const { homedir: homedir24 } = await import("node:os");
|
|
30805
|
+
const abs = resolve3(value.replace(/^~(?=\/|$)/, homedir24()));
|
|
29656
30806
|
const s = loadSettings2();
|
|
29657
30807
|
const beforeTools = s.sync.excludeTools.length;
|
|
29658
30808
|
const beforeProjects = s.sync.excludeProjects.length;
|