threadnote 1.3.3 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp_server.cjs +21 -18
- package/dist/threadnote.cjs +114 -44
- package/docs/agent-instructions.md +2 -2
- package/docs/index.html +2 -2
- package/docs/migration.md +6 -0
- package/package.json +1 -1
package/dist/mcp_server.cjs
CHANGED
|
@@ -35689,6 +35689,9 @@ function commandResultFromExecFileCallback(params) {
|
|
|
35689
35689
|
function defaultCommandTimeoutMs() {
|
|
35690
35690
|
return positiveIntegerFromEnv("THREADNOTE_COMMAND_TIMEOUT_MS") ?? 10 * 60 * 1e3;
|
|
35691
35691
|
}
|
|
35692
|
+
function reindexWaitTimeoutMs() {
|
|
35693
|
+
return positiveIntegerFromEnv("THREADNOTE_REINDEX_TIMEOUT_MS") ?? 12e4;
|
|
35694
|
+
}
|
|
35692
35695
|
function defaultCommandMaxOutputBytes() {
|
|
35693
35696
|
return positiveIntegerFromEnv("THREADNOTE_COMMAND_MAX_OUTPUT_BYTES") ?? 5 * 1024 * 1024;
|
|
35694
35697
|
}
|
|
@@ -36290,7 +36293,7 @@ function readStringArray(object3, key) {
|
|
|
36290
36293
|
|
|
36291
36294
|
// src/runtime.ts
|
|
36292
36295
|
function withIdentity(config2, args) {
|
|
36293
|
-
return [...args, "--account", config2.account, "--user", config2.user
|
|
36296
|
+
return [...args, "--account", config2.account, "--user", config2.user];
|
|
36294
36297
|
}
|
|
36295
36298
|
|
|
36296
36299
|
// src/index_repair.ts
|
|
@@ -36334,7 +36337,11 @@ async function repairStaleRecallIndex(config2, ov, options = {}) {
|
|
|
36334
36337
|
const result = await runCommand(
|
|
36335
36338
|
ov,
|
|
36336
36339
|
withIdentity(config2, ["reindex", target.uri, "--mode", "semantic_and_vectors", "--wait", "true"]),
|
|
36337
|
-
|
|
36340
|
+
// Bound the wait: `ov reindex` has no --timeout, so a stuck/poisoned
|
|
36341
|
+
// semantic queue would block each target for the full 10-min command
|
|
36342
|
+
// timeout. A timed-out target counts as a failure and trips the
|
|
36343
|
+
// consecutive-failure stop instead of hanging the whole repair.
|
|
36344
|
+
{ allowFailure: true, timeoutMs: reindexWaitTimeoutMs() }
|
|
36338
36345
|
);
|
|
36339
36346
|
if (result.exitCode === 0) {
|
|
36340
36347
|
consecutiveFailures = 0;
|
|
@@ -38580,7 +38587,7 @@ async function refreshMemoryIndex(config2, ov, uri, options = {}) {
|
|
|
38580
38587
|
const result = await runCommand(
|
|
38581
38588
|
ov,
|
|
38582
38589
|
withIdentity(config2, ["reindex", uri, "--mode", "vectors_only", "--wait", "true"]),
|
|
38583
|
-
{ allowFailure: true }
|
|
38590
|
+
{ allowFailure: true, timeoutMs: reindexWaitTimeoutMs() }
|
|
38584
38591
|
);
|
|
38585
38592
|
if (result.exitCode === 0) {
|
|
38586
38593
|
if (options.quiet !== true && result.stdout.trim()) {
|
|
@@ -39359,7 +39366,7 @@ function registerTools(server, config2) {
|
|
|
39359
39366
|
}
|
|
39360
39367
|
},
|
|
39361
39368
|
async ({ recursive, uri }) => {
|
|
39362
|
-
const checkedUri = requiredVikingUri(uri, "forget", "viking://
|
|
39369
|
+
const checkedUri = requiredVikingUri(uri, "forget", "viking://user/you/memories/example.md");
|
|
39363
39370
|
if (!checkedUri.ok) {
|
|
39364
39371
|
return checkedUri.error;
|
|
39365
39372
|
}
|
|
@@ -39603,8 +39610,6 @@ function registerOpenVikingParityTools(server, config2) {
|
|
|
39603
39610
|
limit: external_exports.number().int().positive().max(1e3).optional().describe("Maximum result count"),
|
|
39604
39611
|
minScore: external_exports.number().min(0).max(1).optional().describe("Minimum score threshold"),
|
|
39605
39612
|
min_score: external_exports.number().min(0).max(1).optional().describe("Minimum score threshold"),
|
|
39606
|
-
peerId: external_exports.string().optional().describe("Optional native peer id"),
|
|
39607
|
-
peer_id: external_exports.string().optional().describe("Optional native peer id"),
|
|
39608
39613
|
query: external_exports.string().optional().describe("Required search query"),
|
|
39609
39614
|
sessionId: external_exports.string().optional().describe("Optional native session id"),
|
|
39610
39615
|
session_id: external_exports.string().optional().describe("Optional native session id"),
|
|
@@ -40081,7 +40086,7 @@ async function collectExactMemoryMatches(config2, query, includeArchived, projec
|
|
|
40081
40086
|
return collectExactMatches(terms, scopes, async (term, scope) => {
|
|
40082
40087
|
const result = await runCommand(
|
|
40083
40088
|
ov,
|
|
40084
|
-
|
|
40089
|
+
withIdentity(config2, ["grep", term, "--uri", scope, "--node-limit", "5", "--output", "json"]),
|
|
40085
40090
|
{ allowFailure: true }
|
|
40086
40091
|
);
|
|
40087
40092
|
return result.exitCode === 0 ? result.stdout : void 0;
|
|
@@ -40099,7 +40104,7 @@ function registerReadTool(server, config2, name, description) {
|
|
|
40099
40104
|
}
|
|
40100
40105
|
},
|
|
40101
40106
|
async ({ uri, uris }) => {
|
|
40102
|
-
const checkedUris = requiredVikingUriList(uris ?? uri, name, "viking://
|
|
40107
|
+
const checkedUris = requiredVikingUriList(uris ?? uri, name, "viking://user/you/memories/.abstract.md");
|
|
40103
40108
|
if (!checkedUris.ok) {
|
|
40104
40109
|
return checkedUris.error;
|
|
40105
40110
|
}
|
|
@@ -40526,11 +40531,11 @@ async function writeSharedMemoryReplacement(config2, ov, params, targetUri) {
|
|
|
40526
40531
|
return { content: [{ type: "text", text: messages.join("\n") }] };
|
|
40527
40532
|
}
|
|
40528
40533
|
async function vikingResourceExists2(ov, config2, uri) {
|
|
40529
|
-
const stat2 = await runCommand(ov,
|
|
40534
|
+
const stat2 = await runCommand(ov, withIdentity(config2, ["stat", uri]), { allowFailure: true });
|
|
40530
40535
|
return stat2.exitCode === 0;
|
|
40531
40536
|
}
|
|
40532
40537
|
async function removeVikingResourceWithRetry(ov, config2, uri, recursive = false) {
|
|
40533
|
-
const args =
|
|
40538
|
+
const args = withIdentity(config2, ["rm", uri, ...recursive ? ["--recursive"] : []]);
|
|
40534
40539
|
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
40535
40540
|
const result = await runCommand(ov, args, { allowFailure: true });
|
|
40536
40541
|
if (result.exitCode === 0) {
|
|
@@ -40570,13 +40575,13 @@ ${stdout2}`.toLowerCase();
|
|
|
40570
40575
|
}
|
|
40571
40576
|
async function ensureMemoryDirectory(ov, config2, directoryUri) {
|
|
40572
40577
|
for (const uri of vikingDirectoryChain(directoryUri)) {
|
|
40573
|
-
const statResult = await runCommand(ov,
|
|
40578
|
+
const statResult = await runCommand(ov, withIdentity(config2, ["stat", uri]), { allowFailure: true });
|
|
40574
40579
|
if (statResult.exitCode === 0) {
|
|
40575
40580
|
continue;
|
|
40576
40581
|
}
|
|
40577
40582
|
await runCommand(
|
|
40578
40583
|
ov,
|
|
40579
|
-
|
|
40584
|
+
withIdentity(config2, ["mkdir", uri, "--description", "Threadnote lifecycle-aware local memories."])
|
|
40580
40585
|
);
|
|
40581
40586
|
}
|
|
40582
40587
|
}
|
|
@@ -40793,7 +40798,7 @@ ${text}`);
|
|
|
40793
40798
|
async function runOpenVikingCliReadTool(config2, uri) {
|
|
40794
40799
|
try {
|
|
40795
40800
|
const ov = await requiredOpenVikingCli2();
|
|
40796
|
-
const result = await runCommand(ov,
|
|
40801
|
+
const result = await runCommand(ov, withIdentity(config2, ["read", uri]));
|
|
40797
40802
|
const text = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
|
|
40798
40803
|
return { content: [{ type: "text", text: text || "OK" }] };
|
|
40799
40804
|
} catch (err) {
|
|
@@ -40803,7 +40808,7 @@ async function runOpenVikingCliReadTool(config2, uri) {
|
|
|
40803
40808
|
async function runOpenVikingTool(config2, args) {
|
|
40804
40809
|
try {
|
|
40805
40810
|
const ov = await requiredOpenVikingCli2();
|
|
40806
|
-
const result = await runCommand(ov,
|
|
40811
|
+
const result = await runCommand(ov, withIdentity(config2, args));
|
|
40807
40812
|
const text = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
|
|
40808
40813
|
return { content: [{ type: "text", text: text || "OK" }] };
|
|
40809
40814
|
} catch (err) {
|
|
@@ -40816,8 +40821,9 @@ async function runOpenVikingMcpTool(config2, toolName, args) {
|
|
|
40816
40821
|
const transport = new StreamableHTTPClientTransport(new URL(config2.openVikingMcpUrl), {
|
|
40817
40822
|
requestInit: {
|
|
40818
40823
|
headers: {
|
|
40824
|
+
// OpenViking 0.4.x dropped agent_id as an identity input; only
|
|
40825
|
+
// account + user are honored (mirrors withIdentity in runtime.ts).
|
|
40819
40826
|
"X-OpenViking-Account": config2.account,
|
|
40820
|
-
"X-OpenViking-Agent": config2.agentId,
|
|
40821
40827
|
"X-OpenViking-User": config2.user
|
|
40822
40828
|
}
|
|
40823
40829
|
}
|
|
@@ -41048,9 +41054,6 @@ function shareArtifactToolHeader(team, syncedTeams, warnings) {
|
|
|
41048
41054
|
}
|
|
41049
41055
|
return lines;
|
|
41050
41056
|
}
|
|
41051
|
-
function withIdentity2(config2, args) {
|
|
41052
|
-
return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
|
|
41053
|
-
}
|
|
41054
41057
|
async function requiredOpenVikingCli2() {
|
|
41055
41058
|
const command2 = await findOpenVikingCli();
|
|
41056
41059
|
if (!command2) {
|
package/dist/threadnote.cjs
CHANGED
|
@@ -3394,7 +3394,7 @@ var program = new Command();
|
|
|
3394
3394
|
// src/constants.ts
|
|
3395
3395
|
var DEFAULT_HOST = "127.0.0.1";
|
|
3396
3396
|
var DEFAULT_PORT = 1933;
|
|
3397
|
-
var DEFAULT_OPENVIKING_VERSION = "0.
|
|
3397
|
+
var DEFAULT_OPENVIKING_VERSION = "0.4.4";
|
|
3398
3398
|
var OPENVIKING_TOOL_PYTHON = "3.12";
|
|
3399
3399
|
var DEFAULT_ACCOUNT = "local";
|
|
3400
3400
|
var DEFAULT_AGENT_ID = "threadnote";
|
|
@@ -3412,6 +3412,7 @@ var USER_INSTRUCTIONS_START_MARKER = "<!-- BEGIN THREADNOTE USER INSTRUCTIONS --
|
|
|
3412
3412
|
var USER_INSTRUCTIONS_END_MARKER = "<!-- END THREADNOTE USER INSTRUCTIONS -->";
|
|
3413
3413
|
var USER_MANIFEST_NAME = "seed-manifest.yaml";
|
|
3414
3414
|
var SEED_STATE_FILE = "seed-state.json";
|
|
3415
|
+
var SEED_WATCH_INTERVAL_ENV = "THREADNOTE_SEED_WATCH_INTERVAL";
|
|
3415
3416
|
var USER_AGENT_INSTRUCTION_TARGETS = [
|
|
3416
3417
|
{ kind: "block", label: "codex user instructions", path: "~/.codex/AGENTS.md" },
|
|
3417
3418
|
{ kind: "block", label: "claude user instructions", path: "~/.claude/CLAUDE.md" },
|
|
@@ -3834,6 +3835,9 @@ function commandResultFromExecFileCallback(params) {
|
|
|
3834
3835
|
function defaultCommandTimeoutMs() {
|
|
3835
3836
|
return positiveIntegerFromEnv("THREADNOTE_COMMAND_TIMEOUT_MS") ?? 10 * 60 * 1e3;
|
|
3836
3837
|
}
|
|
3838
|
+
function reindexWaitTimeoutMs() {
|
|
3839
|
+
return positiveIntegerFromEnv("THREADNOTE_REINDEX_TIMEOUT_MS") ?? 12e4;
|
|
3840
|
+
}
|
|
3837
3841
|
function defaultCommandMaxOutputBytes() {
|
|
3838
3842
|
return positiveIntegerFromEnv("THREADNOTE_COMMAND_MAX_OUTPUT_BYTES") ?? 5 * 1024 * 1024;
|
|
3839
3843
|
}
|
|
@@ -3915,24 +3919,34 @@ function compareVersions(a, b) {
|
|
|
3915
3919
|
return difference;
|
|
3916
3920
|
}
|
|
3917
3921
|
}
|
|
3918
|
-
|
|
3919
|
-
|
|
3922
|
+
const rankDelta = suffixRank(left.suffix) - suffixRank(right.suffix);
|
|
3923
|
+
if (rankDelta !== 0) {
|
|
3924
|
+
return rankDelta;
|
|
3920
3925
|
}
|
|
3921
|
-
if (left.
|
|
3922
|
-
return
|
|
3926
|
+
if (left.suffix === right.suffix) {
|
|
3927
|
+
return 0;
|
|
3923
3928
|
}
|
|
3924
|
-
|
|
3925
|
-
|
|
3929
|
+
return (left.suffix ?? "").localeCompare(right.suffix ?? "");
|
|
3930
|
+
}
|
|
3931
|
+
function suffixRank(suffix) {
|
|
3932
|
+
if (suffix === void 0) {
|
|
3933
|
+
return 0;
|
|
3926
3934
|
}
|
|
3927
|
-
return
|
|
3935
|
+
return /^post/i.test(suffix) ? 1 : -1;
|
|
3928
3936
|
}
|
|
3929
3937
|
function parseVersion(version) {
|
|
3930
|
-
const normalized = version.trim().replace(/^v/, "");
|
|
3931
|
-
const
|
|
3932
|
-
const
|
|
3938
|
+
const normalized = version.trim().replace(/^v/, "").split("+", 1)[0];
|
|
3939
|
+
const core = normalized.match(/^\d+(?:\.\d+){0,2}/)?.[0] ?? "";
|
|
3940
|
+
const rawSuffix = normalized.slice(core.length).replace(/^[-_.]/, "");
|
|
3941
|
+
const suffix = core.length > 0 && rawSuffix.length > 0 ? rawSuffix : void 0;
|
|
3942
|
+
const parts = core.split(".");
|
|
3933
3943
|
return {
|
|
3934
|
-
numbers: [
|
|
3935
|
-
|
|
3944
|
+
numbers: [
|
|
3945
|
+
safeVersionNumber(Number.parseInt(parts[0] ?? "", 10)),
|
|
3946
|
+
safeVersionNumber(Number.parseInt(parts[1] ?? "", 10)),
|
|
3947
|
+
safeVersionNumber(Number.parseInt(parts[2] ?? "", 10))
|
|
3948
|
+
],
|
|
3949
|
+
suffix
|
|
3936
3950
|
};
|
|
3937
3951
|
}
|
|
3938
3952
|
function safeVersionNumber(value) {
|
|
@@ -7537,7 +7551,7 @@ function openVikingServerArgs(config) {
|
|
|
7537
7551
|
return ["--config", (0, import_node_path4.join)(config.agentContextHome, "ov.conf"), "--host", config.host, "--port", String(config.port)];
|
|
7538
7552
|
}
|
|
7539
7553
|
function withIdentity(config, args) {
|
|
7540
|
-
return [...args, "--account", config.account, "--user", config.user
|
|
7554
|
+
return [...args, "--account", config.account, "--user", config.user];
|
|
7541
7555
|
}
|
|
7542
7556
|
function renderTemplate(template, config, extras = {}) {
|
|
7543
7557
|
let rendered = template.replaceAll("{{THREADNOTE_HOME}}", config.agentContextHome).replaceAll("{{OPENVIKING_ACCOUNT}}", config.account).replaceAll("{{OPENVIKING_AGENT_ID}}", config.agentId).replaceAll("{{OPENVIKING_HOST}}", config.host).replaceAll("{{OPENVIKING_PORT}}", String(config.port)).replaceAll("{{OPENVIKING_USER}}", config.user);
|
|
@@ -7591,7 +7605,11 @@ async function repairStaleRecallIndex(config, ov, options = {}) {
|
|
|
7591
7605
|
const result = await runCommand(
|
|
7592
7606
|
ov,
|
|
7593
7607
|
withIdentity(config, ["reindex", target.uri, "--mode", "semantic_and_vectors", "--wait", "true"]),
|
|
7594
|
-
|
|
7608
|
+
// Bound the wait: `ov reindex` has no --timeout, so a stuck/poisoned
|
|
7609
|
+
// semantic queue would block each target for the full 10-min command
|
|
7610
|
+
// timeout. A timed-out target counts as a failure and trips the
|
|
7611
|
+
// consecutive-failure stop instead of hanging the whole repair.
|
|
7612
|
+
{ allowFailure: true, timeoutMs: reindexWaitTimeoutMs() }
|
|
7595
7613
|
);
|
|
7596
7614
|
if (result.exitCode === 0) {
|
|
7597
7615
|
consecutiveFailures = 0;
|
|
@@ -10894,7 +10912,7 @@ async function refreshMemoryIndex(config, ov, uri, options = {}) {
|
|
|
10894
10912
|
const result = await runCommand(
|
|
10895
10913
|
ov,
|
|
10896
10914
|
withIdentity(config, ["reindex", uri, "--mode", "vectors_only", "--wait", "true"]),
|
|
10897
|
-
{ allowFailure: true }
|
|
10915
|
+
{ allowFailure: true, timeoutMs: reindexWaitTimeoutMs() }
|
|
10898
10916
|
);
|
|
10899
10917
|
if (result.exitCode === 0) {
|
|
10900
10918
|
if (options.quiet !== true && result.stdout.trim()) {
|
|
@@ -12254,7 +12272,7 @@ async function checkForThreadnoteUpdate(args) {
|
|
|
12254
12272
|
}
|
|
12255
12273
|
const cached = await readUpdateCache(args.cachePath);
|
|
12256
12274
|
if (cached && isCacheFresh(cached)) {
|
|
12257
|
-
return
|
|
12275
|
+
return toUpdateCheckResult(args.currentVersion, cached.latestVersion);
|
|
12258
12276
|
}
|
|
12259
12277
|
const fresh = await fetchLatestVersion();
|
|
12260
12278
|
if (fresh) {
|
|
@@ -12263,10 +12281,10 @@ async function checkForThreadnoteUpdate(args) {
|
|
|
12263
12281
|
latestVersion: fresh,
|
|
12264
12282
|
version: 1
|
|
12265
12283
|
});
|
|
12266
|
-
return
|
|
12284
|
+
return toUpdateCheckResult(args.currentVersion, fresh);
|
|
12267
12285
|
}
|
|
12268
12286
|
if (cached) {
|
|
12269
|
-
return
|
|
12287
|
+
return toUpdateCheckResult(args.currentVersion, cached.latestVersion);
|
|
12270
12288
|
}
|
|
12271
12289
|
return void 0;
|
|
12272
12290
|
}
|
|
@@ -12284,27 +12302,13 @@ function spawnDetachedAutoUpdate() {
|
|
|
12284
12302
|
} catch {
|
|
12285
12303
|
}
|
|
12286
12304
|
}
|
|
12287
|
-
function
|
|
12305
|
+
function toUpdateCheckResult(currentVersion, latestVersion) {
|
|
12288
12306
|
return {
|
|
12289
12307
|
currentVersion,
|
|
12290
12308
|
latestVersion,
|
|
12291
|
-
outdated:
|
|
12309
|
+
outdated: compareVersions(latestVersion, currentVersion) > 0
|
|
12292
12310
|
};
|
|
12293
12311
|
}
|
|
12294
|
-
function isNewerVersion(candidate, baseline) {
|
|
12295
|
-
const candidateParts = parseVersion2(candidate);
|
|
12296
|
-
const baselineParts = parseVersion2(baseline);
|
|
12297
|
-
for (let index = 0; index < 3; index += 1) {
|
|
12298
|
-
if (candidateParts[index] !== baselineParts[index]) {
|
|
12299
|
-
return candidateParts[index] > baselineParts[index];
|
|
12300
|
-
}
|
|
12301
|
-
}
|
|
12302
|
-
return false;
|
|
12303
|
-
}
|
|
12304
|
-
function parseVersion2(value) {
|
|
12305
|
-
const parts = value.split(".").map((part) => parseInt(part, 10));
|
|
12306
|
-
return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
|
|
12307
|
-
}
|
|
12308
12312
|
function isCacheFresh(cache) {
|
|
12309
12313
|
const checkedAt = new Date(cache.checkedAt).getTime();
|
|
12310
12314
|
return Number.isFinite(checkedAt) && Date.now() - checkedAt < CACHE_TTL_MS;
|
|
@@ -12584,10 +12588,24 @@ async function emitUpdateBannerIfOutdated(config) {
|
|
|
12584
12588
|
// src/seeding.ts
|
|
12585
12589
|
var import_promises9 = require("node:fs/promises");
|
|
12586
12590
|
var import_node_path12 = require("node:path");
|
|
12591
|
+
function parseSeedWatchIntervalMinutes(rawValue) {
|
|
12592
|
+
if (rawValue === void 0) {
|
|
12593
|
+
return void 0;
|
|
12594
|
+
}
|
|
12595
|
+
const minutes = Number.parseInt(rawValue.trim(), 10);
|
|
12596
|
+
return Number.isInteger(minutes) && minutes > 0 ? minutes : void 0;
|
|
12597
|
+
}
|
|
12598
|
+
function seedWatchArgs(params) {
|
|
12599
|
+
if (params.watchIntervalMinutes === void 0 || !params.importedOriginal || params.redactionProne) {
|
|
12600
|
+
return [];
|
|
12601
|
+
}
|
|
12602
|
+
return ["--watch-interval", String(params.watchIntervalMinutes)];
|
|
12603
|
+
}
|
|
12587
12604
|
async function runSeed(config, options) {
|
|
12588
12605
|
const manifest = await readSeedManifest(config.manifestPath);
|
|
12589
12606
|
const ignorePatterns = await loadIgnorePatterns();
|
|
12590
12607
|
const ov = await openVikingCliForMode(options.dryRun === true);
|
|
12608
|
+
const watchIntervalMinutes = parseSeedWatchIntervalMinutes(process.env[SEED_WATCH_INTERVAL_ENV]);
|
|
12591
12609
|
const statePath = (0, import_node_path12.join)(config.agentContextHome, SEED_STATE_FILE);
|
|
12592
12610
|
const state = options.force === true ? { files: {}, version: 1 } : await readSeedState(statePath);
|
|
12593
12611
|
const projects = filterProjects(manifest.projects, options.only);
|
|
@@ -12620,6 +12638,11 @@ async function runSeed(config, options) {
|
|
|
12620
12638
|
candidate.destinationUri,
|
|
12621
12639
|
"--reason",
|
|
12622
12640
|
seedResourceReason(candidate),
|
|
12641
|
+
...seedWatchArgs({
|
|
12642
|
+
watchIntervalMinutes,
|
|
12643
|
+
importedOriginal: importPath === candidate.filePath,
|
|
12644
|
+
redactionProne: shouldRedactPath(candidate.relativePath)
|
|
12645
|
+
}),
|
|
12623
12646
|
"--wait"
|
|
12624
12647
|
]);
|
|
12625
12648
|
await maybeRun(options.dryRun === true, ov, args);
|
|
@@ -13701,6 +13724,8 @@ async function collectDoctorChecks(config, options = {}) {
|
|
|
13701
13724
|
checks.push(await commandCheck("python3", ["--version"]));
|
|
13702
13725
|
checks.push(await openVikingServerCheck());
|
|
13703
13726
|
checks.push(await openVikingCliCheck());
|
|
13727
|
+
checks.push(await openVikingVersionCheck(config));
|
|
13728
|
+
checks.push(await recallShapeCheck(config));
|
|
13704
13729
|
checks.push(await localEmbeddingCheck());
|
|
13705
13730
|
checks.push(await pythonSystemCertificatesCheck());
|
|
13706
13731
|
checks.push(await firstCommandCheck("python installer", ["pipx", "uv", "pip3"], ["--version"]));
|
|
@@ -13962,21 +13987,13 @@ async function configureOpenVikingCliLanguage(config, dryRun) {
|
|
|
13962
13987
|
if (!ov) {
|
|
13963
13988
|
return;
|
|
13964
13989
|
}
|
|
13965
|
-
const installedVersion = dryRun ? void 0 : await
|
|
13990
|
+
const installedVersion = dryRun ? void 0 : await readOpenVikingCliVersion(ov);
|
|
13966
13991
|
const effectiveVersion = installedVersion ?? config.openVikingVersion;
|
|
13967
13992
|
if (compareVersions(effectiveVersion, "0.3.23") < 0) {
|
|
13968
13993
|
return;
|
|
13969
13994
|
}
|
|
13970
13995
|
await maybeRun(dryRun, ov, ["language", "en"], { allowFailure: true });
|
|
13971
13996
|
}
|
|
13972
|
-
async function readOpenVikingCliVersion2(ov) {
|
|
13973
|
-
const result = await runCommand(ov, ["version"], { allowFailure: true });
|
|
13974
|
-
if (result.exitCode !== 0) {
|
|
13975
|
-
return void 0;
|
|
13976
|
-
}
|
|
13977
|
-
const match = result.stdout.match(/^\s*CLI:\s*(\S+)/m);
|
|
13978
|
-
return match ? match[1] : void 0;
|
|
13979
|
-
}
|
|
13980
13997
|
async function findOpenVikingServer() {
|
|
13981
13998
|
const onPath = await findExecutable([OPENVIKING_SERVER_COMMAND]);
|
|
13982
13999
|
if (onPath) {
|
|
@@ -14213,6 +14230,59 @@ async function openVikingCliCheck() {
|
|
|
14213
14230
|
detail: result.exitCode === 0 ? detail : firstLine(result.stderr || result.stdout) || detail
|
|
14214
14231
|
};
|
|
14215
14232
|
}
|
|
14233
|
+
async function openVikingVersionCheck(config) {
|
|
14234
|
+
const executable = await findOpenVikingCli();
|
|
14235
|
+
const pinned = config.openVikingVersion;
|
|
14236
|
+
if (!executable) {
|
|
14237
|
+
return { name: "openviking version", status: "warn", detail: `CLI not found; pinned ${pinned}` };
|
|
14238
|
+
}
|
|
14239
|
+
const installed = await readOpenVikingCliVersion(executable);
|
|
14240
|
+
if (!installed) {
|
|
14241
|
+
return {
|
|
14242
|
+
name: "openviking version",
|
|
14243
|
+
status: "warn",
|
|
14244
|
+
detail: `could not detect via \`${executable} version\`; pinned ${pinned}`
|
|
14245
|
+
};
|
|
14246
|
+
}
|
|
14247
|
+
if (compareVersions(installed, pinned) < 0) {
|
|
14248
|
+
return {
|
|
14249
|
+
name: "openviking version",
|
|
14250
|
+
status: "warn",
|
|
14251
|
+
detail: `installed ${installed} is older than pinned ${pinned}; run \`threadnote repair\` or \`threadnote update\` to upgrade`
|
|
14252
|
+
};
|
|
14253
|
+
}
|
|
14254
|
+
return { name: "openviking version", status: "ok", detail: `${installed} (pinned ${pinned})` };
|
|
14255
|
+
}
|
|
14256
|
+
async function recallShapeCheck(config) {
|
|
14257
|
+
const executable = await findOpenVikingCli();
|
|
14258
|
+
if (!executable) {
|
|
14259
|
+
return { name: "recall shape", status: "warn", detail: "CLI not found" };
|
|
14260
|
+
}
|
|
14261
|
+
const args = withIdentity(config, ["find", "threadnote", "--node-limit", "1", "--output", "json"]);
|
|
14262
|
+
const result = await runCommand(executable, args, { allowFailure: true });
|
|
14263
|
+
if (result.exitCode !== 0) {
|
|
14264
|
+
return { name: "recall shape", status: "warn", detail: "search failed; run threadnote repair" };
|
|
14265
|
+
}
|
|
14266
|
+
let parsed;
|
|
14267
|
+
try {
|
|
14268
|
+
parsed = JSON.parse(result.stdout.trim());
|
|
14269
|
+
} catch {
|
|
14270
|
+
return {
|
|
14271
|
+
name: "recall shape",
|
|
14272
|
+
status: "warn",
|
|
14273
|
+
detail: "search output is not JSON; recall may silently return nothing"
|
|
14274
|
+
};
|
|
14275
|
+
}
|
|
14276
|
+
const buckets = ["memories", "resources", "skills"];
|
|
14277
|
+
if (!isJsonObject(parsed) || !buckets.some((key) => Array.isArray(parsed[key]))) {
|
|
14278
|
+
return {
|
|
14279
|
+
name: "recall shape",
|
|
14280
|
+
status: "warn",
|
|
14281
|
+
detail: `search JSON missing ${buckets.join("/")} buckets; recall parsing is out of sync with this OpenViking`
|
|
14282
|
+
};
|
|
14283
|
+
}
|
|
14284
|
+
return { name: "recall shape", status: "ok", detail: "memories/resources/skills buckets present" };
|
|
14285
|
+
}
|
|
14216
14286
|
async function localEmbeddingCheck() {
|
|
14217
14287
|
const serverPath = await findOpenVikingServer();
|
|
14218
14288
|
if (!serverPath) {
|
|
@@ -193,8 +193,8 @@ Use these only when MCP tools are not available:
|
|
|
193
193
|
threadnote start
|
|
194
194
|
threadnote recall --query "last handoff for this branch"
|
|
195
195
|
threadnote recall --query "durable feature knowledge for this branch"
|
|
196
|
-
threadnote read viking://
|
|
197
|
-
threadnote list viking://
|
|
196
|
+
threadnote read viking://user/example/memories/.abstract.md
|
|
197
|
+
threadnote list viking://user/example/memories --all --recursive
|
|
198
198
|
threadnote remember --kind durable --project example --topic workflow --text "Durable engineering note..."
|
|
199
199
|
threadnote remember --kind durable --project example --topic active-issue --text "Feature knowledge..."
|
|
200
200
|
threadnote remember --replace viking://user/example/memories/durable/projects/example/workflow.md --text "Updated durable engineering note..."
|
package/docs/index.html
CHANGED
|
@@ -1147,7 +1147,7 @@
|
|
|
1147
1147
|
<span class="out">--user`, which fails on PEP 668 setups.</span>
|
|
1148
1148
|
<span class="out">Install uv now? [Y/n] </span><span class="out-strong">Y</span>
|
|
1149
1149
|
<span class="out">Installing uv via Homebrew…</span>
|
|
1150
|
-
<span class="out-strong">OK openviking 0.
|
|
1150
|
+
<span class="out-strong">OK openviking 0.4.4 ready · server healthy</span></pre>
|
|
1151
1151
|
</div>
|
|
1152
1152
|
<p class="muted" style="margin-top: 0.75rem; font-size: 0.9rem">
|
|
1153
1153
|
Or manually: <code>npm install -g threadnote && threadnote install</code>. On a fresh macOS / modern
|
|
@@ -1651,7 +1651,7 @@ threadnote doctor --dry-run</code></pre>
|
|
|
1651
1651
|
</div>
|
|
1652
1652
|
|
|
1653
1653
|
<p class="colophon">
|
|
1654
|
-
threadnote · AGPL-3.0-or-later · built on OpenViking 0.
|
|
1654
|
+
threadnote · AGPL-3.0-or-later · built on OpenViking 0.4.4 · use <span class="kbd">↑</span>
|
|
1655
1655
|
<span class="kbd">↓</span> / <span class="kbd">j</span> <span class="kbd">k</span> to navigate
|
|
1656
1656
|
</p>
|
|
1657
1657
|
</div>
|
package/docs/migration.md
CHANGED
|
@@ -108,6 +108,12 @@ paths.
|
|
|
108
108
|
threadnote seed
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
Seeded docs refresh on the next `threadnote seed`/`repair`, which re-runs the
|
|
112
|
+
secret scan. To let OpenViking auto-refresh them between runs, opt in with
|
|
113
|
+
`THREADNOTE_SEED_WATCH_INTERVAL=<minutes> threadnote seed`. Watches attach
|
|
114
|
+
only to original, non-redaction-prone files, since an OpenViking-managed
|
|
115
|
+
refresh re-ingests the file without Threadnote's per-import secret scan.
|
|
116
|
+
|
|
111
117
|
7. Inspect and seed shared skills:
|
|
112
118
|
|
|
113
119
|
```bash
|