threadnote 1.7.0 → 1.7.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.
@@ -25,15 +25,16 @@
25
25
  "title": "Consolidate memories under the git remote project name",
26
26
  "description": [
27
27
  "Threadnote now uses the git remote repository name as the default project identifier instead of the clone folder name.",
28
- "This migration scans personal lifecycle-aware memory folders for the old folder-derived project name from the current workspace.",
29
- "It also renames the matching seed manifest project and workset references so future seeded guidance uses the remote-derived project name.",
28
+ "This migration scans personal lifecycle-aware memory project folders and uses repo_path evidence in memories, plus matching seed manifest paths when present, to resolve git remote names.",
29
+ "It covers every detected personal memory project, not just the workspace where threadnote update was run.",
30
+ "It also renames matching seed manifest projects and workset references so future seeded guidance uses the remote-derived project name.",
30
31
  "Matching memories are copied under the remote-derived project name with updated project metadata, then the old copy is removed.",
31
32
  "If a destination file already exists with different content, Threadnote keeps both by writing the migrated copy with a -from-<old-project> suffix.",
32
- "Shared team memories and unrelated project folders are left untouched."
33
+ "Shared team memories and projects without repo path evidence are left untouched."
33
34
  ],
34
35
  "commandArgs": ["migrate-project-names", "--apply"],
35
36
  "instructions": [
36
- "Post-update project-name migration finished. Future default handoffs and memories for this repo will use the git remote repository name as their project.",
37
+ "Post-update project-name migration finished. Future default handoffs and memories for migrated repos will use the git remote repository name as their project.",
37
38
  "If the migration updated your seed manifest, run threadnote seed --only <new-project-name> to re-ingest seeded resources under the new project URI.",
38
39
  "If Threadnote reported any original files were still being processed, rerun the printed threadnote forget <uri> command later."
39
40
  ],
@@ -37023,7 +37023,12 @@ async function loadPendingReindexes(config2, state) {
37023
37023
  }
37024
37024
  const entry = item;
37025
37025
  if (typeof entry.path === "string" && typeof entry.relativePath === "string" && (entry.status === "added" || entry.status === "removed" || entry.status === "modified")) {
37026
- changes.push({ path: entry.path, relativePath: entry.relativePath, status: entry.status });
37026
+ changes.push({
37027
+ path: entry.path,
37028
+ previousContent: typeof entry.previousContent === "string" ? entry.previousContent : void 0,
37029
+ relativePath: entry.relativePath,
37030
+ status: entry.status
37031
+ });
37027
37032
  }
37028
37033
  }
