@cleocode/core 2026.3.44 → 2026.3.45

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/index.js CHANGED
@@ -9122,7 +9122,7 @@ var init_tasks_schema = __esm({
9122
9122
  "supersedes"
9123
9123
  ];
9124
9124
  LIFECYCLE_TRANSITION_TYPES = ["automatic", "manual", "forced"];
9125
- EXTERNAL_LINK_TYPES = ["created", "matched", "manual"];
9125
+ EXTERNAL_LINK_TYPES = ["created", "matched", "manual", "transferred"];
9126
9126
  SYNC_DIRECTIONS = ["inbound", "outbound", "bidirectional"];
9127
9127
  tasks = sqliteTable(
9128
9128
  "tasks",
@@ -10579,7 +10579,9 @@ function getBrainDbPath(cwd) {
10579
10579
  function resolveBrainMigrationsFolder() {
10580
10580
  const __filename = fileURLToPath(import.meta.url);
10581
10581
  const __dirname = dirname3(__filename);
10582
- return join7(__dirname, "..", "..", "migrations", "drizzle-brain");
10582
+ const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
10583
+ const pkgRoot = isBundled ? join7(__dirname, "..") : join7(__dirname, "..", "..");
10584
+ return join7(pkgRoot, "migrations", "drizzle-brain");
10583
10585
  }
10584
10586
  function tableExists(nativeDb, tableName) {
10585
10587
  const result = nativeDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(tableName);
@@ -10823,7 +10825,9 @@ function getNexusDbPath() {
10823
10825
  function resolveNexusMigrationsFolder() {
10824
10826
  const __filename = fileURLToPath2(import.meta.url);
10825
10827
  const __dirname = dirname4(__filename);
10826
- return join8(__dirname, "..", "..", "migrations", "drizzle-nexus");
10828
+ const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
10829
+ const pkgRoot = isBundled ? join8(__dirname, "..") : join8(__dirname, "..", "..");
10830
+ return join8(pkgRoot, "migrations", "drizzle-nexus");
10827
10831
  }
10828
10832
  function tableExists2(nativeDb, tableName) {
10829
10833
  const result = nativeDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(tableName);
@@ -11014,7 +11018,7 @@ function getDbPath(cwd) {
11014
11018
  return join9(getCleoDirAbsolute(cwd), DB_FILENAME3);
11015
11019
  }
11016
11020
  async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
11017
- const log7 = getLogger("sqlite");
11021
+ const log8 = getLogger("sqlite");
11018
11022
  try {
11019
11023
  const countResult = nativeDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
11020
11024
  const taskCount = countResult?.cnt ?? 0;
@@ -11035,7 +11039,7 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
11035
11039
  if (backupTaskCount < MIN_BACKUP_TASK_COUNT) {
11036
11040
  return;
11037
11041
  }
11038
- log7.warn(
11042
+ log8.warn(
11039
11043
  { dbPath, backupPath: newestBackup.path, backupTasks: backupTaskCount },
11040
11044
  `Empty database detected with ${backupTaskCount}-task backup available. Auto-recovering from backup. This likely happened because git-tracked WAL/SHM files were overwritten during a branch switch (T5188).`
11041
11045
  );
@@ -11053,7 +11057,7 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
11053
11057
  const tempPath = dbPath + ".recovery-tmp";
11054
11058
  copyFileSync3(newestBackup.path, tempPath);
11055
11059
  renameSync(tempPath, dbPath);
11056
- log7.info(
11060
+ log8.info(
11057
11061
  { dbPath, backupPath: newestBackup.path, restoredTasks: backupTaskCount },
11058
11062
  "Database auto-recovered from backup successfully."
11059
11063
  );
@@ -11063,7 +11067,7 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
11063
11067
  runMigrations(restoredNativeDb, restoredDb);
11064
11068
  _db2 = restoredDb;
11065
11069
  } catch (err) {
11066
- log7.error({ err, dbPath }, "Auto-recovery from backup failed. Continuing with empty database.");
11070
+ log8.error({ err, dbPath }, "Auto-recovery from backup failed. Continuing with empty database.");
11067
11071
  }
11068
11072
  }
11069
11073
  async function getDb(cwd) {
@@ -11094,7 +11098,7 @@ async function getDb(cwd) {
11094
11098
  const { execFileSync: execFileSync11 } = await import("node:child_process");
11095
11099
  const gitCwd = resolve3(dbPath, "..", "..");
11096
11100
  const filesToCheck = [dbPath, dbPath + "-wal", dbPath + "-shm"];
11097
- const log7 = getLogger("sqlite");
11101
+ const log8 = getLogger("sqlite");
11098
11102
  for (const fileToCheck of filesToCheck) {
11099
11103
  try {
11100
11104
  execFileSync11("git", ["ls-files", "--error-unmatch", fileToCheck], {
@@ -11102,7 +11106,7 @@ async function getDb(cwd) {
11102
11106
  stdio: "pipe"
11103
11107
  });
11104
11108
  const basename18 = fileToCheck.split(/[\\/]/).pop();
11105
- log7.warn(
11109
+ log8.warn(
11106
11110
  { path: fileToCheck },
11107
11111
  `${basename18} is tracked by project git \u2014 this risks data loss on branch switch. Run: git rm --cached ${fileToCheck.replace(gitCwd + sep, "")} (see ADR-013, T5188)`
11108
11112
  );
@@ -11124,7 +11128,9 @@ async function getDb(cwd) {
11124
11128
  function resolveMigrationsFolder() {
11125
11129
  const __filename = fileURLToPath3(import.meta.url);
11126
11130
  const __dirname = dirname5(__filename);
11127
- return join9(__dirname, "..", "..", "migrations", "drizzle-tasks");
11131
+ const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
11132
+ const pkgRoot = isBundled ? join9(__dirname, "..") : join9(__dirname, "..", "..");
11133
+ return join9(pkgRoot, "migrations", "drizzle-tasks");
11128
11134
  }
11129
11135
  function tableExists3(nativeDb, tableName) {
11130
11136
  const result = nativeDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(tableName);
@@ -24246,6 +24252,37 @@ function buildExportPackage(tasks2, options) {
24246
24252
  relationshipGraph
24247
24253
  };
24248
24254
  }
24255
+ function exportSingle(taskId, allTasks, projectName) {
24256
+ const task = allTasks.find((t) => t.id === taskId);
24257
+ if (!task) return null;
24258
+ return buildExportPackage([task], {
24259
+ mode: "single",
24260
+ rootTaskIds: [taskId],
24261
+ includeChildren: false,
24262
+ projectName
24263
+ });
24264
+ }
24265
+ function exportSubtree(rootId, allTasks, projectName) {
24266
+ const root = allTasks.find((t) => t.id === rootId);
24267
+ if (!root) return null;
24268
+ const collected = /* @__PURE__ */ new Map();
24269
+ const queue = [rootId];
24270
+ while (queue.length > 0) {
24271
+ const id = queue.shift();
24272
+ const task = allTasks.find((t) => t.id === id);
24273
+ if (!task || collected.has(id)) continue;
24274
+ collected.set(id, task);
24275
+ const children = allTasks.filter((t) => t.parentId === id);
24276
+ queue.push(...children.map((c) => c.id));
24277
+ }
24278
+ const tasks2 = [...collected.values()];
24279
+ return buildExportPackage(tasks2, {
24280
+ mode: "subtree",
24281
+ rootTaskIds: [rootId],
24282
+ includeChildren: true,
24283
+ projectName
24284
+ });
24285
+ }
24249
24286
 
24250
24287
  // packages/core/src/admin/export-tasks.ts
24251
24288
  function parseFilter(filter) {
@@ -24678,20 +24715,7 @@ function topologicalSort(tasks2) {
24678
24715
  }
24679
24716
  return sorted;
24680
24717
  }
24681
- async function importTasksPackage(params) {
24682
- const { file: file2 } = params;
24683
- try {
24684
- await access2(file2, fsConstants2.R_OK);
24685
- } catch {
24686
- throw new Error(`Export file not found: ${file2}`);
24687
- }
24688
- const content = await readFile4(file2, "utf-8");
24689
- let exportPkg;
24690
- try {
24691
- exportPkg = JSON.parse(content);
24692
- } catch {
24693
- throw new Error(`Invalid JSON in: ${file2}`);
24694
- }
24718
+ async function importFromPackage(exportPkg, options = {}) {
24695
24719
  if (exportPkg._meta?.format !== "cleo-export") {
24696
24720
  throw new Error(
24697
24721
  `Invalid export format (expected 'cleo-export', got '${exportPkg._meta?.format}')`
@@ -24700,16 +24724,16 @@ async function importTasksPackage(params) {
24700
24724
  if (!exportPkg.tasks?.length) {
24701
24725
  throw new Error("Export package contains no tasks");
24702
24726
  }
24703
- const accessor = await getAccessor(params.cwd);
24727
+ const accessor = await getAccessor(options.cwd);
24704
24728
  const { tasks: existingTasks } = await accessor.queryTasks({});
24705
- const onConflict = params.onConflict ?? "fail";
24706
- const onMissingDep = params.onMissingDep ?? "strip";
24707
- const force = params.force ?? false;
24708
- const parentId = params.parent;
24709
- const phaseOverride = params.phase;
24710
- const addLabel = params.addLabel;
24711
- const resetStatus = params.resetStatus;
24712
- const addProvenance = params.provenance !== false;
24729
+ const onConflict = options.onConflict ?? "fail";
24730
+ const onMissingDep = options.onMissingDep === "fail" ? "fail" : "strip";
24731
+ const force = options.force ?? false;
24732
+ const parentId = options.parent;
24733
+ const phaseOverride = options.phase;
24734
+ const addLabel = options.addLabel;
24735
+ const resetStatus = options.resetStatus;
24736
+ const addProvenance = options.provenance !== false;
24713
24737
  if (parentId) {
24714
24738
  const parentExists = existingTasks.some((t) => t.id === parentId);
24715
24739
  if (!parentExists) {
@@ -24778,7 +24802,7 @@ async function importTasksPackage(params) {
24778
24802
  existingTitles.add(remapped.title.toLowerCase());
24779
24803
  existingIds.add(remapped.id);
24780
24804
  }
24781
- if (params.dryRun) {
24805
+ if (options.dryRun) {
24782
24806
  return {
24783
24807
  imported: transformed.length,
24784
24808
  skipped: skipped.length,
@@ -24798,6 +24822,33 @@ async function importTasksPackage(params) {
24798
24822
  idRemap: idRemapJson
24799
24823
  };
24800
24824
  }
24825
+ async function importTasksPackage(params) {
24826
+ const { file: file2 } = params;
24827
+ try {
24828
+ await access2(file2, fsConstants2.R_OK);
24829
+ } catch {
24830
+ throw new Error(`Export file not found: ${file2}`);
24831
+ }
24832
+ const content = await readFile4(file2, "utf-8");
24833
+ let exportPkg;
24834
+ try {
24835
+ exportPkg = JSON.parse(content);
24836
+ } catch {
24837
+ throw new Error(`Invalid JSON in: ${file2}`);
24838
+ }
24839
+ return importFromPackage(exportPkg, {
24840
+ cwd: params.cwd,
24841
+ dryRun: params.dryRun,
24842
+ parent: params.parent,
24843
+ phase: params.phase,
24844
+ addLabel: params.addLabel,
24845
+ provenance: params.provenance,
24846
+ resetStatus: params.resetStatus,
24847
+ onConflict: params.onConflict,
24848
+ onMissingDep: params.onMissingDep === "placeholder" ? "strip" : params.onMissingDep,
24849
+ force: params.force
24850
+ });
24851
+ }
24801
24852
 
24802
24853
  // packages/core/src/adrs/index.ts
24803
24854
  var adrs_exports = {};
@@ -43851,6 +43902,7 @@ __export(nexus_exports, {
43851
43902
  checkPermissionDetail: () => checkPermissionDetail,
43852
43903
  criticalPath: () => criticalPath,
43853
43904
  discoverRelated: () => discoverRelated,
43905
+ executeTransfer: () => executeTransfer,
43854
43906
  extractKeywords: () => extractKeywords2,
43855
43907
  generateProjectHash: () => generateProjectHash,
43856
43908
  getCurrentProject: () => getCurrentProject,
@@ -43874,6 +43926,7 @@ __export(nexus_exports, {
43874
43926
  orphanDetection: () => orphanDetection,
43875
43927
  parseQuery: () => parseQuery,
43876
43928
  permissionLevel: () => permissionLevel,
43929
+ previewTransfer: () => previewTransfer,
43877
43930
  readRegistry: () => readRegistry,
43878
43931
  readRegistryRequired: () => readRegistryRequired,
43879
43932
  requirePermission: () => requirePermission,
@@ -44751,6 +44804,325 @@ async function syncGitignore(cwd) {
44751
44804
  return { updated: true, entriesCount: entries.length };
44752
44805
  }
44753
44806
 
44807
+ // packages/core/src/nexus/transfer.ts
44808
+ import { randomUUID as randomUUID5 } from "node:crypto";
44809
+ init_logger();
44810
+
44811
+ // packages/core/src/reconciliation/link-store.ts
44812
+ init_sqlite2();
44813
+ init_tasks_schema();
44814
+ import { randomUUID as randomUUID4 } from "node:crypto";
44815
+ import { and as and5, eq as eq8 } from "drizzle-orm";
44816
+ async function getLinksByProvider(providerId, cwd) {
44817
+ const db = await getDb(cwd);
44818
+ const rows = await db.select().from(externalTaskLinks).where(eq8(externalTaskLinks.providerId, providerId));
44819
+ return rows.map(rowToLink);
44820
+ }
44821
+ async function getLinkByExternalId(providerId, externalId, cwd) {
44822
+ const db = await getDb(cwd);
44823
+ const rows = await db.select().from(externalTaskLinks).where(
44824
+ and5(
44825
+ eq8(externalTaskLinks.providerId, providerId),
44826
+ eq8(externalTaskLinks.externalId, externalId)
44827
+ )
44828
+ );
44829
+ return rows.length > 0 ? rowToLink(rows[0]) : null;
44830
+ }
44831
+ async function getLinksByTaskId(taskId, cwd) {
44832
+ const db = await getDb(cwd);
44833
+ const rows = await db.select().from(externalTaskLinks).where(eq8(externalTaskLinks.taskId, taskId));
44834
+ return rows.map(rowToLink);
44835
+ }
44836
+ async function createLink(params, cwd) {
44837
+ const db = await getDb(cwd);
44838
+ const now = (/* @__PURE__ */ new Date()).toISOString();
44839
+ const id = randomUUID4();
44840
+ await db.insert(externalTaskLinks).values({
44841
+ id,
44842
+ taskId: params.taskId,
44843
+ providerId: params.providerId,
44844
+ externalId: params.externalId,
44845
+ externalUrl: params.externalUrl ?? null,
44846
+ externalTitle: params.externalTitle ?? null,
44847
+ linkType: params.linkType,
44848
+ syncDirection: params.syncDirection ?? "inbound",
44849
+ metadataJson: params.metadata ? JSON.stringify(params.metadata) : "{}",
44850
+ linkedAt: now,
44851
+ lastSyncAt: now
44852
+ });
44853
+ return {
44854
+ id,
44855
+ taskId: params.taskId,
44856
+ providerId: params.providerId,
44857
+ externalId: params.externalId,
44858
+ externalUrl: params.externalUrl ?? null,
44859
+ externalTitle: params.externalTitle ?? null,
44860
+ linkType: params.linkType,
44861
+ syncDirection: params.syncDirection ?? "inbound",
44862
+ metadata: params.metadata,
44863
+ linkedAt: now,
44864
+ lastSyncAt: now
44865
+ };
44866
+ }
44867
+ async function touchLink(linkId, updates, cwd) {
44868
+ const db = await getDb(cwd);
44869
+ const now = (/* @__PURE__ */ new Date()).toISOString();
44870
+ const values = { lastSyncAt: now };
44871
+ if (updates?.externalTitle !== void 0) {
44872
+ values.externalTitle = updates.externalTitle;
44873
+ }
44874
+ if (updates?.metadata !== void 0) {
44875
+ values.metadataJson = JSON.stringify(updates.metadata);
44876
+ }
44877
+ await db.update(externalTaskLinks).set(values).where(eq8(externalTaskLinks.id, linkId));
44878
+ }
44879
+ async function removeLinksByProvider(providerId, cwd) {
44880
+ const db = await getDb(cwd);
44881
+ const result = await db.delete(externalTaskLinks).where(eq8(externalTaskLinks.providerId, providerId));
44882
+ return Number(result.changes);
44883
+ }
44884
+ function rowToLink(row) {
44885
+ return {
44886
+ id: row.id,
44887
+ taskId: row.taskId,
44888
+ providerId: row.providerId,
44889
+ externalId: row.externalId,
44890
+ externalUrl: row.externalUrl,
44891
+ externalTitle: row.externalTitle,
44892
+ linkType: row.linkType,
44893
+ syncDirection: row.syncDirection,
44894
+ metadata: row.metadataJson ? JSON.parse(row.metadataJson) : void 0,
44895
+ linkedAt: row.linkedAt,
44896
+ lastSyncAt: row.lastSyncAt
44897
+ };
44898
+ }
44899
+
44900
+ // packages/core/src/nexus/transfer.ts
44901
+ init_brain_accessor();
44902
+ init_brain_sqlite();
44903
+ init_data_accessor();
44904
+ init_registry3();
44905
+ var log6 = getLogger("nexus:transfer");
44906
+ async function previewTransfer(params) {
44907
+ return executeTransferInternal({ ...params, dryRun: true });
44908
+ }
44909
+ async function executeTransfer(params) {
44910
+ return executeTransferInternal(params);
44911
+ }
44912
+ async function executeTransferInternal(params) {
44913
+ const {
44914
+ taskIds,
44915
+ sourceProject: sourceProjectRef,
44916
+ targetProject: targetProjectRef,
44917
+ mode = "copy",
44918
+ scope = "subtree",
44919
+ onConflict = "rename",
44920
+ onMissingDep = "strip",
44921
+ provenance = true,
44922
+ targetParent,
44923
+ transferBrain = false,
44924
+ dryRun = false
44925
+ } = params;
44926
+ if (!taskIds.length) {
44927
+ throw new Error("No task IDs specified for transfer");
44928
+ }
44929
+ const sourceProject = await nexusGetProject(sourceProjectRef);
44930
+ if (!sourceProject) {
44931
+ throw new Error(`Source project not found: ${sourceProjectRef}`);
44932
+ }
44933
+ const targetProject = await nexusGetProject(targetProjectRef);
44934
+ if (!targetProject) {
44935
+ throw new Error(`Target project not found: ${targetProjectRef}`);
44936
+ }
44937
+ if (sourceProject.hash === targetProject.hash) {
44938
+ throw new Error("Source and target projects must be different");
44939
+ }
44940
+ await requirePermission(sourceProject.hash, "read", "nexus.transfer");
44941
+ await requirePermission(targetProject.hash, "write", "nexus.transfer");
44942
+ const sourceAccessor = await getAccessor(sourceProject.path);
44943
+ const { tasks: allSourceTasks } = await sourceAccessor.queryTasks({});
44944
+ const exportPackages = [];
44945
+ for (const taskId of taskIds) {
44946
+ const pkg = scope === "subtree" ? exportSubtree(taskId, allSourceTasks, sourceProject.name) : exportSingle(taskId, allSourceTasks, sourceProject.name);
44947
+ if (!pkg) {
44948
+ throw new Error(`Task not found in source project: ${taskId}`);
44949
+ }
44950
+ exportPackages.push(pkg);
44951
+ }
44952
+ const seenIds = /* @__PURE__ */ new Set();
44953
+ const mergedTasks = [];
44954
+ for (const pkg of exportPackages) {
44955
+ for (const task of pkg.tasks) {
44956
+ if (!seenIds.has(task.id)) {
44957
+ seenIds.add(task.id);
44958
+ mergedTasks.push(task);
44959
+ }
44960
+ }
44961
+ }
44962
+ const mergedPkg = { ...exportPackages[0] };
44963
+ mergedPkg.tasks = mergedTasks;
44964
+ mergedPkg._meta = { ...mergedPkg._meta, taskCount: mergedTasks.length };
44965
+ const importResult = await importFromPackage(mergedPkg, {
44966
+ cwd: targetProject.path,
44967
+ dryRun,
44968
+ parent: targetParent,
44969
+ provenance,
44970
+ onConflict,
44971
+ onMissingDep
44972
+ });
44973
+ const entries = mergedTasks.map((t) => ({
44974
+ sourceId: t.id,
44975
+ targetId: importResult.idRemap[t.id] ?? t.id,
44976
+ title: t.title,
44977
+ type: t.type ?? "task"
44978
+ }));
44979
+ const manifest = {
44980
+ sourceProject: sourceProject.name,
44981
+ targetProject: targetProject.name,
44982
+ mode,
44983
+ scope,
44984
+ entries,
44985
+ idRemap: importResult.idRemap,
44986
+ brainObservationsTransferred: 0
44987
+ };
44988
+ const result = {
44989
+ dryRun,
44990
+ transferred: importResult.imported,
44991
+ skipped: importResult.skipped,
44992
+ archived: 0,
44993
+ linksCreated: 0,
44994
+ brainObservationsTransferred: 0,
44995
+ manifest
44996
+ };
44997
+ if (dryRun) {
44998
+ return result;
44999
+ }
45000
+ let linksCreated = 0;
45001
+ const targetAccessor = await getAccessor(targetProject.path);
45002
+ const { tasks: targetTasks } = await targetAccessor.queryTasks({});
45003
+ const targetTaskIds = new Set(targetTasks.map((t) => t.id));
45004
+ for (const entry of entries) {
45005
+ if (importResult.idRemap[entry.sourceId] && targetTaskIds.has(entry.targetId)) {
45006
+ await createLink(
45007
+ {
45008
+ taskId: entry.targetId,
45009
+ providerId: `nexus:${sourceProject.name}`,
45010
+ externalId: entry.sourceId,
45011
+ externalTitle: entry.title,
45012
+ linkType: "transferred",
45013
+ syncDirection: "inbound",
45014
+ metadata: {
45015
+ transferMode: mode,
45016
+ transferScope: scope,
45017
+ sourceProject: sourceProject.name,
45018
+ transferredAt: (/* @__PURE__ */ new Date()).toISOString()
45019
+ }
45020
+ },
45021
+ targetProject.path
45022
+ );
45023
+ linksCreated++;
45024
+ await createLink(
45025
+ {
45026
+ taskId: entry.sourceId,
45027
+ providerId: `nexus:${targetProject.name}`,
45028
+ externalId: entry.targetId,
45029
+ externalTitle: entry.title,
45030
+ linkType: "transferred",
45031
+ syncDirection: "outbound",
45032
+ metadata: {
45033
+ transferMode: mode,
45034
+ transferScope: scope,
45035
+ targetProject: targetProject.name,
45036
+ transferredAt: (/* @__PURE__ */ new Date()).toISOString()
45037
+ }
45038
+ },
45039
+ sourceProject.path
45040
+ );
45041
+ linksCreated++;
45042
+ }
45043
+ }
45044
+ result.linksCreated = linksCreated;
45045
+ try {
45046
+ const { getNexusDb: getNexusDb2 } = await Promise.resolve().then(() => (init_nexus_sqlite(), nexus_sqlite_exports));
45047
+ const { nexusAuditLog: nexusAuditLog2 } = await Promise.resolve().then(() => (init_nexus_schema(), nexus_schema_exports));
45048
+ const db = await getNexusDb2();
45049
+ await db.insert(nexusAuditLog2).values({
45050
+ id: randomUUID5(),
45051
+ action: "transfer",
45052
+ projectHash: sourceProject.hash,
45053
+ projectId: sourceProject.projectId,
45054
+ domain: "nexus",
45055
+ operation: "transfer",
45056
+ success: 1,
45057
+ detailsJson: JSON.stringify({
45058
+ sourceProject: sourceProject.name,
45059
+ targetProject: targetProject.name,
45060
+ mode,
45061
+ scope,
45062
+ taskCount: result.transferred,
45063
+ idRemap: importResult.idRemap
45064
+ })
45065
+ });
45066
+ } catch (err) {
45067
+ log6.warn({ err }, "nexus transfer audit write failed");
45068
+ }
45069
+ if (mode === "move") {
45070
+ let archived = 0;
45071
+ for (const entry of entries) {
45072
+ if (importResult.idRemap[entry.sourceId]) {
45073
+ try {
45074
+ await sourceAccessor.archiveSingleTask(entry.sourceId, {
45075
+ archivedAt: (/* @__PURE__ */ new Date()).toISOString(),
45076
+ archiveReason: `Transferred to ${targetProject.name} as ${entry.targetId}`
45077
+ });
45078
+ archived++;
45079
+ } catch (err) {
45080
+ log6.warn({ err, taskId: entry.sourceId }, "failed to archive source task after transfer");
45081
+ }
45082
+ }
45083
+ }
45084
+ result.archived = archived;
45085
+ }
45086
+ if (transferBrain) {
45087
+ let brainTransferred = 0;
45088
+ try {
45089
+ const sourceBrainDb = await getBrainDb(sourceProject.path);
45090
+ const targetBrainDb = await getBrainDb(targetProject.path);
45091
+ const sourceBrain = new BrainDataAccessor(sourceBrainDb);
45092
+ const targetBrain = new BrainDataAccessor(targetBrainDb);
45093
+ for (const entry of entries) {
45094
+ if (!importResult.idRemap[entry.sourceId]) continue;
45095
+ const links = await sourceBrain.getLinksForTask(entry.sourceId);
45096
+ for (const link of links) {
45097
+ if (link.memoryType !== "observation") continue;
45098
+ const observation = await sourceBrain.getObservation(link.memoryId);
45099
+ if (!observation) continue;
45100
+ const newObsId = `O-${randomUUID5().slice(0, 8)}`;
45101
+ await targetBrain.addObservation({
45102
+ ...observation,
45103
+ id: newObsId,
45104
+ createdAt: observation.createdAt,
45105
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19)
45106
+ });
45107
+ await targetBrain.addLink({
45108
+ memoryType: "observation",
45109
+ memoryId: newObsId,
45110
+ taskId: entry.targetId,
45111
+ linkType: "applies_to",
45112
+ createdAt: (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19)
45113
+ });
45114
+ brainTransferred++;
45115
+ }
45116
+ }
45117
+ } catch (err) {
45118
+ log6.warn({ err }, "brain observation transfer failed");
45119
+ }
45120
+ result.brainObservationsTransferred = brainTransferred;
45121
+ result.manifest.brainObservationsTransferred = brainTransferred;
45122
+ }
45123
+ return result;
45124
+ }
45125
+
44754
45126
  // packages/core/src/observability/index.ts
44755
45127
  var observability_exports = {};
44756
45128
  __export(observability_exports, {
@@ -46149,95 +46521,6 @@ __export(reconciliation_exports, {
46149
46521
  touchLink: () => touchLink
46150
46522
  });
46151
46523
 
46152
- // packages/core/src/reconciliation/link-store.ts
46153
- init_sqlite2();
46154
- init_tasks_schema();
46155
- import { randomUUID as randomUUID4 } from "node:crypto";
46156
- import { and as and5, eq as eq8 } from "drizzle-orm";
46157
- async function getLinksByProvider(providerId, cwd) {
46158
- const db = await getDb(cwd);
46159
- const rows = await db.select().from(externalTaskLinks).where(eq8(externalTaskLinks.providerId, providerId));
46160
- return rows.map(rowToLink);
46161
- }
46162
- async function getLinkByExternalId(providerId, externalId, cwd) {
46163
- const db = await getDb(cwd);
46164
- const rows = await db.select().from(externalTaskLinks).where(
46165
- and5(
46166
- eq8(externalTaskLinks.providerId, providerId),
46167
- eq8(externalTaskLinks.externalId, externalId)
46168
- )
46169
- );
46170
- return rows.length > 0 ? rowToLink(rows[0]) : null;
46171
- }
46172
- async function getLinksByTaskId(taskId, cwd) {
46173
- const db = await getDb(cwd);
46174
- const rows = await db.select().from(externalTaskLinks).where(eq8(externalTaskLinks.taskId, taskId));
46175
- return rows.map(rowToLink);
46176
- }
46177
- async function createLink(params, cwd) {
46178
- const db = await getDb(cwd);
46179
- const now = (/* @__PURE__ */ new Date()).toISOString();
46180
- const id = randomUUID4();
46181
- await db.insert(externalTaskLinks).values({
46182
- id,
46183
- taskId: params.taskId,
46184
- providerId: params.providerId,
46185
- externalId: params.externalId,
46186
- externalUrl: params.externalUrl ?? null,
46187
- externalTitle: params.externalTitle ?? null,
46188
- linkType: params.linkType,
46189
- syncDirection: params.syncDirection ?? "inbound",
46190
- metadataJson: params.metadata ? JSON.stringify(params.metadata) : "{}",
46191
- linkedAt: now,
46192
- lastSyncAt: now
46193
- });
46194
- return {
46195
- id,
46196
- taskId: params.taskId,
46197
- providerId: params.providerId,
46198
- externalId: params.externalId,
46199
- externalUrl: params.externalUrl ?? null,
46200
- externalTitle: params.externalTitle ?? null,
46201
- linkType: params.linkType,
46202
- syncDirection: params.syncDirection ?? "inbound",
46203
- metadata: params.metadata,
46204
- linkedAt: now,
46205
- lastSyncAt: now
46206
- };
46207
- }
46208
- async function touchLink(linkId, updates, cwd) {
46209
- const db = await getDb(cwd);
46210
- const now = (/* @__PURE__ */ new Date()).toISOString();
46211
- const values = { lastSyncAt: now };
46212
- if (updates?.externalTitle !== void 0) {
46213
- values.externalTitle = updates.externalTitle;
46214
- }
46215
- if (updates?.metadata !== void 0) {
46216
- values.metadataJson = JSON.stringify(updates.metadata);
46217
- }
46218
- await db.update(externalTaskLinks).set(values).where(eq8(externalTaskLinks.id, linkId));
46219
- }
46220
- async function removeLinksByProvider(providerId, cwd) {
46221
- const db = await getDb(cwd);
46222
- const result = await db.delete(externalTaskLinks).where(eq8(externalTaskLinks.providerId, providerId));
46223
- return Number(result.changes);
46224
- }
46225
- function rowToLink(row) {
46226
- return {
46227
- id: row.id,
46228
- taskId: row.taskId,
46229
- providerId: row.providerId,
46230
- externalId: row.externalId,
46231
- externalUrl: row.externalUrl,
46232
- externalTitle: row.externalTitle,
46233
- linkType: row.linkType,
46234
- syncDirection: row.syncDirection,
46235
- metadata: row.metadataJson ? JSON.parse(row.metadataJson) : void 0,
46236
- linkedAt: row.linkedAt,
46237
- lastSyncAt: row.lastSyncAt
46238
- };
46239
- }
46240
-
46241
46524
  // packages/core/src/reconciliation/reconciliation-engine.ts
46242
46525
  init_data_accessor();
46243
46526
  init_add();
@@ -53945,11 +54228,11 @@ import { join as join84 } from "node:path";
53945
54228
  import { Readable } from "node:stream";
53946
54229
  import { pipeline } from "node:stream/promises";
53947
54230
  import { createGzip } from "node:zlib";
53948
- var log6 = getLogger("prune");
54231
+ var log7 = getLogger("prune");
53949
54232
  async function pruneAuditLog(cleoDir, config2) {
53950
54233
  try {
53951
54234
  if (!config2.auditRetentionDays || config2.auditRetentionDays <= 0) {
53952
- log6.debug("auditRetentionDays is 0 or unset; skipping audit prune");
54235
+ log7.debug("auditRetentionDays is 0 or unset; skipping audit prune");
53953
54236
  return { rowsArchived: 0, rowsDeleted: 0 };
53954
54237
  }
53955
54238
  const cutoff = new Date(Date.now() - config2.auditRetentionDays * 864e5).toISOString();
@@ -53960,7 +54243,7 @@ async function pruneAuditLog(cleoDir, config2) {
53960
54243
  const db = await getDb3(projectRoot);
53961
54244
  const oldRows = await db.select().from(auditLog2).where(lt3(auditLog2.timestamp, cutoff));
53962
54245
  if (oldRows.length === 0) {
53963
- log6.debug("No audit_log rows older than cutoff; nothing to prune");
54246
+ log7.debug("No audit_log rows older than cutoff; nothing to prune");
53964
54247
  return { rowsArchived: 0, rowsDeleted: 0 };
53965
54248
  }
53966
54249
  let archivePath;
@@ -53978,17 +54261,17 @@ async function pruneAuditLog(cleoDir, config2) {
53978
54261
  const inStream = Readable.from([jsonlContent]);
53979
54262
  await pipeline(inStream, gzip, outStream);
53980
54263
  rowsArchived = oldRows.length;
53981
- log6.info(
54264
+ log7.info(
53982
54265
  { archivePath, rowsArchived },
53983
54266
  `Archived ${rowsArchived} audit rows to ${archivePath}`
53984
54267
  );
53985
54268
  } catch (archiveErr) {
53986
- log6.warn({ err: archiveErr }, "Failed to archive audit rows; continuing with deletion");
54269
+ log7.warn({ err: archiveErr }, "Failed to archive audit rows; continuing with deletion");
53987
54270
  archivePath = void 0;
53988
54271
  }
53989
54272
  }
53990
54273
  await db.delete(auditLog2).where(lt3(auditLog2.timestamp, cutoff)).run();
53991
- log6.info(
54274
+ log7.info(
53992
54275
  { rowsDeleted: oldRows.length, cutoff },
53993
54276
  `Pruned ${oldRows.length} audit_log rows older than ${cutoff}`
53994
54277
  );
@@ -53998,7 +54281,7 @@ async function pruneAuditLog(cleoDir, config2) {
53998
54281
  archivePath
53999
54282
  };
54000
54283
  } catch (err) {
54001
- log6.warn({ err }, "audit log pruning failed");
54284
+ log7.warn({ err }, "audit log pruning failed");
54002
54285
  return { rowsArchived: 0, rowsDeleted: 0 };
54003
54286
  }
54004
54287
  }
@@ -60816,7 +61099,7 @@ init_logger();
60816
61099
 
60817
61100
  // packages/core/src/output.ts
60818
61101
  init_errors3();
60819
- import { randomUUID as randomUUID5 } from "node:crypto";
61102
+ import { randomUUID as randomUUID6 } from "node:crypto";
60820
61103
 
60821
61104
  // packages/core/src/sessions/context-alert.ts
60822
61105
  init_paths();
@@ -60849,7 +61132,7 @@ function createCliMeta(operation, mvi = "standard") {
60849
61132
  schemaVersion: "2026.2.1",
60850
61133
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
60851
61134
  operation,
60852
- requestId: randomUUID5(),
61135
+ requestId: randomUUID6(),
60853
61136
  transport: "cli",
60854
61137
  strict: true,
60855
61138
  mvi,