threadnote 1.0.0 → 1.1.0
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 +4 -0
- package/dist/mcp_server.cjs +4021 -1393
- package/dist/threadnote.cjs +182 -82
- package/docs/troubleshooting.md +4 -0
- package/package.json +1 -1
package/dist/threadnote.cjs
CHANGED
|
@@ -3616,6 +3616,41 @@ async function withSpinner(message, action) {
|
|
|
3616
3616
|
(0, import_node_readline.cursorTo)(import_node_process.stdout, 0);
|
|
3617
3617
|
}
|
|
3618
3618
|
}
|
|
3619
|
+
function startProgress(message) {
|
|
3620
|
+
if (import_node_process.stdout.isTTY !== true || process.env.CI !== void 0 || process.env.THREADNOTE_NO_SPINNER !== void 0) {
|
|
3621
|
+
console.log(message);
|
|
3622
|
+
return {
|
|
3623
|
+
update(nextMessage) {
|
|
3624
|
+
console.log(nextMessage);
|
|
3625
|
+
},
|
|
3626
|
+
stop() {
|
|
3627
|
+
return;
|
|
3628
|
+
}
|
|
3629
|
+
};
|
|
3630
|
+
}
|
|
3631
|
+
const frames = ["-", "\\", "|", "/"];
|
|
3632
|
+
let frameIndex = 0;
|
|
3633
|
+
let currentMessage = message;
|
|
3634
|
+
const render = () => {
|
|
3635
|
+
(0, import_node_readline.clearLine)(import_node_process.stdout, 0);
|
|
3636
|
+
(0, import_node_readline.cursorTo)(import_node_process.stdout, 0);
|
|
3637
|
+
import_node_process.stdout.write(`${muted(frames[frameIndex])} ${currentMessage}`);
|
|
3638
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
3639
|
+
};
|
|
3640
|
+
const timer = setInterval(render, 100);
|
|
3641
|
+
render();
|
|
3642
|
+
return {
|
|
3643
|
+
update(nextMessage) {
|
|
3644
|
+
currentMessage = nextMessage;
|
|
3645
|
+
render();
|
|
3646
|
+
},
|
|
3647
|
+
stop() {
|
|
3648
|
+
clearInterval(timer);
|
|
3649
|
+
(0, import_node_readline.clearLine)(import_node_process.stdout, 0);
|
|
3650
|
+
(0, import_node_readline.cursorTo)(import_node_process.stdout, 0);
|
|
3651
|
+
}
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
3619
3654
|
|
|
3620
3655
|
// src/utils.ts
|
|
3621
3656
|
function isJsonObject(value) {
|
|
@@ -3753,15 +3788,11 @@ async function maybeRun(dryRun, executable, args, options = {}) {
|
|
|
3753
3788
|
}
|
|
3754
3789
|
async function runCommand(executable, args, options = {}) {
|
|
3755
3790
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
3756
|
-
const child = (0, import_node_child_process.spawn)(executable, args, { cwd: options.cwd });
|
|
3757
|
-
const stdoutChunks = [];
|
|
3758
|
-
const stderrChunks = [];
|
|
3759
3791
|
const maxOutputBytes = options.maxOutputBytes ?? defaultCommandMaxOutputBytes();
|
|
3760
|
-
let stdoutBytes = 0;
|
|
3761
|
-
let stderrBytes = 0;
|
|
3762
3792
|
let finished = false;
|
|
3763
|
-
let failureResult;
|
|
3764
3793
|
let killTimer;
|
|
3794
|
+
let killEscalationTimer;
|
|
3795
|
+
let failureMessage;
|
|
3765
3796
|
let sentTerminationSignal = false;
|
|
3766
3797
|
const timeoutMs = options.timeoutMs ?? defaultCommandTimeoutMs();
|
|
3767
3798
|
const finish = (result) => {
|
|
@@ -3772,22 +3803,47 @@ async function runCommand(executable, args, options = {}) {
|
|
|
3772
3803
|
if (killTimer) {
|
|
3773
3804
|
clearTimeout(killTimer);
|
|
3774
3805
|
}
|
|
3806
|
+
if (killEscalationTimer) {
|
|
3807
|
+
clearTimeout(killEscalationTimer);
|
|
3808
|
+
}
|
|
3775
3809
|
if (result.exitCode !== 0 && options.allowFailure !== true) {
|
|
3776
3810
|
rejectPromise(new Error(`${formatShellCommand(executable, args)} failed: ${result.stderr || result.stdout}`));
|
|
3777
3811
|
return;
|
|
3778
3812
|
}
|
|
3779
3813
|
resolvePromise(result);
|
|
3780
3814
|
};
|
|
3815
|
+
const child = (0, import_node_child_process.execFile)(
|
|
3816
|
+
executable,
|
|
3817
|
+
[...args],
|
|
3818
|
+
{
|
|
3819
|
+
cwd: options.cwd,
|
|
3820
|
+
encoding: "utf8",
|
|
3821
|
+
maxBuffer: maxOutputBytes
|
|
3822
|
+
},
|
|
3823
|
+
(err, stdout2, stderr) => {
|
|
3824
|
+
finish(
|
|
3825
|
+
commandResultFromExecFileCallback({
|
|
3826
|
+
args,
|
|
3827
|
+
err,
|
|
3828
|
+
executable,
|
|
3829
|
+
failureMessage,
|
|
3830
|
+
maxOutputBytes,
|
|
3831
|
+
stderr,
|
|
3832
|
+
stdout: stdout2
|
|
3833
|
+
})
|
|
3834
|
+
);
|
|
3835
|
+
}
|
|
3836
|
+
);
|
|
3781
3837
|
const failAndKill = (message) => {
|
|
3782
|
-
if (
|
|
3838
|
+
if (failureMessage) {
|
|
3783
3839
|
return;
|
|
3784
3840
|
}
|
|
3785
|
-
|
|
3841
|
+
failureMessage = message;
|
|
3786
3842
|
if (!sentTerminationSignal) {
|
|
3787
3843
|
sentTerminationSignal = true;
|
|
3788
3844
|
child.kill("SIGTERM");
|
|
3789
3845
|
}
|
|
3790
|
-
setTimeout(() => {
|
|
3846
|
+
killEscalationTimer = setTimeout(() => {
|
|
3791
3847
|
if (!finished) {
|
|
3792
3848
|
child.kill("SIGKILL");
|
|
3793
3849
|
}
|
|
@@ -3799,54 +3855,20 @@ async function runCommand(executable, args, options = {}) {
|
|
|
3799
3855
|
}, timeoutMs);
|
|
3800
3856
|
killTimer.unref?.();
|
|
3801
3857
|
}
|
|
3802
|
-
child.stdout.on("data", (chunk) => {
|
|
3803
|
-
if (failureResult) {
|
|
3804
|
-
return;
|
|
3805
|
-
}
|
|
3806
|
-
const text = String(chunk);
|
|
3807
|
-
stdoutBytes += Buffer.byteLength(text);
|
|
3808
|
-
if (stdoutBytes + stderrBytes > maxOutputBytes) {
|
|
3809
|
-
failAndKill(`${formatShellCommand(executable, args)} exceeded output limit of ${maxOutputBytes} bytes`);
|
|
3810
|
-
return;
|
|
3811
|
-
}
|
|
3812
|
-
stdoutChunks.push(text);
|
|
3813
|
-
});
|
|
3814
|
-
child.stderr.on("data", (chunk) => {
|
|
3815
|
-
if (failureResult) {
|
|
3816
|
-
return;
|
|
3817
|
-
}
|
|
3818
|
-
const text = String(chunk);
|
|
3819
|
-
stderrBytes += Buffer.byteLength(text);
|
|
3820
|
-
if (stdoutBytes + stderrBytes > maxOutputBytes) {
|
|
3821
|
-
failAndKill(`${formatShellCommand(executable, args)} exceeded output limit of ${maxOutputBytes} bytes`);
|
|
3822
|
-
return;
|
|
3823
|
-
}
|
|
3824
|
-
stderrChunks.push(text);
|
|
3825
|
-
});
|
|
3826
|
-
child.on("error", (err) => {
|
|
3827
|
-
if (finished) {
|
|
3828
|
-
return;
|
|
3829
|
-
}
|
|
3830
|
-
if (killTimer) {
|
|
3831
|
-
clearTimeout(killTimer);
|
|
3832
|
-
}
|
|
3833
|
-
if (options.allowFailure === true) {
|
|
3834
|
-
finish({ exitCode: 127, stderr: errorMessage(err), stdout: "" });
|
|
3835
|
-
} else {
|
|
3836
|
-
finished = true;
|
|
3837
|
-
rejectPromise(err);
|
|
3838
|
-
}
|
|
3839
|
-
});
|
|
3840
|
-
child.on("close", (code) => {
|
|
3841
|
-
const result = failureResult ?? {
|
|
3842
|
-
exitCode: code ?? 1,
|
|
3843
|
-
stderr: stderrChunks.join(""),
|
|
3844
|
-
stdout: stdoutChunks.join("")
|
|
3845
|
-
};
|
|
3846
|
-
finish(result);
|
|
3847
|
-
});
|
|
3848
3858
|
});
|
|
3849
3859
|
}
|
|
3860
|
+
function commandResultFromExecFileCallback(params) {
|
|
3861
|
+
if (params.failureMessage) {
|
|
3862
|
+
return { exitCode: 124, stderr: params.failureMessage, stdout: params.stdout };
|
|
3863
|
+
}
|
|
3864
|
+
if (!params.err) {
|
|
3865
|
+
return { exitCode: 0, stderr: params.stderr, stdout: params.stdout };
|
|
3866
|
+
}
|
|
3867
|
+
const error = params.err;
|
|
3868
|
+
const exitCode = typeof error.code === "number" ? error.code : error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" ? 124 : 127;
|
|
3869
|
+
const stderr = error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" ? `${formatShellCommand(params.executable, params.args)} exceeded output limit of ${params.maxOutputBytes} bytes` : params.stderr || errorMessage(error);
|
|
3870
|
+
return { exitCode, stderr, stdout: params.stdout };
|
|
3871
|
+
}
|
|
3850
3872
|
function defaultCommandTimeoutMs() {
|
|
3851
3873
|
return positiveIntegerFromEnv("THREADNOTE_COMMAND_TIMEOUT_MS") ?? 10 * 60 * 1e3;
|
|
3852
3874
|
}
|
|
@@ -7529,9 +7551,17 @@ var AUTO_REPAIR_STATE_FILE = "index-auto-repair.json";
|
|
|
7529
7551
|
var AUTO_REPAIR_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
7530
7552
|
var MAX_SCAN_DEPTH = 5;
|
|
7531
7553
|
var MAX_REPAIR_TARGETS = 4;
|
|
7532
|
-
var
|
|
7554
|
+
var MAINTENANCE_COLLAPSE_DEPTH = 3;
|
|
7555
|
+
var MAINTENANCE_MAX_REPAIR_TARGETS = 512;
|
|
7533
7556
|
async function repairStaleRecallIndex(config, ov, options = {}) {
|
|
7557
|
+
options.onProgress?.({ type: "scan-start" });
|
|
7534
7558
|
const targets = await findStaleRecallIndexTargets(config, options);
|
|
7559
|
+
const repairTargets = targets.slice(0, options.maxTargets ?? MAX_REPAIR_TARGETS);
|
|
7560
|
+
options.onProgress?.({
|
|
7561
|
+
repairTargetCount: repairTargets.length,
|
|
7562
|
+
totalTargets: targets.length,
|
|
7563
|
+
type: "scan-complete"
|
|
7564
|
+
});
|
|
7535
7565
|
if (targets.length === 0) {
|
|
7536
7566
|
return { repairedUris: [], skippedRecentUris: [], warnings: [] };
|
|
7537
7567
|
}
|
|
@@ -7540,16 +7570,20 @@ async function repairStaleRecallIndex(config, ov, options = {}) {
|
|
|
7540
7570
|
const repairedUris = [];
|
|
7541
7571
|
const skippedRecentUris = [];
|
|
7542
7572
|
const warnings = [];
|
|
7543
|
-
for (const target of
|
|
7573
|
+
for (const [targetIndex, target] of repairTargets.entries()) {
|
|
7574
|
+
const progressBase = { index: targetIndex + 1, target, total: repairTargets.length };
|
|
7544
7575
|
const previous = state.entries[target.uri];
|
|
7545
7576
|
if (previous?.signature === target.signature && now - Date.parse(previous.repairedAt) < AUTO_REPAIR_TTL_MS && options.dryRun !== true && options.ignoreBackoff !== true) {
|
|
7577
|
+
options.onProgress?.({ ...progressBase, type: "repair-skip-recent" });
|
|
7546
7578
|
skippedRecentUris.push(target.uri);
|
|
7547
7579
|
continue;
|
|
7548
7580
|
}
|
|
7549
7581
|
if (options.dryRun === true) {
|
|
7582
|
+
options.onProgress?.({ ...progressBase, type: "repair-dry-run" });
|
|
7550
7583
|
repairedUris.push(target.uri);
|
|
7551
7584
|
continue;
|
|
7552
7585
|
}
|
|
7586
|
+
options.onProgress?.({ ...progressBase, type: "repair-start" });
|
|
7553
7587
|
const result = await runCommand(
|
|
7554
7588
|
ov,
|
|
7555
7589
|
withIdentity(config, ["reindex", target.uri, "--mode", "semantic_and_vectors", "--wait", "true"]),
|
|
@@ -7560,6 +7594,7 @@ async function repairStaleRecallIndex(config, ov, options = {}) {
|
|
|
7560
7594
|
state.entries[target.uri] = { repairedAt: new Date(now).toISOString(), signature: target.signature };
|
|
7561
7595
|
} else {
|
|
7562
7596
|
warnings.push(indexRepairWarning(target.uri, result));
|
|
7597
|
+
await options.onRepairFailure?.(target, result);
|
|
7563
7598
|
}
|
|
7564
7599
|
}
|
|
7565
7600
|
if (options.dryRun !== true && repairedUris.length > 0) {
|
|
@@ -7576,16 +7611,14 @@ async function findStaleRecallIndexTargets(config, options = {}) {
|
|
|
7576
7611
|
}
|
|
7577
7612
|
const sidecars = await staleSidecars(root.path, root.uri);
|
|
7578
7613
|
if (options.collapseToRoots === true) {
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7614
|
+
for (const sidecar of sidecars) {
|
|
7615
|
+
const uri = collapseStaleSidecarUri(root.uri, sidecar.relativePath, options.collapseDepth);
|
|
7616
|
+
const current = byUri.get(uri) ?? { parts: [], staleCount: 0, uri };
|
|
7617
|
+
current.parts.push(`${sidecar.relativePath}
|
|
7583
7618
|
${sidecar.content}`);
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
uri: root.uri
|
|
7588
|
-
});
|
|
7619
|
+
current.staleCount += 1;
|
|
7620
|
+
byUri.set(uri, current);
|
|
7621
|
+
}
|
|
7589
7622
|
continue;
|
|
7590
7623
|
}
|
|
7591
7624
|
for (const sidecar of sidecars) {
|
|
@@ -7602,13 +7635,29 @@ ${sidecar.content}`);
|
|
|
7602
7635
|
uri: target.uri
|
|
7603
7636
|
}));
|
|
7604
7637
|
}
|
|
7638
|
+
function collapseStaleSidecarUri(rootUri, relativePath, depth = 0) {
|
|
7639
|
+
const normalizedRootUri = trimLocalRootUri(rootUri);
|
|
7640
|
+
if (depth <= 0) {
|
|
7641
|
+
return normalizedRootUri;
|
|
7642
|
+
}
|
|
7643
|
+
const parentParts = relativePath.split("/").slice(0, -1);
|
|
7644
|
+
const collapsedParts = parentParts.slice(0, depth);
|
|
7645
|
+
return collapsedParts.length === 0 ? normalizedRootUri : `${normalizedRootUri}/${collapsedParts.join("/")}`;
|
|
7646
|
+
}
|
|
7605
7647
|
function formatRecallIndexRepairMessages(result, options = {}) {
|
|
7606
7648
|
const messages = [];
|
|
7607
|
-
|
|
7649
|
+
const maxUris = options.maxUris ?? result.repairedUris.length;
|
|
7650
|
+
for (const uri of result.repairedUris.slice(0, maxUris)) {
|
|
7608
7651
|
messages.push(
|
|
7609
7652
|
`${options.dryRun === true ? "Would auto-reindex stale recall scope" : "Auto-reindexed stale recall scope"}: ${uri}`
|
|
7610
7653
|
);
|
|
7611
7654
|
}
|
|
7655
|
+
const hiddenUriCount = result.repairedUris.length - maxUris;
|
|
7656
|
+
if (hiddenUriCount > 0) {
|
|
7657
|
+
messages.push(
|
|
7658
|
+
options.dryRun === true ? `Would auto-reindex ${hiddenUriCount} more stale recall scope(s).` : `Auto-reindexed ${hiddenUriCount} more stale recall scope(s).`
|
|
7659
|
+
);
|
|
7660
|
+
}
|
|
7612
7661
|
for (const warning2 of result.warnings) {
|
|
7613
7662
|
messages.push(`Auto-index repair warning: ${warning2}`);
|
|
7614
7663
|
}
|
|
@@ -11622,7 +11671,9 @@ async function runUpdate(config, options) {
|
|
|
11622
11671
|
return;
|
|
11623
11672
|
}
|
|
11624
11673
|
const threadnoteCommand = await installedThreadnoteCommand(runtime);
|
|
11625
|
-
|
|
11674
|
+
console.log("");
|
|
11675
|
+
console.log("Repairing local Threadnote setup after package update.");
|
|
11676
|
+
await runThreadnoteSubcommand(options.dryRun === true, threadnoteCommand, ["repair", "--no-post-update"]);
|
|
11626
11677
|
if (options.postUpdate !== false) {
|
|
11627
11678
|
const postUpdateArgs = [
|
|
11628
11679
|
"post-update",
|
|
@@ -11632,15 +11683,7 @@ async function runUpdate(config, options) {
|
|
|
11632
11683
|
info2.latestVersion,
|
|
11633
11684
|
...options.yes === true ? ["--yes"] : []
|
|
11634
11685
|
];
|
|
11635
|
-
|
|
11636
|
-
await maybeRun(true, threadnoteCommand, postUpdateArgs);
|
|
11637
|
-
} else {
|
|
11638
|
-
console.log(`Running: ${formatShellCommand(threadnoteCommand, postUpdateArgs)}`);
|
|
11639
|
-
const postUpdateExitCode = await runInteractive(threadnoteCommand, postUpdateArgs);
|
|
11640
|
-
if (postUpdateExitCode !== 0) {
|
|
11641
|
-
throw new Error(`${formatShellCommand(threadnoteCommand, postUpdateArgs)} exited with ${postUpdateExitCode}.`);
|
|
11642
|
-
}
|
|
11643
|
-
}
|
|
11686
|
+
await runThreadnoteSubcommand(options.dryRun === true, threadnoteCommand, postUpdateArgs);
|
|
11644
11687
|
} else {
|
|
11645
11688
|
console.log("Skipping post-update migration prompts because --no-post-update was provided.");
|
|
11646
11689
|
}
|
|
@@ -11742,9 +11785,11 @@ async function ensurePinnedOpenVikingInstalled(config, options) {
|
|
|
11742
11785
|
if (usingLaunchd) {
|
|
11743
11786
|
const launchAgentPath = launchAgentPlistPath();
|
|
11744
11787
|
await runCommand("launchctl", ["unload", launchAgentPath], { allowFailure: true });
|
|
11788
|
+
await waitForOpenVikingPortClosed(config, 15e3);
|
|
11745
11789
|
await runCommand("launchctl", ["load", launchAgentPath], { allowFailure: true });
|
|
11746
11790
|
} else {
|
|
11747
11791
|
await maybeRun(false, threadnoteCommand, ["stop"]);
|
|
11792
|
+
await waitForOpenVikingPortClosed(config, 15e3);
|
|
11748
11793
|
await maybeRun(false, threadnoteCommand, ["start"]);
|
|
11749
11794
|
}
|
|
11750
11795
|
const healthyAfter = await waitForOpenVikingHealthy(config, 1e4);
|
|
@@ -11755,6 +11800,17 @@ async function ensurePinnedOpenVikingInstalled(config, options) {
|
|
|
11755
11800
|
console.log("Check the server log or run: threadnote start");
|
|
11756
11801
|
}
|
|
11757
11802
|
}
|
|
11803
|
+
async function runThreadnoteSubcommand(dryRun, executable, args) {
|
|
11804
|
+
if (dryRun) {
|
|
11805
|
+
await maybeRun(true, executable, args);
|
|
11806
|
+
return;
|
|
11807
|
+
}
|
|
11808
|
+
console.log(`Running: ${formatShellCommand(executable, args)}`);
|
|
11809
|
+
const exitCode = await runInteractive(executable, args);
|
|
11810
|
+
if (exitCode !== 0) {
|
|
11811
|
+
throw new Error(`${formatShellCommand(executable, args)} exited with ${exitCode}.`);
|
|
11812
|
+
}
|
|
11813
|
+
}
|
|
11758
11814
|
function openVikingHealthEndpoint(config) {
|
|
11759
11815
|
return `http://${config.host}:${config.port}/health`;
|
|
11760
11816
|
}
|
|
@@ -11778,12 +11834,27 @@ async function waitForOpenVikingHealthy(config, timeoutMs) {
|
|
|
11778
11834
|
if (await isOpenVikingHealthy(config)) {
|
|
11779
11835
|
return true;
|
|
11780
11836
|
}
|
|
11781
|
-
await
|
|
11782
|
-
setTimeout(resolvePromise, 500);
|
|
11783
|
-
});
|
|
11837
|
+
await sleep(500);
|
|
11784
11838
|
}
|
|
11785
11839
|
return isOpenVikingHealthy(config);
|
|
11786
11840
|
}
|
|
11841
|
+
async function waitForOpenVikingPortClosed(config, timeoutMs) {
|
|
11842
|
+
console.log(`Waiting for OpenViking port ${config.host}:${config.port} to close before restart.`);
|
|
11843
|
+
const deadline = Date.now() + timeoutMs;
|
|
11844
|
+
while (Date.now() < deadline) {
|
|
11845
|
+
if (!await isTcpPortOpen(config.host, config.port, 300)) {
|
|
11846
|
+
return true;
|
|
11847
|
+
}
|
|
11848
|
+
await sleep(300);
|
|
11849
|
+
}
|
|
11850
|
+
if (!await isTcpPortOpen(config.host, config.port, 300)) {
|
|
11851
|
+
return true;
|
|
11852
|
+
}
|
|
11853
|
+
console.log(
|
|
11854
|
+
`Warning: OpenViking port ${config.host}:${config.port} is still in use after ${timeoutMs / 1e3}s; start may fail.`
|
|
11855
|
+
);
|
|
11856
|
+
return false;
|
|
11857
|
+
}
|
|
11787
11858
|
function launchAgentPlistPath() {
|
|
11788
11859
|
return (0, import_node_path12.join)((0, import_node_os5.homedir)(), "Library", "LaunchAgents", "io.threadnote.openviking.plist");
|
|
11789
11860
|
}
|
|
@@ -12314,16 +12385,44 @@ async function repairRecallIndex(config, dryRun) {
|
|
|
12314
12385
|
console.log("Skipping recall index repair: neither ov nor openviking was found in PATH.");
|
|
12315
12386
|
return;
|
|
12316
12387
|
}
|
|
12388
|
+
const progress = startProgress("Scanning recall index freshness across memories and seeded resources.");
|
|
12317
12389
|
try {
|
|
12318
12390
|
const result = await repairStaleRecallIndex(config, ov, {
|
|
12391
|
+
collapseDepth: MAINTENANCE_COLLAPSE_DEPTH,
|
|
12319
12392
|
collapseToRoots: true,
|
|
12320
12393
|
dryRun,
|
|
12321
12394
|
ignoreBackoff: true,
|
|
12322
12395
|
includeAgentSkills: true,
|
|
12323
12396
|
includeManifestResources: true,
|
|
12324
|
-
maxTargets: MAINTENANCE_MAX_REPAIR_TARGETS
|
|
12397
|
+
maxTargets: MAINTENANCE_MAX_REPAIR_TARGETS,
|
|
12398
|
+
onRepairFailure: async () => {
|
|
12399
|
+
progress.update("A recall index reindex failed; checking OpenViking health before continuing.");
|
|
12400
|
+
await repairServerHealth(config, dryRun);
|
|
12401
|
+
},
|
|
12402
|
+
onProgress: (event) => {
|
|
12403
|
+
if (event.type === "scan-complete") {
|
|
12404
|
+
if (event.totalTargets === 0) {
|
|
12405
|
+
progress.update("No stale recall index scopes found.");
|
|
12406
|
+
} else {
|
|
12407
|
+
progress.update(
|
|
12408
|
+
`Found ${event.totalTargets} stale recall index scope(s); repairing ${event.repairTargetCount}.`
|
|
12409
|
+
);
|
|
12410
|
+
}
|
|
12411
|
+
} else if (event.type === "repair-start") {
|
|
12412
|
+
progress.update(
|
|
12413
|
+
`Reindexing ${event.index}/${event.total}: ${event.target.uri} (${event.target.staleCount} stale summaries).`
|
|
12414
|
+
);
|
|
12415
|
+
} else if (event.type === "repair-dry-run") {
|
|
12416
|
+
progress.update(
|
|
12417
|
+
`Planning reindex ${event.index}/${event.total}: ${event.target.uri} (${event.target.staleCount} stale summaries).`
|
|
12418
|
+
);
|
|
12419
|
+
} else if (event.type === "repair-skip-recent") {
|
|
12420
|
+
progress.update(`Skipping recently repaired scope ${event.index}/${event.total}: ${event.target.uri}.`);
|
|
12421
|
+
}
|
|
12422
|
+
}
|
|
12325
12423
|
});
|
|
12326
|
-
|
|
12424
|
+
progress.stop();
|
|
12425
|
+
const messages = formatRecallIndexRepairMessages(result, { dryRun, maxUris: 20 });
|
|
12327
12426
|
if (messages.length === 0) {
|
|
12328
12427
|
console.log("Recall index freshness OK.");
|
|
12329
12428
|
return;
|
|
@@ -12332,6 +12431,7 @@ async function repairRecallIndex(config, dryRun) {
|
|
|
12332
12431
|
console.log(message);
|
|
12333
12432
|
}
|
|
12334
12433
|
} catch (err) {
|
|
12434
|
+
progress.stop();
|
|
12335
12435
|
console.log(`WARN could not repair recall index freshness: ${errorMessage(err)}`);
|
|
12336
12436
|
}
|
|
12337
12437
|
}
|
package/docs/troubleshooting.md
CHANGED
|
@@ -124,6 +124,10 @@ Threadnote uses its bundled stdio MCP adapter by default, even when the installe
|
|
|
124
124
|
`/mcp`. The adapter adds Threadnote-specific tools and behavior such as shared-memory sync, exact recall fallback,
|
|
125
125
|
seeded-resource recall augmentation, and recall-index repair.
|
|
126
126
|
|
|
127
|
+
The adapter also exposes raw OpenViking parity tools with `ov_*` names for native behaviors such as code symbol
|
|
128
|
+
navigation, watch management, raw search/read/list/store/remember, grep/glob, resource import, and forget. Prefer
|
|
129
|
+
Threadnote-named tools for memory workflows; use `ov_*` when you intentionally want native OpenViking behavior.
|
|
130
|
+
|
|
127
131
|
Use the default stdio adapter:
|
|
128
132
|
|
|
129
133
|
```bash
|