threadnote 1.4.1 → 1.4.2

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.
@@ -35715,6 +35715,57 @@ async function sleep(ms) {
35715
35715
  setTimeout(resolvePromise, ms);
35716
35716
  });
35717
35717
  }
35718
+ function compareVersions(a, b) {
35719
+ const left = parseVersion(a);
35720
+ const right = parseVersion(b);
35721
+ for (let index = 0; index < 3; index += 1) {
35722
+ const difference = left.numbers[index] - right.numbers[index];
35723
+ if (difference !== 0) {
35724
+ return difference;
35725
+ }
35726
+ }
35727
+ const rankDelta = suffixRank(left.suffix) - suffixRank(right.suffix);
35728
+ if (rankDelta !== 0) {
35729
+ return rankDelta;
35730
+ }
35731
+ if (left.suffix === right.suffix) {
35732
+ return 0;
35733
+ }
35734
+ return (left.suffix ?? "").localeCompare(right.suffix ?? "");
35735
+ }
35736
+ function suffixRank(suffix) {
35737
+ if (suffix === void 0) {
35738
+ return 0;
35739
+ }
35740
+ return /^post/i.test(suffix) ? 1 : -1;
35741
+ }
35742
+ function parseVersion(version2) {
35743
+ const normalized = version2.trim().replace(/^v/, "").split("+", 1)[0];
35744
+ const core = normalized.match(/^\d+(?:\.\d+){0,2}/)?.[0] ?? "";
35745
+ const rawSuffix = normalized.slice(core.length).replace(/^[-_.]/, "");
35746
+ const suffix = core.length > 0 && rawSuffix.length > 0 ? rawSuffix : void 0;
35747
+ const parts = core.split(".");
35748
+ return {
35749
+ numbers: [
35750
+ safeVersionNumber(Number.parseInt(parts[0] ?? "", 10)),
35751
+ safeVersionNumber(Number.parseInt(parts[1] ?? "", 10)),
35752
+ safeVersionNumber(Number.parseInt(parts[2] ?? "", 10))
35753
+ ],
35754
+ suffix
35755
+ };
35756
+ }
35757
+ function safeVersionNumber(value) {
35758
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : 0;
35759
+ }
35760
+ function formatStaleVersionNotice(runningVersion, diskVersion) {
35761
+ if (runningVersion === void 0 || diskVersion === void 0) {
35762
+ return void 0;
35763
+ }
35764
+ if (compareVersions(diskVersion, runningVersion) <= 0) {
35765
+ return void 0;
35766
+ }
35767
+ return `threadnote ${diskVersion} is installed but this MCP server is still running ${runningVersion}. Reconnect the threadnote MCP server (e.g. /mcp) to load the update.`;
35768
+ }
35718
35769
  async function ensureDirectory(path, dryRun) {
35719
35770
  if (dryRun) {
35720
35771
  console.log(`Would create directory: ${path}`);
@@ -36218,6 +36269,20 @@ function shellQuote(value) {
36218
36269
  }
36219
36270
  return `'${value.replaceAll("'", `'"'"'`)}'`;
36220
36271
  }
36272
+ function toolRoot() {
36273
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(__dirname, "package.json"))) {
36274
+ return __dirname;
36275
+ }
36276
+ return (0, import_node_path.resolve)(__dirname, "..");
36277
+ }
36278
+ async function currentPackageVersion() {
36279
+ const rawPackage = await (0, import_promises.readFile)((0, import_node_path.join)(toolRoot(), "package.json"), "utf8");
36280
+ const parsed = JSON.parse(rawPackage);
36281
+ if (!isJsonObject(parsed) || typeof parsed.version !== "string") {
36282
+ throw new Error("Could not read current threadnote package version.");
36283
+ }
36284
+ return parsed.version;
36285
+ }
36221
36286
  function errorMessage(err) {
36222
36287
  return err instanceof Error ? err.message : String(err);
36223
36288
  }
@@ -39267,8 +39332,36 @@ function formatPlanSection(title, lines) {
39267
39332
  }
39268
39333
 
39269
39334
  // src/mcp_server.ts