37029
37034
  if (changes.length > 0) {
@@ -39092,7 +39097,7 @@ async function applyChangesToOpenViking(config2, team, changes, options = {}) {
39092
39097
  const currentContent = await readExistingMemoryContent(config2, ov, uri);
39093
39098
  if (currentContent !== void 0) {
39094
39099
  if (change.status === "added") {
39095
- if (currentContent === content) {
39100
+ if (sharedMemoryContentsEquivalent(currentContent, content)) {
39096
39101
  continue;
39097
39102
  }
39098
39103
  throw new Error(
@@ -39131,12 +39136,18 @@ function assertInboundPreviousContentMatches(change, uri, currentContent) {
39131
39136
  );
39132
39137
  }
39133
39138
  const expectedContent = prepareSharedInboundContent(uri, change.previousContent);
39134
- if (currentContent !== expectedContent) {
39139
+ if (!sharedMemoryContentsEquivalent(currentContent, expectedContent)) {
39135
39140
  throw new Error(
39136
39141
  `Refusing to apply inbound shared change for ${uri}: local OpenViking content differs from the previous shared version. Inspect and resolve the local edit first.`
39137
39142
  );
39138
39143
  }
39139
39144
  }
39145
+ function sharedMemoryContentsEquivalent(left, right) {
39146
+ return normalizeSharedMemoryComparisonContent(left) === normalizeSharedMemoryComparisonContent(right);
39147
+ }
39148
+ function normalizeSharedMemoryComparisonContent(content) {
39149
+ return content.replace(/\r\n?/g, "\n").replace(/\n$/, "");
39150
+ }
39140
39151
  function mergeChanges(...lists) {
39141
39152
  const map2 = /* @__PURE__ */ new Map();
39142
39153
  for (const list of lists) {
@@ -8808,7 +8808,12 @@ async function loadPendingReindexes(config, state) {
8808
8808
  }
8809
8809
  const entry = item;
8810
8810
  if (typeof entry.path === "string" && typeof entry.relativePath === "string" && (entry.status === "added" || entry.status === "removed" || entry.status === "modified")) {
8811
- changes.push({ path: entry.path, relativePath: entry.relativePath, status: entry.status });
8811
+ changes.push({
8812
+ path: entry.path,
8813
+ previousContent: typeof entry.previousContent === "string" ? entry.previousContent : void 0,
8814
+ relativePath: entry.relativePath,
8815
+ status: entry.status
8816
+ });
8812
8817
  }
8813
8818
  }
8814
8819
  if (changes.length > 0) {
@@ -11395,7 +11400,7 @@ async function applyChangesToOpenViking(config, team, changes, options = {}) {
11395
11400
  const currentContent = await readExistingMemoryContent(config, ov, uri);
11396
11401
  if (currentContent !== void 0) {
11397
11402
  if (change.status === "added") {
11398
- if (currentContent === content) {
11403
+ if (sharedMemoryContentsEquivalent(currentContent, content)) {
11399
11404
  continue;
11400
11405
  }
11401
11406
  throw new Error(
@@ -11434,12 +11439,18 @@ function assertInboundPreviousContentMatches(change, uri, currentContent) {
11434
11439
  );
11435
11440
  }
11436
11441
  const expectedContent = prepareSharedInboundContent(uri, change.previousContent);
11437
- if (currentContent !== expectedContent) {
11442
+ if (!sharedMemoryContentsEquivalent(currentContent, expectedContent)) {
11438
11443
  throw new Error(
11439
11444
  `Refusing to apply inbound shared change for ${uri}: local OpenViking content differs from the previous shared version. Inspect and resolve the local edit first.`
11440
11445
  );
11441
11446
  }
11442
11447
  }
11448
+ function sharedMemoryContentsEquivalent(left, right) {
11449
+ return normalizeSharedMemoryComparisonContent(left) === normalizeSharedMemoryComparisonContent(right);
11450
+ }
11451
+ function normalizeSharedMemoryComparisonContent(content) {
11452
+ return content.replace(/\r\n?/g, "\n").replace(/\n$/, "");
11453
+ }
11443
11454
  function mergeChanges(...lists) {
11444
11455
  const map = /* @__PURE__ */ new Map();
11445
11456
  for (const list of lists) {
@@ -11633,21 +11644,30 @@ async function runMigrateLifecycle(config, options) {
11633
11644
  async function runMigrateProjectNames(config, options) {
11634
11645
  const dryRun = options.dryRun === true || options.apply !== true;
11635
11646
  const limit = options.limit ? parsePositiveInteger(options.limit, "project-name migration limit") : void 0;
11636
- const context = await projectNameMigrationContext();
11637
- if (!context) {
11638
- console.log("No git remote project-name change applies in the current workspace.");
11647
+ const contexts = await projectNameMigrationContexts(config);
11648
+ if (contexts.length === 0) {
11649
+ console.log("No git remote project-name changes apply across configured projects.");
11639
11650
  return;
11640
11651
  }
11641
- const candidates = await projectNameMigrationCandidates(config, context, limit);
11642
- const seedManifestCandidate = await hasSeedManifestProjectNameMigrationCandidate(config, context);
11643
- if (candidates.length === 0 && !seedManifestCandidate) {
11644
- console.log(`No project-name migration candidates found for ${context.oldProject} -> ${context.newProject}.`);
11652
+ const plans = [];
11653
+ let remaining = limit;
11654
+ for (const context of contexts) {
11655
+ const candidates2 = remaining === 0 ? [] : await projectNameMigrationCandidates(config, context, remaining);
11656
+ plans.push({ candidates: candidates2, context });
11657
+ if (remaining !== void 0) {
11658
+ remaining = Math.max(0, remaining - candidates2.length);
11659
+ }
11660
+ }
11661
+ const seedManifestMigration = await seedManifestProjectNameMigration(config, contexts);
11662
+ if (!plans.some((plan) => plan.candidates.length > 0) && !seedManifestMigration) {
11663
+ console.log("No project-name migration candidates found across configured projects.");
11645
11664
  return;
11646
11665
  }
11647
- const seedManifestUpdated = await migrateSeedManifestProjectName(config, context, dryRun);
11666
+ const seedManifestUpdated = await migrateSeedManifestProjectNames(config, seedManifestMigration, dryRun);
11648
11667
  let existingCount = 0;
11649
11668
  let migratedCount = 0;
11650
11669
  let skippedCount = 0;
11670
+ const candidates = plans.flatMap((plan) => [...plan.candidates]);
11651
11671
  if (candidates.length > 0) {
11652
11672
  const ov = await openVikingCliForMode(dryRun);
11653
11673
  for (const candidate of candidates) {
@@ -11671,37 +11691,186 @@ async function runMigrateProjectNames(config, options) {
11671
11691
  migratedCount += 1;
11672
11692
  }
11673
11693
  }
11694
+ const activeContexts = projectNameMigrationActiveContexts(plans, seedManifestMigration);
11695
+ const newProjectsToSeed = [...new Set(seedManifestMigration?.newProjects ?? [])];
11674
11696
  console.log(
11675
11697
  [
11676
- `Project-name migration summary: ${migratedCount} memor${migratedCount === 1 ? "y" : "ies"} ${dryRun ? "would be migrated" : "migrated"} from ${context.oldProject} to ${context.newProject}`,
11698
+ projectNameMigrationSummary(migratedCount, dryRun, activeContexts),
11677
11699
  seedManifestUpdated ? `seed manifest ${dryRun ? "would be updated" : "updated"}` : "seed manifest unchanged",
11678
11700
  `${existingCount} duplicate destination(s) reused`,
11679
11701
  `${skippedCount} source(s) still processing`,
11680
11702
  dryRun ? "Run with --apply to perform this migration." : void 0,
11681
- seedManifestUpdated ? `Run threadnote seed --only ${context.newProject} to re-ingest seeded resources under the new project URI.` : void 0
11703
+ ...newProjectsToSeed.map(
11704
+ (project) => `Run threadnote seed --only ${project} to re-ingest seeded resources under the new project URI.`
11705
+ )
11682
11706
  ].filter((part) => part !== void 0).join("; ")
11683
11707
  );
11684
11708
  }
11685
11709
  async function hasProjectNameMigrationCandidates(config) {
11686
- const context = await projectNameMigrationContext();
11687
- return context ? (await projectNameMigrationCandidates(config, context, 1)).length > 0 || await hasSeedManifestProjectNameMigrationCandidate(config, context) : false;
11710
+ const contexts = await projectNameMigrationContexts(config);
11711
+ if (contexts.length === 0) {
11712
+ return false;
11713
+ }
11714
+ for (const context of contexts) {
11715
+ if ((await projectNameMigrationCandidates(config, context, 1)).length > 0) {
11716
+ return true;
11717
+ }
11718
+ }
11719
+ return await seedManifestProjectNameMigration(config, contexts) !== void 0;
11720
+ }
11721
+ async function projectNameMigrationContexts(config) {
11722
+ const evidence = await projectNameMigrationMemoryEvidence(config);
11723
+ const contexts = [];
11724
+ let manifest;
11725
+ try {
11726
+ manifest = await readSeedManifest(config.manifestPath);
11727
+ } catch (_err) {
11728
+ manifest = void 0;
11729
+ }
11730
+ if (manifest) {
11731
+ for (const project of manifest.projects) {
11732
+ const projectEvidence = evidence.get(uriSegment(project.name));
11733
+ if (projectEvidence) {
11734
+ projectEvidence.repoPaths.add(expandPath(project.path));
11735
+ }
11736
+ }
11737
+ }
11738
+ for (const projectEvidence of evidence.values()) {
11739
+ for (const repoPath of projectEvidence.repoPaths) {
11740
+ const context = await projectNameMigrationContextForRepoPath(projectEvidence.oldProject, repoPath);
11741
+ if (context) {
11742
+ contexts.push(context);
11743
+ }
11744
+ }
11745
+ }
11746
+ const currentContext = await currentWorkspaceProjectNameMigrationContext(evidence);
11747
+ if (currentContext) {
11748
+ contexts.push(currentContext);
11749
+ }
11750
+ return dedupeProjectNameMigrationContexts(contexts);
11751
+ }
11752
+ async function projectNameMigrationMemoryEvidence(config) {
11753
+ const evidence = /* @__PURE__ */ new Map();
11754
+ for (const location of projectMemoryLocations()) {
11755
+ const locationRoot = (0, import_node_path8.join)(localUserMemoriesRoot(config), ...location.relativePath);
11756
+ let projectEntries;
11757
+ try {
11758
+ projectEntries = await (0, import_promises6.readdir)(locationRoot, { withFileTypes: true });
11759
+ } catch (_err) {
11760
+ continue;
11761
+ }
11762
+ for (const projectEntry of projectEntries) {
11763
+ if (!projectEntry.isDirectory() || projectEntry.name.startsWith(".")) {
11764
+ continue;
11765
+ }
11766
+ const oldSegment = projectEntry.name;
11767
+ const projectEvidence = ensureProjectNameMigrationEvidence(evidence, oldSegment);
11768
+ const projectDirectory = (0, import_node_path8.join)(locationRoot, projectEntry.name);
11769
+ let memoryEntries;
11770
+ try {
11771
+ memoryEntries = await (0, import_promises6.readdir)(projectDirectory, { withFileTypes: true });
11772
+ } catch (_err) {
11773
+ continue;
11774
+ }
11775
+ for (const memoryEntry of memoryEntries) {
11776
+ if (!memoryEntry.isFile() || memoryEntry.name.startsWith(".") || !memoryEntry.name.endsWith(".md")) {
11777
+ continue;
11778
+ }
11779
+ const content = await readTextIfExists((0, import_node_path8.join)(projectDirectory, memoryEntry.name));
11780
+ if (!content) {
11781
+ continue;
11782
+ }
11783
+ const sourceUri = `viking://user/${uriSegment(config.user)}/memories/${location.uriPath}/${oldSegment}/${memoryEntry.name}`;
11784
+ const record = parseMemoryDocument(sourceUri, content);
11785
+ if (record?.metadata.project && uriSegment(record.metadata.project) === oldSegment) {
11786
+ projectEvidence.oldProject = record.metadata.project;
11787
+ }
11788
+ const repoPath = repoPathEvidenceFromMemory(content);
11789
+ if (repoPath) {
11790
+ projectEvidence.repoPaths.add(repoPath);
11791
+ }
11792
+ }
11793
+ }
11794
+ }
11795
+ return evidence;
11688
11796
  }
11689
- async function projectNameMigrationContext() {
11797
+ function ensureProjectNameMigrationEvidence(evidence, oldSegment) {
11798
+ const existing = evidence.get(oldSegment);
11799
+ if (existing) {
11800
+ return existing;
11801
+ }
11802
+ const created = { oldProject: oldSegment, oldSegment, repoPaths: /* @__PURE__ */ new Set() };
11803
+ evidence.set(oldSegment, created);
11804
+ return created;
11805
+ }
11806
+ function repoPathEvidenceFromMemory(content) {
11807
+ const match = /^repo_path:\s*(.+)$/m.exec(content);
11808
+ if (!match?.[1]) {
11809
+ return void 0;
11810
+ }
11811
+ const cleaned = match[1].trim().replace(/^['"`]+|['"`]+$/g, "").replace(/[.,;]+$/g, "");
11812
+ if (!cleaned.startsWith("/") && !cleaned.startsWith("~/")) {
11813
+ return void 0;
11814
+ }
11815
+ return expandPath(cleaned);
11816
+ }
11817
+ async function projectNameMigrationContextForRepoPath(oldProject, repoPath) {
11818
+ const repoRoot = await gitValue(["rev-parse", "--show-toplevel"], repoPath);
11819
+ if (!repoRoot) {
11820
+ return void 0;
11821
+ }
11822
+ const newProject = await resolveGitRemoteRepoName(repoRoot);
11823
+ if (!newProject) {
11824
+ return void 0;
11825
+ }
11826
+ return projectNameMigrationContextFromParts({
11827
+ newProject,
11828
+ oldProject,
11829
+ repoRoot
11830
+ });
11831
+ }
11832
+ async function currentWorkspaceProjectNameMigrationContext(evidence) {
11690
11833
  const repoRoot = await gitValue(["rev-parse", "--show-toplevel"]);
11691
11834
  if (!repoRoot) {
11692
11835
  return void 0;
11693
11836
  }
11694
- const newProject = await resolveRepoName(repoRoot);
11837
+ const newProject = await resolveGitRemoteRepoName(repoRoot);
11695
11838
  const oldProject = await resolveRepoFolderName(repoRoot);
11696
11839
  if (!newProject || !oldProject) {
11697
11840
  return void 0;
11698
11841
  }
11699
- const newSegment = uriSegment(newProject);
11700
11842
  const oldSegment = uriSegment(oldProject);
11843
+ if (!evidence.has(oldSegment)) {
11844
+ return void 0;
11845
+ }
11846
+ return projectNameMigrationContextFromParts({ newProject, oldProject, repoRoot });
11847
+ }
11848
+ function projectNameMigrationContextFromParts(params) {
11849
+ const newSegment = uriSegment(params.newProject);
11850
+ const oldSegment = uriSegment(params.oldProject);
11701
11851
  if (newSegment === oldSegment) {
11702
11852
  return void 0;
11703
11853
  }
11704
- return { newProject, newSegment, oldProject, oldSegment, repoRoot };
11854
+ return {
11855
+ newProject: params.newProject,
11856
+ newSegment,
11857
+ oldProject: params.oldProject,
11858
+ oldSegment,
11859
+ repoRoot: params.repoRoot
11860
+ };
11861
+ }
11862
+ function dedupeProjectNameMigrationContexts(contexts) {
11863
+ const seen = /* @__PURE__ */ new Set();
11864
+ const out = [];
11865
+ for (const context of contexts) {
11866
+ const key = `${context.oldSegment}\0${context.newSegment}\0${context.repoRoot}`;
11867
+ if (seen.has(key)) {
11868
+ continue;
11869
+ }
11870
+ seen.add(key);
11871
+ out.push(context);
11872
+ }
11873
+ return out;
11705
11874
  }
11706
11875
  async function projectNameMigrationCandidates(config, context, limit) {
11707
11876
  const candidates = [];
@@ -11753,11 +11922,23 @@ async function projectNameMigrationCandidates(config, context, limit) {
11753
11922
  }
11754
11923
  return candidates;
11755
11924
  }
11756
- async function hasSeedManifestProjectNameMigrationCandidate(config, context) {
11757
- return await seedManifestProjectNameMigration(config, context) !== void 0;
11925
+ function projectNameMigrationActiveContexts(plans, seedManifestMigration) {
11926
+ return dedupeProjectNameMigrationContexts([
11927
+ ...plans.filter((plan) => plan.candidates.length > 0).map((plan) => plan.context),
11928
+ ...seedManifestMigration?.contexts ?? []
11929
+ ]);
11930
+ }
11931
+ function projectNameMigrationSummary(migratedCount, dryRun, contexts) {
11932
+ const memoryWord = migratedCount === 1 ? "memory" : "memories";
11933
+ const verb = dryRun ? "would be migrated" : "migrated";
11934
+ if (contexts.length === 1) {
11935
+ const [context] = contexts;
11936
+ return `Project-name migration summary: ${migratedCount} ${memoryWord} ${verb} from ${context.oldProject} to ${context.newProject}`;
11937
+ }
11938
+ const renameSummary = contexts.map((context) => `${context.oldProject} -> ${context.newProject}`).join(", ");
11939
+ return `Project-name migration summary: ${migratedCount} ${memoryWord} ${verb} across ${contexts.length} project rename(s)${renameSummary ? `: ${renameSummary}` : ""}`;
11758
11940
  }
11759
- async function migrateSeedManifestProjectName(config, context, dryRun) {
11760
- const migration = await seedManifestProjectNameMigration(config, context);
11941
+ async function migrateSeedManifestProjectNames(config, migration, dryRun) {
11761
11942
  if (!migration) {
11762
11943
  return false;
11763
11944
  }
@@ -11779,33 +11960,45 @@ async function migrateSeedManifestProjectName(config, context, dryRun) {
11779
11960
  console.log(`Updated seed manifest: ${config.manifestPath}`);
11780
11961
  return true;
11781
11962
  }
11782
- async function seedManifestProjectNameMigration(config, context) {
11963
+ async function seedManifestProjectNameMigration(config, contexts) {
11783
11964
  let manifest;
11784
11965
  try {
11785
11966
  manifest = await readSeedManifest(config.manifestPath);
11786
11967
  } catch (_err) {
11787
11968
  return void 0;
11788
11969
  }
11789
- const oldDefaultUri = `viking://resources/repos/${context.oldSegment}`;
11790
- const newDefaultUri = `viking://resources/repos/${context.newSegment}`;
11791
- const newNameExists = manifest.projects.some((project) => uriSegment(project.name) === context.newSegment);
11970
+ const renamed = /* @__PURE__ */ new Map();
11792
11971
  let changed = false;
11793
- let renamedProject = false;
11794
11972
  const projects = manifest.projects.map((project) => {
11795
- if (!isSeedManifestProjectNameCandidate(project, context, oldDefaultUri) || newNameExists) {
11973
+ const context = contexts.find(
11974
+ (candidate) => isSeedManifestProjectNameCandidate(
11975
+ project,
11976
+ candidate,
11977
+ `viking://resources/repos/${candidate.oldSegment}`,
11978
+ `viking://resources/repos/${candidate.newSegment}`
11979
+ )
11980
+ );
11981
+ if (!context) {
11982
+ return project;
11983
+ }
11984
+ const newNameExists = manifest.projects.some(
11985
+ (other) => other !== project && uriSegment(other.name) === context.newSegment
11986
+ );
11987
+ if (newNameExists || [...renamed.values()].some((existing) => existing.newSegment === context.newSegment)) {
11796
11988
  return project;
11797
11989
  }
11798
11990
  changed = true;
11799
- renamedProject = true;
11991
+ renamed.set(context.oldSegment, context);
11800
11992
  return {
11801
11993
  ...project,
11802
11994
  name: context.newProject,
11803
- uri: trimTrailingSlash(project.uri) === oldDefaultUri ? newDefaultUri : project.uri
11995
+ uri: trimTrailingSlash(project.uri) === `viking://resources/repos/${context.oldSegment}` ? `viking://resources/repos/${context.newSegment}` : project.uri
11804
11996
  };
11805
11997
  });
11806
- const worksets = renamedProject ? manifest.worksets?.map((workset) => {
11998
+ const worksets = renamed.size > 0 ? manifest.worksets?.map((workset) => {
11807
11999
  const members = workset.projects.map((projectName) => {
11808
- if (uriSegment(projectName) !== context.oldSegment) {
12000
+ const context = renamed.get(uriSegment(projectName));
12001
+ if (!context) {
11809
12002
  return projectName;
11810
12003
  }
11811
12004
  changed = true;
@@ -11817,6 +12010,8 @@ async function seedManifestProjectNameMigration(config, context) {
11817
12010
  return void 0;
11818
12011
  }
11819
12012
  return {
12013
+ contexts: [...renamed.values()],
12014
+ newProjects: [...new Set([...renamed.values()].map((context) => context.newProject))],
11820
12015
  output: `${index_vite_proxy_tmp_default.dump(
11821
12016
  {
11822
12017
  version: manifest.version,
@@ -11844,11 +12039,17 @@ async function seedManifestProjectNameMigration(config, context) {
11844
12039
  )}`
11845
12040
  };
11846
12041
  }
11847
- function isSeedManifestProjectNameCandidate(project, context, oldDefaultUri) {
11848
- if (uriSegment(project.name) !== context.oldSegment) {
12042
+ function isSeedManifestProjectNameCandidate(project, context, oldDefaultUri, newDefaultUri) {
12043
+ const nameSegment = uriSegment(project.name);
12044
+ const uriMatchesOld = trimTrailingSlash(project.uri) === oldDefaultUri;
12045
+ const pathMatchesRepo = expandPath(project.path) === context.repoRoot;
12046
+ if (nameSegment === context.newSegment && !uriMatchesOld) {
12047
+ return false;
12048
+ }
12049
+ if (nameSegment !== context.oldSegment && !uriMatchesOld && !pathMatchesRepo) {
11849
12050
  return false;
11850
12051
  }
11851
- return trimTrailingSlash(project.uri) === oldDefaultUri || expandPath(project.path) === context.repoRoot;
12052
+ return nameSegment !== context.newSegment || uriMatchesOld || trimTrailingSlash(project.uri) !== newDefaultUri;
11852
12053
  }
11853
12054
  function canMigrateProjectName(record, context) {
11854
12055
  const projectSegment = record.metadata.project ? uriSegment(record.metadata.project) : context.oldSegment;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadnote",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "type": "commonjs",
5
5
  "main": "dist/threadnote.cjs",
6
6
  "description": "Shared local context and handoffs for development agents",