threadnote 1.3.3 → 1.4.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.
@@ -39359,7 +39359,7 @@ function registerTools(server, config2) {
39359
39359
  }
39360
39360
  },
39361
39361
  async ({ recursive, uri }) => {
39362
- const checkedUri = requiredVikingUri(uri, "forget", "viking://agent/threadnote/memories/example.md");
39362
+ const checkedUri = requiredVikingUri(uri, "forget", "viking://user/you/memories/example.md");
39363
39363
  if (!checkedUri.ok) {
39364
39364
  return checkedUri.error;
39365
39365
  }
@@ -39603,8 +39603,6 @@ function registerOpenVikingParityTools(server, config2) {
39603
39603
  limit: external_exports.number().int().positive().max(1e3).optional().describe("Maximum result count"),
39604
39604
  minScore: external_exports.number().min(0).max(1).optional().describe("Minimum score threshold"),
39605
39605
  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
39606
  query: external_exports.string().optional().describe("Required search query"),
39609
39607
  sessionId: external_exports.string().optional().describe("Optional native session id"),
39610
39608
  session_id: external_exports.string().optional().describe("Optional native session id"),
@@ -40081,7 +40079,7 @@ async function collectExactMemoryMatches(config2, query, includeArchived, projec
40081
40079
  return collectExactMatches(terms, scopes, async (term, scope) => {
40082
40080
  const result = await runCommand(
40083
40081
  ov,
40084
- withIdentity2(config2, ["grep", term, "--uri", scope, "--node-limit", "5", "--output", "json"]),
40082
+ withIdentity(config2, ["grep", term, "--uri", scope, "--node-limit", "5", "--output", "json"]),
40085
40083
  { allowFailure: true }
40086
40084
  );
40087
40085
  return result.exitCode === 0 ? result.stdout : void 0;
@@ -40099,7 +40097,7 @@ function registerReadTool(server, config2, name, description) {
40099
40097
  }
40100
40098
  },
40101
40099
  async ({ uri, uris }) => {
40102
- const checkedUris = requiredVikingUriList(uris ?? uri, name, "viking://agent/threadnote/memories/.abstract.md");
40100
+ const checkedUris = requiredVikingUriList(uris ?? uri, name, "viking://user/you/memories/.abstract.md");
40103
40101
  if (!checkedUris.ok) {
40104
40102
  return checkedUris.error;
40105
40103
  }
@@ -40526,11 +40524,11 @@ async function writeSharedMemoryReplacement(config2, ov, params, targetUri) {
40526
40524
  return { content: [{ type: "text", text: messages.join("\n") }] };
40527
40525
  }
40528
40526
  async function vikingResourceExists2(ov, config2, uri) {
40529
- const stat2 = await runCommand(ov, withIdentity2(config2, ["stat", uri]), { allowFailure: true });
40527
+ const stat2 = await runCommand(ov, withIdentity(config2, ["stat", uri]), { allowFailure: true });
40530
40528
  return stat2.exitCode === 0;
40531
40529
  }
40532
40530
  async function removeVikingResourceWithRetry(ov, config2, uri, recursive = false) {
40533
- const args = withIdentity2(config2, ["rm", uri, ...recursive ? ["--recursive"] : []]);
40531
+ const args = withIdentity(config2, ["rm", uri, ...recursive ? ["--recursive"] : []]);
40534
40532
  for (let attempt = 0; attempt < 4; attempt += 1) {
40535
40533
  const result = await runCommand(ov, args, { allowFailure: true });
40536
40534
  if (result.exitCode === 0) {
@@ -40570,13 +40568,13 @@ ${stdout2}`.toLowerCase();
40570
40568
  }
40571
40569
  async function ensureMemoryDirectory(ov, config2, directoryUri) {
40572
40570
  for (const uri of vikingDirectoryChain(directoryUri)) {
40573
- const statResult = await runCommand(ov, withIdentity2(config2, ["stat", uri]), { allowFailure: true });
40571
+ const statResult = await runCommand(ov, withIdentity(config2, ["stat", uri]), { allowFailure: true });
40574
40572
  if (statResult.exitCode === 0) {
40575
40573
  continue;
40576
40574
  }
40577
40575
  await runCommand(
40578
40576
  ov,
40579
- withIdentity2(config2, ["mkdir", uri, "--description", "Threadnote lifecycle-aware local memories."])
40577
+ withIdentity(config2, ["mkdir", uri, "--description", "Threadnote lifecycle-aware local memories."])
40580
40578
  );
40581
40579
  }
40582
40580
  }
@@ -40793,7 +40791,7 @@ ${text}`);
40793
40791
  async function runOpenVikingCliReadTool(config2, uri) {
40794
40792
  try {
40795
40793
  const ov = await requiredOpenVikingCli2();
40796
- const result = await runCommand(ov, withIdentity2(config2, ["read", uri]));
40794
+ const result = await runCommand(ov, withIdentity(config2, ["read", uri]));
40797
40795
  const text = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
40798
40796
  return { content: [{ type: "text", text: text || "OK" }] };
40799
40797
  } catch (err) {
@@ -40803,7 +40801,7 @@ async function runOpenVikingCliReadTool(config2, uri) {
40803
40801
  async function runOpenVikingTool(config2, args) {
40804
40802
  try {
40805
40803
  const ov = await requiredOpenVikingCli2();
40806
- const result = await runCommand(ov, withIdentity2(config2, args));
40804
+ const result = await runCommand(ov, withIdentity(config2, args));
40807
40805
  const text = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
40808
40806
  return { content: [{ type: "text", text: text || "OK" }] };
40809
40807
  } catch (err) {
@@ -41048,9 +41046,6 @@ function shareArtifactToolHeader(team, syncedTeams, warnings) {
41048
41046
  }
41049
41047
  return lines;
41050
41048
  }
41051
- function withIdentity2(config2, args) {
41052
- return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
41053
- }
41054
41049
  async function requiredOpenVikingCli2() {
41055
41050
  const command2 = await findOpenVikingCli();
41056
41051
  if (!command2) {
@@ -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.3.24";
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" },
@@ -3915,24 +3916,34 @@ function compareVersions(a, b) {
3915
3916
  return difference;
3916
3917
  }
3917
3918
  }
3918
- if (left.prerelease === right.prerelease) {
3919
- return 0;
3919
+ const rankDelta = suffixRank(left.suffix) - suffixRank(right.suffix);
3920
+ if (rankDelta !== 0) {
3921
+ return rankDelta;
3920
3922
  }
3921
- if (left.prerelease === void 0) {
3922
- return 1;
3923
+ if (left.suffix === right.suffix) {
3924
+ return 0;
3923
3925
  }
3924
- if (right.prerelease === void 0) {
3925
- return -1;
3926
+ return (left.suffix ?? "").localeCompare(right.suffix ?? "");
3927
+ }
3928
+ function suffixRank(suffix) {
3929
+ if (suffix === void 0) {
3930
+ return 0;
3926
3931
  }
3927
- return left.prerelease.localeCompare(right.prerelease);
3932
+ return /^post/i.test(suffix) ? 1 : -1;
3928
3933
  }
3929
3934
  function parseVersion(version) {
3930
- const normalized = version.trim().replace(/^v/, "");
3931
- const [core, prerelease] = normalized.split("-", 2);
3932
- const parts = core.split(".").map((part) => Number(part));
3935
+ const normalized = version.trim().replace(/^v/, "").split("+", 1)[0];
3936
+ const core = normalized.match(/^\d+(?:\.\d+){0,2}/)?.[0] ?? "";
3937
+ const rawSuffix = normalized.slice(core.length).replace(/^[-_.]/, "");
3938
+ const suffix = core.length > 0 && rawSuffix.length > 0 ? rawSuffix : void 0;
3939
+ const parts = core.split(".");
3933
3940
  return {
3934
- numbers: [safeVersionNumber(parts[0]), safeVersionNumber(parts[1]), safeVersionNumber(parts[2])],
3935
- prerelease
3941
+ numbers: [
3942
+ safeVersionNumber(Number.parseInt(parts[0] ?? "", 10)),
3943
+ safeVersionNumber(Number.parseInt(parts[1] ?? "", 10)),
3944
+ safeVersionNumber(Number.parseInt(parts[2] ?? "", 10))
3945
+ ],
3946
+ suffix
3936
3947
  };
3937
3948
  }
3938
3949
  function safeVersionNumber(value) {
@@ -12254,7 +12265,7 @@ async function checkForThreadnoteUpdate(args) {
12254
12265
  }
12255
12266
  const cached = await readUpdateCache(args.cachePath);
12256
12267
  if (cached && isCacheFresh(cached)) {
12257
- return compareVersions2(args.currentVersion, cached.latestVersion);
12268
+ return toUpdateCheckResult(args.currentVersion, cached.latestVersion);
12258
12269
  }
12259
12270
  const fresh = await fetchLatestVersion();
12260
12271
  if (fresh) {
@@ -12263,10 +12274,10 @@ async function checkForThreadnoteUpdate(args) {
12263
12274
  latestVersion: fresh,
12264
12275
  version: 1
12265
12276
  });
12266
- return compareVersions2(args.currentVersion, fresh);
12277
+ return toUpdateCheckResult(args.currentVersion, fresh);
12267
12278
  }
12268
12279
  if (cached) {
12269
- return compareVersions2(args.currentVersion, cached.latestVersion);
12280
+ return toUpdateCheckResult(args.currentVersion, cached.latestVersion);
12270
12281
  }
12271
12282
  return void 0;
12272
12283
  }
@@ -12284,27 +12295,13 @@ function spawnDetachedAutoUpdate() {
12284
12295
  } catch {
12285
12296
  }
12286
12297
  }
12287
- function compareVersions2(currentVersion, latestVersion) {
12298
+ function toUpdateCheckResult(currentVersion, latestVersion) {
12288
12299
  return {
12289
12300
  currentVersion,
12290
12301
  latestVersion,
12291
- outdated: isNewerVersion(latestVersion, currentVersion)
12302
+ outdated: compareVersions(latestVersion, currentVersion) > 0
12292
12303
  };
12293
12304
  }
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
12305
  function isCacheFresh(cache) {
12309
12306
  const checkedAt = new Date(cache.checkedAt).getTime();
12310
12307
  return Number.isFinite(checkedAt) && Date.now() - checkedAt < CACHE_TTL_MS;
@@ -12584,10 +12581,24 @@ async function emitUpdateBannerIfOutdated(config) {
12584
12581
  // src/seeding.ts
12585
12582
  var import_promises9 = require("node:fs/promises");
12586
12583
  var import_node_path12 = require("node:path");
12584
+ function parseSeedWatchIntervalMinutes(rawValue) {
12585
+ if (rawValue === void 0) {
12586
+ return void 0;
12587
+ }
12588
+ const minutes = Number.parseInt(rawValue.trim(), 10);
12589
+ return Number.isInteger(minutes) && minutes > 0 ? minutes : void 0;
12590
+ }
12591
+ function seedWatchArgs(params) {
12592
+ if (params.watchIntervalMinutes === void 0 || !params.importedOriginal || params.redactionProne) {
12593
+ return [];
12594
+ }
12595
+ return ["--watch-interval", String(params.watchIntervalMinutes)];
12596
+ }
12587
12597
  async function runSeed(config, options) {
12588
12598
  const manifest = await readSeedManifest(config.manifestPath);
12589
12599
  const ignorePatterns = await loadIgnorePatterns();
12590
12600
  const ov = await openVikingCliForMode(options.dryRun === true);
12601
+ const watchIntervalMinutes = parseSeedWatchIntervalMinutes(process.env[SEED_WATCH_INTERVAL_ENV]);
12591
12602
  const statePath = (0, import_node_path12.join)(config.agentContextHome, SEED_STATE_FILE);
12592
12603
  const state = options.force === true ? { files: {}, version: 1 } : await readSeedState(statePath);
12593
12604
  const projects = filterProjects(manifest.projects, options.only);
@@ -12620,6 +12631,11 @@ async function runSeed(config, options) {
12620
12631
  candidate.destinationUri,
12621
12632
  "--reason",
12622
12633
  seedResourceReason(candidate),
12634
+ ...seedWatchArgs({
12635
+ watchIntervalMinutes,
12636
+ importedOriginal: importPath === candidate.filePath,
12637
+ redactionProne: shouldRedactPath(candidate.relativePath)
12638
+ }),
12623
12639
  "--wait"
12624
12640
  ]);
12625
12641
  await maybeRun(options.dryRun === true, ov, args);
@@ -13701,6 +13717,8 @@ async function collectDoctorChecks(config, options = {}) {
13701
13717
  checks.push(await commandCheck("python3", ["--version"]));
13702
13718
  checks.push(await openVikingServerCheck());
13703
13719
  checks.push(await openVikingCliCheck());
13720
+ checks.push(await openVikingVersionCheck(config));
13721
+ checks.push(await recallShapeCheck(config));
13704
13722
  checks.push(await localEmbeddingCheck());
13705
13723
  checks.push(await pythonSystemCertificatesCheck());
13706
13724
  checks.push(await firstCommandCheck("python installer", ["pipx", "uv", "pip3"], ["--version"]));
@@ -13962,21 +13980,13 @@ async function configureOpenVikingCliLanguage(config, dryRun) {
13962
13980
  if (!ov) {
13963
13981
  return;
13964
13982
  }
13965
- const installedVersion = dryRun ? void 0 : await readOpenVikingCliVersion2(ov);
13983
+ const installedVersion = dryRun ? void 0 : await readOpenVikingCliVersion(ov);
13966
13984
  const effectiveVersion = installedVersion ?? config.openVikingVersion;
13967
13985
  if (compareVersions(effectiveVersion, "0.3.23") < 0) {
13968
13986
  return;
13969
13987
  }
13970
13988
  await maybeRun(dryRun, ov, ["language", "en"], { allowFailure: true });
13971
13989
  }
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
13990
  async function findOpenVikingServer() {
13981
13991
  const onPath = await findExecutable([OPENVIKING_SERVER_COMMAND]);
13982
13992
  if (onPath) {
@@ -14213,6 +14223,59 @@ async function openVikingCliCheck() {
14213
14223
  detail: result.exitCode === 0 ? detail : firstLine(result.stderr || result.stdout) || detail
14214
14224
  };
14215
14225
  }
14226
+ async function openVikingVersionCheck(config) {
14227
+ const executable = await findOpenVikingCli();
14228
+ const pinned = config.openVikingVersion;
14229
+ if (!executable) {
14230
+ return { name: "openviking version", status: "warn", detail: `CLI not found; pinned ${pinned}` };
14231
+ }
14232
+ const installed = await readOpenVikingCliVersion(executable);
14233
+ if (!installed) {
14234
+ return {
14235
+ name: "openviking version",
14236
+ status: "warn",
14237
+ detail: `could not detect via \`${executable} version\`; pinned ${pinned}`
14238
+ };
14239
+ }
14240
+ if (compareVersions(installed, pinned) < 0) {
14241
+ return {
14242
+ name: "openviking version",
14243
+ status: "warn",
14244
+ detail: `installed ${installed} is older than pinned ${pinned}; run \`threadnote repair\` or \`threadnote update\` to upgrade`
14245
+ };
14246
+ }
14247
+ return { name: "openviking version", status: "ok", detail: `${installed} (pinned ${pinned})` };
14248
+ }
14249
+ async function recallShapeCheck(config) {
14250
+ const executable = await findOpenVikingCli();
14251
+ if (!executable) {
14252
+ return { name: "recall shape", status: "warn", detail: "CLI not found" };
14253
+ }
14254
+ const args = withIdentity(config, ["find", "threadnote", "--node-limit", "1", "--output", "json"]);
14255
+ const result = await runCommand(executable, args, { allowFailure: true });
14256
+ if (result.exitCode !== 0) {
14257
+ return { name: "recall shape", status: "warn", detail: "search failed; run threadnote repair" };
14258
+ }
14259
+ let parsed;
14260
+ try {
14261
+ parsed = JSON.parse(result.stdout.trim());
14262
+ } catch {
14263
+ return {
14264
+ name: "recall shape",
14265
+ status: "warn",
14266
+ detail: "search output is not JSON; recall may silently return nothing"
14267
+ };
14268
+ }
14269
+ const buckets = ["memories", "resources", "skills"];
14270
+ if (!isJsonObject(parsed) || !buckets.some((key) => Array.isArray(parsed[key]))) {
14271
+ return {
14272
+ name: "recall shape",
14273
+ status: "warn",
14274
+ detail: `search JSON missing ${buckets.join("/")} buckets; recall parsing is out of sync with this OpenViking`
14275
+ };
14276
+ }
14277
+ return { name: "recall shape", status: "ok", detail: "memories/resources/skills buckets present" };
14278
+ }
14216
14279
  async function localEmbeddingCheck() {
14217
14280
  const serverPath = await findOpenVikingServer();
14218
14281
  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://agent/threadnote/memories/.abstract.md
197
- threadnote list viking://agent/threadnote/memories --all --recursive
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.3.24 ready · server healthy</span></pre>
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.3.24 · use <span class="kbd">↑</span>
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadnote",
3
- "version": "1.3.3",
3
+ "version": "1.4.0",
4
4
  "type": "commonjs",
5
5
  "main": "dist/threadnote.cjs",
6
6
  "description": "Shared local context and handoffs for development agents",