39335
+ var mcpStartupVersion;
39336
+ var staleNoticeCache;
39337
+ var STALE_NOTICE_TTL_MS = 6e4;
39338
+ async function staleVersionNotice() {
39339
+ if (mcpStartupVersion === void 0) {
39340
+ return void 0;
39341
+ }
39342
+ const nowMs = Date.now();
39343
+ if (staleNoticeCache && nowMs - staleNoticeCache.checkedAtMs < STALE_NOTICE_TTL_MS) {
39344
+ return staleNoticeCache.notice;
39345
+ }
39346
+ let notice;
39347
+ try {
39348
+ notice = formatStaleVersionNotice(mcpStartupVersion, await currentPackageVersion());
39349
+ } catch {
39350
+ notice = void 0;
39351
+ }
39352
+ staleNoticeCache = { checkedAtMs: nowMs, notice };
39353
+ return notice;
39354
+ }
39355
+ async function withStaleVersionNotice(result) {
39356
+ const notice = await staleVersionNotice();
39357
+ if (notice === void 0) {
39358
+ return result;
39359
+ }
39360
+ return { ...result, content: [...result.content ?? [], { type: "text", text: `\u26A0 ${notice}` }] };
39361
+ }
39270
39362
  async function main() {
39271
39363
  const config2 = getRuntimeConfig();
39364
+ mcpStartupVersion = await currentPackageVersion().catch(() => void 0);
39272
39365
  const server = new McpServer(
39273
39366
  { name: "threadnote-local-adapter", version: "0.2.0" },
39274
39367
  {
@@ -39466,7 +39559,7 @@ function registerTools(server, config2) {
39466
39559
  description: "Check OpenViking server health through the CLI.",
39467
39560
  inputSchema: {}
39468
39561
  },
39469
- async () => runOpenVikingMcpTool(config2, "health", {})
39562
+ async () => withStaleVersionNotice(await runOpenVikingMcpTool(config2, "health", {}))
39470
39563
  );
39471
39564
  registerOpenVikingParityTools(server, config2);
39472
39565
  server.registerTool(
@@ -39959,14 +40052,16 @@ function registerSearchTool(server, config2, name, description) {
39959
40052
  if (!checkedUri.ok) {
39960
40053
  return checkedUri.error;
39961
40054
  }
39962
- return runRecallTool(config2, {
39963
- callerCwd,
39964
- query: checkedQuery.value,
39965
- pinnedUri: checkedUri.value,
39966
- nodeLimit,
39967
- includeArchived: includeArchived === true,
39968
- threshold: threshold === void 0 ? void 0 : String(threshold)
39969
- });
40055
+ return withStaleVersionNotice(
40056
+ await runRecallTool(config2, {
40057
+ callerCwd,
40058
+ query: checkedQuery.value,
40059
+ pinnedUri: checkedUri.value,
40060
+ nodeLimit,
40061
+ includeArchived: includeArchived === true,
40062
+ threshold: threshold === void 0 ? void 0 : String(threshold)
40063
+ })
40064
+ );
39970
40065
  }
39971
40066
  );
39972
40067
  }
@@ -40194,11 +40289,13 @@ function registerStoreTool(server, config2, name, description) {
40194
40289
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
40195
40290
  topic: normalizeOptionalMetadata2(topic)
40196
40291
  };
40197
- return writeDurableMemory(config2, {
40198
- bodyText: checkedText.value,
40199
- metadata,
40200
- replaceUri: checkedReplaceUri.value
40201
- });
40292
+ return withStaleVersionNotice(
40293
+ await writeDurableMemory(config2, {
40294
+ bodyText: checkedText.value,
40295
+ metadata,
40296
+ replaceUri: checkedReplaceUri.value
40297
+ })
40298
+ );
40202
40299
  }
40203
40300
  );
40204
40301
  }
@@ -4583,6 +4583,14 @@ function toolRoot() {
4583
4583
  }
4584
4584
  return (0, import_node_path2.resolve)(__dirname, "..");
4585
4585
  }
4586
+ async function currentPackageVersion() {
4587
+ const rawPackage = await (0, import_promises.readFile)((0, import_node_path2.join)(toolRoot(), "package.json"), "utf8");
4588
+ const parsed = JSON.parse(rawPackage);
4589
+ if (!isJsonObject(parsed) || typeof parsed.version !== "string") {
4590
+ throw new Error("Could not read current threadnote package version.");
4591
+ }
4592
+ return parsed.version;
4593
+ }
4586
4594
  function errorMessage(err) {
4587
4595
  return err instanceof Error ? err.message : String(err);
4588
4596
  }
@@ -13430,14 +13438,6 @@ async function getUpdateInfo(config, options) {
13430
13438
  registry: options.registry
13431
13439
  };
13432
13440
  }
13433
- async function currentPackageVersion() {
13434
- const rawPackage = await (0, import_promises10.readFile)((0, import_node_path13.join)(toolRoot(), "package.json"), "utf8");
13435
- const parsed = JSON.parse(rawPackage);
13436
- if (!isJsonObject(parsed) || typeof parsed.version !== "string") {
13437
- throw new Error("Could not read current threadnote package version.");
13438
- }
13439
- return parsed.version;
13440
- }
13441
13441
  async function fetchLatestVersion2(registry) {
13442
13442
  const url = new URL(`${NPM_PACKAGE_NAME}/latest`, normalizeRegistry(registry));
13443
13443
  const controller = new AbortController();
@@ -14263,22 +14263,20 @@ async function recallShapeCheck(config) {
14263
14263
  if (result.exitCode !== 0) {
14264
14264
  return { name: "recall shape", status: "warn", detail: "search failed; run threadnote repair" };
14265
14265
  }
14266
- let parsed;
14266
+ const start = result.stdout.search(/^\{/m);
14267
+ let envelope;
14267
14268
  try {
14268
- parsed = JSON.parse(result.stdout.trim());
14269
+ const parsed = start >= 0 ? JSON.parse(result.stdout.slice(start)) : void 0;
14270
+ envelope = isJsonObject(parsed) ? parsed.result : void 0;
14269
14271
  } catch {
14270
- return {
14271
- name: "recall shape",
14272
- status: "warn",
14273
- detail: "search output is not JSON; recall may silently return nothing"
14274
- };
14272
+ envelope = void 0;
14275
14273
  }
14276
14274
  const buckets = ["memories", "resources", "skills"];
14277
- if (!isJsonObject(parsed) || !buckets.some((key) => Array.isArray(parsed[key]))) {
14275
+ if (!isJsonObject(envelope) || !buckets.some((key) => Array.isArray(envelope[key]))) {
14278
14276
  return {
14279
14277
  name: "recall shape",
14280
14278
  status: "warn",
14281
- detail: `search JSON missing ${buckets.join("/")} buckets; recall parsing is out of sync with this OpenViking`
14279
+ detail: `search JSON missing the result.{${buckets.join(",")}} buckets recall parsing depends on`
14282
14280
  };
14283
14281
  }
14284
14282
  return { name: "recall shape", status: "ok", detail: "memories/resources/skills buckets present" };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadnote",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "type": "commonjs",
5
5
  "main": "dist/threadnote.cjs",
6
6
  "description": "Shared local context and handoffs for development agents",