@cleocode/cleo 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/mcp/index.js CHANGED
@@ -9123,7 +9123,7 @@ var init_tasks_schema = __esm({
9123
9123
  "supersedes"
9124
9124
  ];
9125
9125
  LIFECYCLE_TRANSITION_TYPES = ["automatic", "manual", "forced"];
9126
- EXTERNAL_LINK_TYPES = ["created", "matched", "manual"];
9126
+ EXTERNAL_LINK_TYPES = ["created", "matched", "manual", "transferred"];
9127
9127
  SYNC_DIRECTIONS = ["inbound", "outbound", "bidirectional"];
9128
9128
  tasks = sqliteTable(
9129
9129
  "tasks",
@@ -10583,7 +10583,9 @@ function getBrainDbPath(cwd) {
10583
10583
  function resolveBrainMigrationsFolder() {
10584
10584
  const __filename = fileURLToPath(import.meta.url);
10585
10585
  const __dirname = dirname3(__filename);
10586
- return join7(__dirname, "..", "..", "migrations", "drizzle-brain");
10586
+ const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
10587
+ const pkgRoot = isBundled ? join7(__dirname, "..") : join7(__dirname, "..", "..");
10588
+ return join7(pkgRoot, "migrations", "drizzle-brain");
10587
10589
  }
10588
10590
  function tableExists(nativeDb, tableName) {
10589
10591
  const result = nativeDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(tableName);
@@ -10827,7 +10829,9 @@ function getNexusDbPath() {
10827
10829
  function resolveNexusMigrationsFolder() {
10828
10830
  const __filename = fileURLToPath2(import.meta.url);
10829
10831
  const __dirname = dirname4(__filename);
10830
- return join8(__dirname, "..", "..", "migrations", "drizzle-nexus");
10832
+ const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
10833
+ const pkgRoot = isBundled ? join8(__dirname, "..") : join8(__dirname, "..", "..");
10834
+ return join8(pkgRoot, "migrations", "drizzle-nexus");
10831
10835
  }
10832
10836
  function tableExists2(nativeDb, tableName) {
10833
10837
  const result = nativeDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(tableName);
@@ -11018,7 +11022,7 @@ function getDbPath(cwd) {
11018
11022
  return join9(getCleoDirAbsolute(cwd), DB_FILENAME3);
11019
11023
  }
11020
11024
  async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
11021
- const log9 = getLogger("sqlite");
11025
+ const log10 = getLogger("sqlite");
11022
11026
  try {
11023
11027
  const countResult = nativeDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
11024
11028
  const taskCount = countResult?.cnt ?? 0;
@@ -11039,7 +11043,7 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
11039
11043
  if (backupTaskCount < MIN_BACKUP_TASK_COUNT) {
11040
11044
  return;
11041
11045
  }
11042
- log9.warn(
11046
+ log10.warn(
11043
11047
  { dbPath, backupPath: newestBackup.path, backupTasks: backupTaskCount },
11044
11048
  `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).`
11045
11049
  );
@@ -11057,7 +11061,7 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
11057
11061
  const tempPath = dbPath + ".recovery-tmp";
11058
11062
  copyFileSync3(newestBackup.path, tempPath);
11059
11063
  renameSync(tempPath, dbPath);
11060
- log9.info(
11064
+ log10.info(
11061
11065
  { dbPath, backupPath: newestBackup.path, restoredTasks: backupTaskCount },
11062
11066
  "Database auto-recovered from backup successfully."
11063
11067
  );
@@ -11067,7 +11071,7 @@ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
11067
11071
  runMigrations(restoredNativeDb, restoredDb);
11068
11072
  _db2 = restoredDb;
11069
11073
  } catch (err) {
11070
- log9.error({ err, dbPath }, "Auto-recovery from backup failed. Continuing with empty database.");
11074
+ log10.error({ err, dbPath }, "Auto-recovery from backup failed. Continuing with empty database.");
11071
11075
  }
11072
11076
  }
11073
11077
  async function getDb(cwd) {
@@ -11098,7 +11102,7 @@ async function getDb(cwd) {
11098
11102
  const { execFileSync: execFileSync14 } = await import("node:child_process");
11099
11103
  const gitCwd = resolve3(dbPath, "..", "..");
11100
11104
  const filesToCheck = [dbPath, dbPath + "-wal", dbPath + "-shm"];
11101
- const log9 = getLogger("sqlite");
11105
+ const log10 = getLogger("sqlite");
11102
11106
  for (const fileToCheck of filesToCheck) {
11103
11107
  try {
11104
11108
  execFileSync14("git", ["ls-files", "--error-unmatch", fileToCheck], {
@@ -11106,7 +11110,7 @@ async function getDb(cwd) {
11106
11110
  stdio: "pipe"
11107
11111
  });
11108
11112
  const basename19 = fileToCheck.split(/[\\/]/).pop();
11109
- log9.warn(
11113
+ log10.warn(
11110
11114
  { path: fileToCheck },
11111
11115
  `${basename19} 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)`
11112
11116
  );
@@ -11128,7 +11132,9 @@ async function getDb(cwd) {
11128
11132
  function resolveMigrationsFolder() {
11129
11133
  const __filename = fileURLToPath3(import.meta.url);
11130
11134
  const __dirname = dirname5(__filename);
11131
- return join9(__dirname, "..", "..", "migrations", "drizzle-tasks");
11135
+ const isBundled = __dirname.endsWith("/dist") || __dirname.endsWith("\\dist");
11136
+ const pkgRoot = isBundled ? join9(__dirname, "..") : join9(__dirname, "..", "..");
11137
+ return join9(pkgRoot, "migrations", "drizzle-tasks");
11132
11138
  }
11133
11139
  function tableExists3(nativeDb, tableName) {
11134
11140
  const result = nativeDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(tableName);
@@ -14396,6 +14402,37 @@ function buildExportPackage(tasks2, options) {
14396
14402
  relationshipGraph
14397
14403
  };
14398
14404
  }
14405
+ function exportSingle(taskId, allTasks, projectName) {
14406
+ const task = allTasks.find((t) => t.id === taskId);
14407
+ if (!task) return null;
14408
+ return buildExportPackage([task], {
14409
+ mode: "single",
14410
+ rootTaskIds: [taskId],
14411
+ includeChildren: false,
14412
+ projectName
14413
+ });
14414
+ }
14415
+ function exportSubtree(rootId, allTasks, projectName) {
14416
+ const root = allTasks.find((t) => t.id === rootId);
14417
+ if (!root) return null;
14418
+ const collected = /* @__PURE__ */ new Map();
14419
+ const queue = [rootId];
14420
+ while (queue.length > 0) {
14421
+ const id = queue.shift();
14422
+ const task = allTasks.find((t) => t.id === id);
14423
+ if (!task || collected.has(id)) continue;
14424
+ collected.set(id, task);
14425
+ const children = allTasks.filter((t) => t.parentId === id);
14426
+ queue.push(...children.map((c) => c.id));
14427
+ }
14428
+ const tasks2 = [...collected.values()];
14429
+ return buildExportPackage(tasks2, {
14430
+ mode: "subtree",
14431
+ rootTaskIds: [rootId],
14432
+ includeChildren: true,
14433
+ projectName
14434
+ });
14435
+ }
14399
14436
  var EXPORT_FORMAT_VERSION;
14400
14437
  var init_export2 = __esm({
14401
14438
  "packages/core/src/store/export.ts"() {
@@ -14856,20 +14893,7 @@ function topologicalSort(tasks2) {
14856
14893
  }
14857
14894
  return sorted;
14858
14895
  }
14859
- async function importTasksPackage(params) {
14860
- const { file: file2 } = params;
14861
- try {
14862
- await access2(file2, fsConstants2.R_OK);
14863
- } catch {
14864
- throw new Error(`Export file not found: ${file2}`);
14865
- }
14866
- const content = await readFile4(file2, "utf-8");
14867
- let exportPkg;
14868
- try {
14869
- exportPkg = JSON.parse(content);
14870
- } catch {
14871
- throw new Error(`Invalid JSON in: ${file2}`);
14872
- }
14896
+ async function importFromPackage(exportPkg, options = {}) {
14873
14897
  if (exportPkg._meta?.format !== "cleo-export") {
14874
14898
  throw new Error(
14875
14899
  `Invalid export format (expected 'cleo-export', got '${exportPkg._meta?.format}')`
@@ -14878,16 +14902,16 @@ async function importTasksPackage(params) {
14878
14902
  if (!exportPkg.tasks?.length) {
14879
14903
  throw new Error("Export package contains no tasks");
14880
14904
  }
14881
- const accessor = await getAccessor(params.cwd);
14905
+ const accessor = await getAccessor(options.cwd);
14882
14906
  const { tasks: existingTasks } = await accessor.queryTasks({});
14883
- const onConflict = params.onConflict ?? "fail";
14884
- const onMissingDep = params.onMissingDep ?? "strip";
14885
- const force = params.force ?? false;
14886
- const parentId = params.parent;
14887
- const phaseOverride = params.phase;
14888
- const addLabel = params.addLabel;
14889
- const resetStatus = params.resetStatus;
14890
- const addProvenance = params.provenance !== false;
14907
+ const onConflict = options.onConflict ?? "fail";
14908
+ const onMissingDep = options.onMissingDep === "fail" ? "fail" : "strip";
14909
+ const force = options.force ?? false;
14910
+ const parentId = options.parent;
14911
+ const phaseOverride = options.phase;
14912
+ const addLabel = options.addLabel;
14913
+ const resetStatus = options.resetStatus;
14914
+ const addProvenance = options.provenance !== false;
14891
14915
  if (parentId) {
14892
14916
  const parentExists = existingTasks.some((t) => t.id === parentId);
14893
14917
  if (!parentExists) {
@@ -14956,7 +14980,7 @@ async function importTasksPackage(params) {
14956
14980
  existingTitles.add(remapped.title.toLowerCase());
14957
14981
  existingIds.add(remapped.id);
14958
14982
  }
14959
- if (params.dryRun) {
14983
+ if (options.dryRun) {
14960
14984
  return {
14961
14985
  imported: transformed.length,
14962
14986
  skipped: skipped.length,
@@ -14976,6 +15000,33 @@ async function importTasksPackage(params) {
14976
15000
  idRemap: idRemapJson
14977
15001
  };
14978
15002
  }
15003
+ async function importTasksPackage(params) {
15004
+ const { file: file2 } = params;
15005
+ try {
15006
+ await access2(file2, fsConstants2.R_OK);
15007
+ } catch {
15008
+ throw new Error(`Export file not found: ${file2}`);
15009
+ }
15010
+ const content = await readFile4(file2, "utf-8");
15011
+ let exportPkg;
15012
+ try {
15013
+ exportPkg = JSON.parse(content);
15014
+ } catch {
15015
+ throw new Error(`Invalid JSON in: ${file2}`);
15016
+ }
15017
+ return importFromPackage(exportPkg, {
15018
+ cwd: params.cwd,
15019
+ dryRun: params.dryRun,
15020
+ parent: params.parent,
15021
+ phase: params.phase,
15022
+ addLabel: params.addLabel,
15023
+ provenance: params.provenance,
15024
+ resetStatus: params.resetStatus,
15025
+ onConflict: params.onConflict,
15026
+ onMissingDep: params.onMissingDep === "placeholder" ? "strip" : params.onMissingDep,
15027
+ force: params.force
15028
+ });
15029
+ }
14979
15030
  var init_import_tasks = __esm({
14980
15031
  "packages/core/src/admin/import-tasks.ts"() {
14981
15032
  "use strict";
@@ -43370,6 +43421,338 @@ var init_sharing = __esm({
43370
43421
  }
43371
43422
  });
43372
43423
 
43424
+ // packages/core/src/reconciliation/link-store.ts
43425
+ import { randomUUID as randomUUID4 } from "node:crypto";
43426
+ import { and as and5, eq as eq8 } from "drizzle-orm";
43427
+ async function getLinksByProvider(providerId, cwd) {
43428
+ const db = await getDb(cwd);
43429
+ const rows = await db.select().from(externalTaskLinks).where(eq8(externalTaskLinks.providerId, providerId));
43430
+ return rows.map(rowToLink);
43431
+ }
43432
+ async function getLinkByExternalId(providerId, externalId, cwd) {
43433
+ const db = await getDb(cwd);
43434
+ const rows = await db.select().from(externalTaskLinks).where(
43435
+ and5(
43436
+ eq8(externalTaskLinks.providerId, providerId),
43437
+ eq8(externalTaskLinks.externalId, externalId)
43438
+ )
43439
+ );
43440
+ return rows.length > 0 ? rowToLink(rows[0]) : null;
43441
+ }
43442
+ async function getLinksByTaskId(taskId, cwd) {
43443
+ const db = await getDb(cwd);
43444
+ const rows = await db.select().from(externalTaskLinks).where(eq8(externalTaskLinks.taskId, taskId));
43445
+ return rows.map(rowToLink);
43446
+ }
43447
+ async function createLink(params, cwd) {
43448
+ const db = await getDb(cwd);
43449
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
43450
+ const id = randomUUID4();
43451
+ await db.insert(externalTaskLinks).values({
43452
+ id,
43453
+ taskId: params.taskId,
43454
+ providerId: params.providerId,
43455
+ externalId: params.externalId,
43456
+ externalUrl: params.externalUrl ?? null,
43457
+ externalTitle: params.externalTitle ?? null,
43458
+ linkType: params.linkType,
43459
+ syncDirection: params.syncDirection ?? "inbound",
43460
+ metadataJson: params.metadata ? JSON.stringify(params.metadata) : "{}",
43461
+ linkedAt: now2,
43462
+ lastSyncAt: now2
43463
+ });
43464
+ return {
43465
+ id,
43466
+ taskId: params.taskId,
43467
+ providerId: params.providerId,
43468
+ externalId: params.externalId,
43469
+ externalUrl: params.externalUrl ?? null,
43470
+ externalTitle: params.externalTitle ?? null,
43471
+ linkType: params.linkType,
43472
+ syncDirection: params.syncDirection ?? "inbound",
43473
+ metadata: params.metadata,
43474
+ linkedAt: now2,
43475
+ lastSyncAt: now2
43476
+ };
43477
+ }
43478
+ async function touchLink(linkId, updates, cwd) {
43479
+ const db = await getDb(cwd);
43480
+ const now2 = (/* @__PURE__ */ new Date()).toISOString();
43481
+ const values = { lastSyncAt: now2 };
43482
+ if (updates?.externalTitle !== void 0) {
43483
+ values.externalTitle = updates.externalTitle;
43484
+ }
43485
+ if (updates?.metadata !== void 0) {
43486
+ values.metadataJson = JSON.stringify(updates.metadata);
43487
+ }
43488
+ await db.update(externalTaskLinks).set(values).where(eq8(externalTaskLinks.id, linkId));
43489
+ }
43490
+ async function removeLinksByProvider(providerId, cwd) {
43491
+ const db = await getDb(cwd);
43492
+ const result = await db.delete(externalTaskLinks).where(eq8(externalTaskLinks.providerId, providerId));
43493
+ return Number(result.changes);
43494
+ }
43495
+ function rowToLink(row) {
43496
+ return {
43497
+ id: row.id,
43498
+ taskId: row.taskId,
43499
+ providerId: row.providerId,
43500
+ externalId: row.externalId,
43501
+ externalUrl: row.externalUrl,
43502
+ externalTitle: row.externalTitle,
43503
+ linkType: row.linkType,
43504
+ syncDirection: row.syncDirection,
43505
+ metadata: row.metadataJson ? JSON.parse(row.metadataJson) : void 0,
43506
+ linkedAt: row.linkedAt,
43507
+ lastSyncAt: row.lastSyncAt
43508
+ };
43509
+ }
43510
+ var init_link_store = __esm({
43511
+ "packages/core/src/reconciliation/link-store.ts"() {
43512
+ "use strict";
43513
+ init_sqlite2();
43514
+ init_tasks_schema();
43515
+ }
43516
+ });
43517
+
43518
+ // packages/core/src/nexus/transfer.ts
43519
+ import { randomUUID as randomUUID5 } from "node:crypto";
43520
+ async function previewTransfer(params) {
43521
+ return executeTransferInternal({ ...params, dryRun: true });
43522
+ }
43523
+ async function executeTransfer(params) {
43524
+ return executeTransferInternal(params);
43525
+ }
43526
+ async function executeTransferInternal(params) {
43527
+ const {
43528
+ taskIds,
43529
+ sourceProject: sourceProjectRef,
43530
+ targetProject: targetProjectRef,
43531
+ mode = "copy",
43532
+ scope = "subtree",
43533
+ onConflict = "rename",
43534
+ onMissingDep = "strip",
43535
+ provenance = true,
43536
+ targetParent,
43537
+ transferBrain = false,
43538
+ dryRun = false
43539
+ } = params;
43540
+ if (!taskIds.length) {
43541
+ throw new Error("No task IDs specified for transfer");
43542
+ }
43543
+ const sourceProject = await nexusGetProject(sourceProjectRef);
43544
+ if (!sourceProject) {
43545
+ throw new Error(`Source project not found: ${sourceProjectRef}`);
43546
+ }
43547
+ const targetProject = await nexusGetProject(targetProjectRef);
43548
+ if (!targetProject) {
43549
+ throw new Error(`Target project not found: ${targetProjectRef}`);
43550
+ }
43551
+ if (sourceProject.hash === targetProject.hash) {
43552
+ throw new Error("Source and target projects must be different");
43553
+ }
43554
+ await requirePermission(sourceProject.hash, "read", "nexus.transfer");
43555
+ await requirePermission(targetProject.hash, "write", "nexus.transfer");
43556
+ const sourceAccessor = await getAccessor(sourceProject.path);
43557
+ const { tasks: allSourceTasks } = await sourceAccessor.queryTasks({});
43558
+ const exportPackages = [];
43559
+ for (const taskId of taskIds) {
43560
+ const pkg = scope === "subtree" ? exportSubtree(taskId, allSourceTasks, sourceProject.name) : exportSingle(taskId, allSourceTasks, sourceProject.name);
43561
+ if (!pkg) {
43562
+ throw new Error(`Task not found in source project: ${taskId}`);
43563
+ }
43564
+ exportPackages.push(pkg);
43565
+ }
43566
+ const seenIds = /* @__PURE__ */ new Set();
43567
+ const mergedTasks = [];
43568
+ for (const pkg of exportPackages) {
43569
+ for (const task of pkg.tasks) {
43570
+ if (!seenIds.has(task.id)) {
43571
+ seenIds.add(task.id);
43572
+ mergedTasks.push(task);
43573
+ }
43574
+ }
43575
+ }
43576
+ const mergedPkg = { ...exportPackages[0] };
43577
+ mergedPkg.tasks = mergedTasks;
43578
+ mergedPkg._meta = { ...mergedPkg._meta, taskCount: mergedTasks.length };
43579
+ const importResult = await importFromPackage(mergedPkg, {
43580
+ cwd: targetProject.path,
43581
+ dryRun,
43582
+ parent: targetParent,
43583
+ provenance,
43584
+ onConflict,
43585
+ onMissingDep
43586
+ });
43587
+ const entries = mergedTasks.map((t) => ({
43588
+ sourceId: t.id,
43589
+ targetId: importResult.idRemap[t.id] ?? t.id,
43590
+ title: t.title,
43591
+ type: t.type ?? "task"
43592
+ }));
43593
+ const manifest = {
43594
+ sourceProject: sourceProject.name,
43595
+ targetProject: targetProject.name,
43596
+ mode,
43597
+ scope,
43598
+ entries,
43599
+ idRemap: importResult.idRemap,
43600
+ brainObservationsTransferred: 0
43601
+ };
43602
+ const result = {
43603
+ dryRun,
43604
+ transferred: importResult.imported,
43605
+ skipped: importResult.skipped,
43606
+ archived: 0,
43607
+ linksCreated: 0,
43608
+ brainObservationsTransferred: 0,
43609
+ manifest
43610
+ };
43611
+ if (dryRun) {
43612
+ return result;
43613
+ }
43614
+ let linksCreated = 0;
43615
+ const targetAccessor = await getAccessor(targetProject.path);
43616
+ const { tasks: targetTasks } = await targetAccessor.queryTasks({});
43617
+ const targetTaskIds = new Set(targetTasks.map((t) => t.id));
43618
+ for (const entry of entries) {
43619
+ if (importResult.idRemap[entry.sourceId] && targetTaskIds.has(entry.targetId)) {
43620
+ await createLink(
43621
+ {
43622
+ taskId: entry.targetId,
43623
+ providerId: `nexus:${sourceProject.name}`,
43624
+ externalId: entry.sourceId,
43625
+ externalTitle: entry.title,
43626
+ linkType: "transferred",
43627
+ syncDirection: "inbound",
43628
+ metadata: {
43629
+ transferMode: mode,
43630
+ transferScope: scope,
43631
+ sourceProject: sourceProject.name,
43632
+ transferredAt: (/* @__PURE__ */ new Date()).toISOString()
43633
+ }
43634
+ },
43635
+ targetProject.path
43636
+ );
43637
+ linksCreated++;
43638
+ await createLink(
43639
+ {
43640
+ taskId: entry.sourceId,
43641
+ providerId: `nexus:${targetProject.name}`,
43642
+ externalId: entry.targetId,
43643
+ externalTitle: entry.title,
43644
+ linkType: "transferred",
43645
+ syncDirection: "outbound",
43646
+ metadata: {
43647
+ transferMode: mode,
43648
+ transferScope: scope,
43649
+ targetProject: targetProject.name,
43650
+ transferredAt: (/* @__PURE__ */ new Date()).toISOString()
43651
+ }
43652
+ },
43653
+ sourceProject.path
43654
+ );
43655
+ linksCreated++;
43656
+ }
43657
+ }
43658
+ result.linksCreated = linksCreated;
43659
+ try {
43660
+ const { getNexusDb: getNexusDb2 } = await Promise.resolve().then(() => (init_nexus_sqlite(), nexus_sqlite_exports));
43661
+ const { nexusAuditLog: nexusAuditLog2 } = await Promise.resolve().then(() => (init_nexus_schema(), nexus_schema_exports));
43662
+ const db = await getNexusDb2();
43663
+ await db.insert(nexusAuditLog2).values({
43664
+ id: randomUUID5(),
43665
+ action: "transfer",
43666
+ projectHash: sourceProject.hash,
43667
+ projectId: sourceProject.projectId,
43668
+ domain: "nexus",
43669
+ operation: "transfer",
43670
+ success: 1,
43671
+ detailsJson: JSON.stringify({
43672
+ sourceProject: sourceProject.name,
43673
+ targetProject: targetProject.name,
43674
+ mode,
43675
+ scope,
43676
+ taskCount: result.transferred,
43677
+ idRemap: importResult.idRemap
43678
+ })
43679
+ });
43680
+ } catch (err) {
43681
+ log6.warn({ err }, "nexus transfer audit write failed");
43682
+ }
43683
+ if (mode === "move") {
43684
+ let archived = 0;
43685
+ for (const entry of entries) {
43686
+ if (importResult.idRemap[entry.sourceId]) {
43687
+ try {
43688
+ await sourceAccessor.archiveSingleTask(entry.sourceId, {
43689
+ archivedAt: (/* @__PURE__ */ new Date()).toISOString(),
43690
+ archiveReason: `Transferred to ${targetProject.name} as ${entry.targetId}`
43691
+ });
43692
+ archived++;
43693
+ } catch (err) {
43694
+ log6.warn({ err, taskId: entry.sourceId }, "failed to archive source task after transfer");
43695
+ }
43696
+ }
43697
+ }
43698
+ result.archived = archived;
43699
+ }
43700
+ if (transferBrain) {
43701
+ let brainTransferred = 0;
43702
+ try {
43703
+ const sourceBrainDb = await getBrainDb(sourceProject.path);
43704
+ const targetBrainDb = await getBrainDb(targetProject.path);
43705
+ const sourceBrain = new BrainDataAccessor(sourceBrainDb);
43706
+ const targetBrain = new BrainDataAccessor(targetBrainDb);
43707
+ for (const entry of entries) {
43708
+ if (!importResult.idRemap[entry.sourceId]) continue;
43709
+ const links = await sourceBrain.getLinksForTask(entry.sourceId);
43710
+ for (const link of links) {
43711
+ if (link.memoryType !== "observation") continue;
43712
+ const observation = await sourceBrain.getObservation(link.memoryId);
43713
+ if (!observation) continue;
43714
+ const newObsId = `O-${randomUUID5().slice(0, 8)}`;
43715
+ await targetBrain.addObservation({
43716
+ ...observation,
43717
+ id: newObsId,
43718
+ createdAt: observation.createdAt,
43719
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19)
43720
+ });
43721
+ await targetBrain.addLink({
43722
+ memoryType: "observation",
43723
+ memoryId: newObsId,
43724
+ taskId: entry.targetId,
43725
+ linkType: "applies_to",
43726
+ createdAt: (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19)
43727
+ });
43728
+ brainTransferred++;
43729
+ }
43730
+ }
43731
+ } catch (err) {
43732
+ log6.warn({ err }, "brain observation transfer failed");
43733
+ }
43734
+ result.brainObservationsTransferred = brainTransferred;
43735
+ result.manifest.brainObservationsTransferred = brainTransferred;
43736
+ }
43737
+ return result;
43738
+ }
43739
+ var log6;
43740
+ var init_transfer = __esm({
43741
+ "packages/core/src/nexus/transfer.ts"() {
43742
+ "use strict";
43743
+ init_import_tasks();
43744
+ init_logger();
43745
+ init_link_store();
43746
+ init_brain_accessor();
43747
+ init_brain_sqlite();
43748
+ init_data_accessor();
43749
+ init_export2();
43750
+ init_permissions();
43751
+ init_registry3();
43752
+ log6 = getLogger("nexus:transfer");
43753
+ }
43754
+ });
43755
+
43373
43756
  // packages/core/src/nexus/index.ts
43374
43757
  var nexus_exports = {};
43375
43758
  __export(nexus_exports, {
@@ -43382,6 +43765,7 @@ __export(nexus_exports, {
43382
43765
  checkPermissionDetail: () => checkPermissionDetail,
43383
43766
  criticalPath: () => criticalPath,
43384
43767
  discoverRelated: () => discoverRelated,
43768
+ executeTransfer: () => executeTransfer,
43385
43769
  extractKeywords: () => extractKeywords2,
43386
43770
  generateProjectHash: () => generateProjectHash,
43387
43771
  getCurrentProject: () => getCurrentProject,
@@ -43405,6 +43789,7 @@ __export(nexus_exports, {
43405
43789
  orphanDetection: () => orphanDetection,
43406
43790
  parseQuery: () => parseQuery,
43407
43791
  permissionLevel: () => permissionLevel,
43792
+ previewTransfer: () => previewTransfer,
43408
43793
  readRegistry: () => readRegistry,
43409
43794
  readRegistryRequired: () => readRegistryRequired,
43410
43795
  requirePermission: () => requirePermission,
@@ -43427,6 +43812,7 @@ var init_nexus = __esm({
43427
43812
  init_query3();
43428
43813
  init_registry3();
43429
43814
  init_sharing();
43815
+ init_transfer();
43430
43816
  }
43431
43817
  });
43432
43818
 
@@ -44927,100 +45313,6 @@ var init_pipeline = __esm({
44927
45313
  }
44928
45314
  });
44929
45315
 
44930
- // packages/core/src/reconciliation/link-store.ts
44931
- import { randomUUID as randomUUID4 } from "node:crypto";
44932
- import { and as and5, eq as eq8 } from "drizzle-orm";
44933
- async function getLinksByProvider(providerId, cwd) {
44934
- const db = await getDb(cwd);
44935
- const rows = await db.select().from(externalTaskLinks).where(eq8(externalTaskLinks.providerId, providerId));
44936
- return rows.map(rowToLink);
44937
- }
44938
- async function getLinkByExternalId(providerId, externalId, cwd) {
44939
- const db = await getDb(cwd);
44940
- const rows = await db.select().from(externalTaskLinks).where(
44941
- and5(
44942
- eq8(externalTaskLinks.providerId, providerId),
44943
- eq8(externalTaskLinks.externalId, externalId)
44944
- )
44945
- );
44946
- return rows.length > 0 ? rowToLink(rows[0]) : null;
44947
- }
44948
- async function getLinksByTaskId(taskId, cwd) {
44949
- const db = await getDb(cwd);
44950
- const rows = await db.select().from(externalTaskLinks).where(eq8(externalTaskLinks.taskId, taskId));
44951
- return rows.map(rowToLink);
44952
- }
44953
- async function createLink(params, cwd) {
44954
- const db = await getDb(cwd);
44955
- const now2 = (/* @__PURE__ */ new Date()).toISOString();
44956
- const id = randomUUID4();
44957
- await db.insert(externalTaskLinks).values({
44958
- id,
44959
- taskId: params.taskId,
44960
- providerId: params.providerId,
44961
- externalId: params.externalId,
44962
- externalUrl: params.externalUrl ?? null,
44963
- externalTitle: params.externalTitle ?? null,
44964
- linkType: params.linkType,
44965
- syncDirection: params.syncDirection ?? "inbound",
44966
- metadataJson: params.metadata ? JSON.stringify(params.metadata) : "{}",
44967
- linkedAt: now2,
44968
- lastSyncAt: now2
44969
- });
44970
- return {
44971
- id,
44972
- taskId: params.taskId,
44973
- providerId: params.providerId,
44974
- externalId: params.externalId,
44975
- externalUrl: params.externalUrl ?? null,
44976
- externalTitle: params.externalTitle ?? null,
44977
- linkType: params.linkType,
44978
- syncDirection: params.syncDirection ?? "inbound",
44979
- metadata: params.metadata,
44980
- linkedAt: now2,
44981
- lastSyncAt: now2
44982
- };
44983
- }
44984
- async function touchLink(linkId, updates, cwd) {
44985
- const db = await getDb(cwd);
44986
- const now2 = (/* @__PURE__ */ new Date()).toISOString();
44987
- const values = { lastSyncAt: now2 };
44988
- if (updates?.externalTitle !== void 0) {
44989
- values.externalTitle = updates.externalTitle;
44990
- }
44991
- if (updates?.metadata !== void 0) {
44992
- values.metadataJson = JSON.stringify(updates.metadata);
44993
- }
44994
- await db.update(externalTaskLinks).set(values).where(eq8(externalTaskLinks.id, linkId));
44995
- }
44996
- async function removeLinksByProvider(providerId, cwd) {
44997
- const db = await getDb(cwd);
44998
- const result = await db.delete(externalTaskLinks).where(eq8(externalTaskLinks.providerId, providerId));
44999
- return Number(result.changes);
45000
- }
45001
- function rowToLink(row) {
45002
- return {
45003
- id: row.id,
45004
- taskId: row.taskId,
45005
- providerId: row.providerId,
45006
- externalId: row.externalId,
45007
- externalUrl: row.externalUrl,
45008
- externalTitle: row.externalTitle,
45009
- linkType: row.linkType,
45010
- syncDirection: row.syncDirection,
45011
- metadata: row.metadataJson ? JSON.parse(row.metadataJson) : void 0,
45012
- linkedAt: row.linkedAt,
45013
- lastSyncAt: row.lastSyncAt
45014
- };
45015
- }
45016
- var init_link_store = __esm({
45017
- "packages/core/src/reconciliation/link-store.ts"() {
45018
- "use strict";
45019
- init_sqlite2();
45020
- init_tasks_schema();
45021
- }
45022
- });
45023
-
45024
45316
  // packages/core/src/memory/auto-extract.ts
45025
45317
  var auto_extract_exports = {};
45026
45318
  __export(auto_extract_exports, {
@@ -55464,7 +55756,7 @@ import { createGzip } from "node:zlib";
55464
55756
  async function pruneAuditLog(cleoDir, config2) {
55465
55757
  try {
55466
55758
  if (!config2.auditRetentionDays || config2.auditRetentionDays <= 0) {
55467
- log6.debug("auditRetentionDays is 0 or unset; skipping audit prune");
55759
+ log7.debug("auditRetentionDays is 0 or unset; skipping audit prune");
55468
55760
  return { rowsArchived: 0, rowsDeleted: 0 };
55469
55761
  }
55470
55762
  const cutoff = new Date(Date.now() - config2.auditRetentionDays * 864e5).toISOString();
@@ -55475,7 +55767,7 @@ async function pruneAuditLog(cleoDir, config2) {
55475
55767
  const db = await getDb4(projectRoot);
55476
55768
  const oldRows = await db.select().from(auditLog2).where(lt3(auditLog2.timestamp, cutoff));
55477
55769
  if (oldRows.length === 0) {
55478
- log6.debug("No audit_log rows older than cutoff; nothing to prune");
55770
+ log7.debug("No audit_log rows older than cutoff; nothing to prune");
55479
55771
  return { rowsArchived: 0, rowsDeleted: 0 };
55480
55772
  }
55481
55773
  let archivePath;
@@ -55493,17 +55785,17 @@ async function pruneAuditLog(cleoDir, config2) {
55493
55785
  const inStream = Readable.from([jsonlContent]);
55494
55786
  await pipeline(inStream, gzip, outStream);
55495
55787
  rowsArchived = oldRows.length;
55496
- log6.info(
55788
+ log7.info(
55497
55789
  { archivePath, rowsArchived },
55498
55790
  `Archived ${rowsArchived} audit rows to ${archivePath}`
55499
55791
  );
55500
55792
  } catch (archiveErr) {
55501
- log6.warn({ err: archiveErr }, "Failed to archive audit rows; continuing with deletion");
55793
+ log7.warn({ err: archiveErr }, "Failed to archive audit rows; continuing with deletion");
55502
55794
  archivePath = void 0;
55503
55795
  }
55504
55796
  }
55505
55797
  await db.delete(auditLog2).where(lt3(auditLog2.timestamp, cutoff)).run();
55506
- log6.info(
55798
+ log7.info(
55507
55799
  { rowsDeleted: oldRows.length, cutoff },
55508
55800
  `Pruned ${oldRows.length} audit_log rows older than ${cutoff}`
55509
55801
  );
@@ -55513,16 +55805,16 @@ async function pruneAuditLog(cleoDir, config2) {
55513
55805
  archivePath
55514
55806
  };
55515
55807
  } catch (err) {
55516
- log6.warn({ err }, "audit log pruning failed");
55808
+ log7.warn({ err }, "audit log pruning failed");
55517
55809
  return { rowsArchived: 0, rowsDeleted: 0 };
55518
55810
  }
55519
55811
  }
55520
- var log6;
55812
+ var log7;
55521
55813
  var init_audit_prune = __esm({
55522
55814
  "packages/core/src/audit-prune.ts"() {
55523
55815
  "use strict";
55524
55816
  init_logger();
55525
- log6 = getLogger("prune");
55817
+ log7 = getLogger("prune");
55526
55818
  }
55527
55819
  });
55528
55820
 
@@ -63237,7 +63529,7 @@ var init_context_alert = __esm({
63237
63529
  });
63238
63530
 
63239
63531
  // packages/core/src/output.ts
63240
- import { randomUUID as randomUUID5 } from "node:crypto";
63532
+ import { randomUUID as randomUUID6 } from "node:crypto";
63241
63533
  function pushWarning(warning) {
63242
63534
  pendingWarnings.push(warning);
63243
63535
  }
@@ -63254,7 +63546,7 @@ function createCliMeta(operation, mvi = "standard") {
63254
63546
  schemaVersion: "2026.2.1",
63255
63547
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
63256
63548
  operation,
63257
- requestId: randomUUID5(),
63549
+ requestId: randomUUID6(),
63258
63550
  transport: "cli",
63259
63551
  strict: true,
63260
63552
  mvi,
@@ -63630,6 +63922,7 @@ var init_cleo = __esm({
63630
63922
  // packages/core/src/index.ts
63631
63923
  var init_src2 = __esm({
63632
63924
  "packages/core/src/index.ts"() {
63925
+ "use strict";
63633
63926
  init_src();
63634
63927
  init_adapters();
63635
63928
  init_admin();
@@ -65048,7 +65341,7 @@ var init_chain_validation = __esm({
65048
65341
  });
65049
65342
 
65050
65343
  // packages/core/src/lifecycle/chain-store.ts
65051
- import { randomUUID as randomUUID6 } from "node:crypto";
65344
+ import { randomUUID as randomUUID7 } from "node:crypto";
65052
65345
  async function addChain(chain, projectRoot) {
65053
65346
  const validation = validateChain(chain);
65054
65347
  if (validation.errors.length > 0) {
@@ -65087,7 +65380,7 @@ async function createInstance(params, projectRoot) {
65087
65380
  if (!chain) {
65088
65381
  throw new Error(`Chain "${params.chainId}" not found`);
65089
65382
  }
65090
- const id = `wci-${randomUUID6().slice(0, 8)}`;
65383
+ const id = `wci-${randomUUID7().slice(0, 8)}`;
65091
65384
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
65092
65385
  const variables = params.variables ?? {};
65093
65386
  const stageToTask = params.stageToTask ?? {};
@@ -67392,7 +67685,7 @@ var init_model_provider_registry = __esm({
67392
67685
  });
67393
67686
 
67394
67687
  // packages/core/src/metrics/token-service.ts
67395
- import { createHash as createHash13, randomUUID as randomUUID7 } from "node:crypto";
67688
+ import { createHash as createHash13, randomUUID as randomUUID8 } from "node:crypto";
67396
67689
  import { existsSync as existsSync106, readdirSync as readdirSync34, readFileSync as readFileSync78 } from "node:fs";
67397
67690
  import { join as join105 } from "node:path";
67398
67691
  function normalizeProvider(provider, model, runtimeProvider) {
@@ -67645,7 +67938,7 @@ async function recordTokenExchange(input) {
67645
67938
  const measurement = await measureTokenExchange(input);
67646
67939
  const db = await getDb4(input.cwd);
67647
67940
  const row = {
67648
- id: randomUUID7(),
67941
+ id: randomUUID8(),
67649
67942
  provider: measurement.provider,
67650
67943
  model: measurement.model,
67651
67944
  transport: input.transport ?? "unknown",
@@ -68338,7 +68631,7 @@ async function validateAndRepairSequence(cwd, config2 = {}) {
68338
68631
  }
68339
68632
  const repair = await repairSequence(cwd);
68340
68633
  if (repair.repaired) {
68341
- log7.warn(
68634
+ log8.warn(
68342
68635
  { oldCounter: repair.oldCounter, newCounter: repair.newCounter },
68343
68636
  "Sequence repaired"
68344
68637
  );
@@ -68367,7 +68660,7 @@ async function triggerCheckpoint(context, cwd, config2 = {}) {
68367
68660
  try {
68368
68661
  await gitCheckpoint("auto", context, cwd);
68369
68662
  } catch (err) {
68370
- log7.warn({ err }, "Checkpoint failed (non-fatal)");
68663
+ log8.warn({ err }, "Checkpoint failed (non-fatal)");
68371
68664
  }
68372
68665
  vacuumIntoBackup({ cwd }).catch(() => {
68373
68666
  });
@@ -68405,16 +68698,16 @@ async function safeDeleteTask(deleteFn, taskId, cwd, config2 = {}) {
68405
68698
  return result;
68406
68699
  }
68407
68700
  async function forceCheckpointBeforeOperation(operation, cwd) {
68408
- log7.info({ operation }, "Forcing checkpoint before operation");
68701
+ log8.info({ operation }, "Forcing checkpoint before operation");
68409
68702
  try {
68410
68703
  await gitCheckpoint("manual", `pre-${operation}`, cwd);
68411
68704
  } catch (err) {
68412
- log7.error({ err }, "Failed to create pre-operation checkpoint");
68705
+ log8.error({ err }, "Failed to create pre-operation checkpoint");
68413
68706
  }
68414
68707
  vacuumIntoBackup({ cwd, force: true }).catch(() => {
68415
68708
  });
68416
68709
  }
68417
- var log7, DEFAULT_CONFIG2, SafetyError;
68710
+ var log8, DEFAULT_CONFIG2, SafetyError;
68418
68711
  var init_data_safety = __esm({
68419
68712
  "packages/core/src/store/data-safety.ts"() {
68420
68713
  "use strict";
@@ -68424,7 +68717,7 @@ var init_data_safety = __esm({
68424
68717
  init_sqlite2();
68425
68718
  init_sqlite_backup();
68426
68719
  init_tasks_schema();
68427
- log7 = getLogger("data-safety");
68720
+ log8 = getLogger("data-safety");
68428
68721
  DEFAULT_CONFIG2 = {
68429
68722
  verifyWrites: true,
68430
68723
  detectCollisions: true,
@@ -74283,6 +74576,7 @@ __export(internal_exports, {
74283
74576
  ensureProjectContext: () => ensureProjectContext,
74284
74577
  ensureSqliteDb: () => ensureSqliteDb,
74285
74578
  estimateContext: () => estimateContext,
74579
+ executeTransfer: () => executeTransfer,
74286
74580
  exportSnapshot: () => exportSnapshot,
74287
74581
  exportTasks: () => exportTasks,
74288
74582
  exportTasksPackage: () => exportTasksPackage,
@@ -74409,6 +74703,7 @@ __export(internal_exports, {
74409
74703
  gradeSession: () => gradeSession,
74410
74704
  heartbeat: () => heartbeat,
74411
74705
  hooks: () => hooks,
74706
+ importFromPackage: () => importFromPackage,
74412
74707
  importSnapshot: () => importSnapshot,
74413
74708
  importTasks: () => importTasks,
74414
74709
  importTasksPackage: () => importTasksPackage,
@@ -74552,6 +74847,7 @@ __export(internal_exports, {
74552
74847
  predictValidationOutcome: () => predictValidationOutcome,
74553
74848
  prepareRelease: () => prepareRelease,
74554
74849
  prepareSpawn: () => prepareSpawn,
74850
+ previewTransfer: () => previewTransfer,
74555
74851
  protocolEnforcer: () => protocolEnforcer,
74556
74852
  pruneAuditLog: () => pruneAuditLog,
74557
74853
  pull: () => pull,
@@ -74842,6 +75138,7 @@ var init_internal = __esm({
74842
75138
  init_session_memory();
74843
75139
  init_discover();
74844
75140
  init_registry3();
75141
+ init_transfer();
74845
75142
  init_analyze();
74846
75143
  init_critical_path();
74847
75144
  init_orchestration();
@@ -78461,10 +78758,10 @@ var StdioServerTransport = class {
78461
78758
 
78462
78759
  // packages/cleo/src/dispatch/adapters/mcp.ts
78463
78760
  init_src2();
78464
- import { randomUUID as randomUUID9 } from "node:crypto";
78761
+ import { randomUUID as randomUUID10 } from "node:crypto";
78465
78762
 
78466
78763
  // packages/cleo/src/dispatch/lib/meta.ts
78467
- import { randomUUID as randomUUID8 } from "node:crypto";
78764
+ import { randomUUID as randomUUID9 } from "node:crypto";
78468
78765
  function createDispatchMeta(gateway, domain2, operation, startTime, source = "mcp", requestId, sessionId) {
78469
78766
  return {
78470
78767
  gateway,
@@ -78473,7 +78770,7 @@ function createDispatchMeta(gateway, domain2, operation, startTime, source = "mc
78473
78770
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
78474
78771
  duration_ms: Date.now() - startTime,
78475
78772
  source,
78476
- requestId: requestId ?? randomUUID8(),
78773
+ requestId: requestId ?? randomUUID9(),
78477
78774
  ...sessionId != null && { sessionId }
78478
78775
  };
78479
78776
  }
@@ -81029,6 +81326,95 @@ var OPERATIONS = [
81029
81326
  requiredParams: []
81030
81327
  },
81031
81328
  // ---------------------------------------------------------------------------
81329
+ // nexus.transfer — Cross-project task transfer (T046)
81330
+ // ---------------------------------------------------------------------------
81331
+ {
81332
+ gateway: "query",
81333
+ domain: "nexus",
81334
+ operation: "transfer.preview",
81335
+ description: "nexus.transfer.preview (query) \u2014 preview a cross-project task transfer",
81336
+ tier: 2,
81337
+ idempotent: true,
81338
+ sessionRequired: false,
81339
+ requiredParams: ["taskIds", "sourceProject", "targetProject"],
81340
+ params: [
81341
+ { name: "taskIds", type: "array", required: true, description: "Task IDs to transfer" },
81342
+ {
81343
+ name: "sourceProject",
81344
+ type: "string",
81345
+ required: true,
81346
+ description: "Source project name or hash"
81347
+ },
81348
+ {
81349
+ name: "targetProject",
81350
+ type: "string",
81351
+ required: true,
81352
+ description: "Target project name or hash"
81353
+ },
81354
+ {
81355
+ name: "mode",
81356
+ type: "string",
81357
+ required: false,
81358
+ description: "Transfer mode: 'copy' (default) or 'move'"
81359
+ },
81360
+ {
81361
+ name: "scope",
81362
+ type: "string",
81363
+ required: false,
81364
+ description: "Transfer scope: 'subtree' (default) or 'single'"
81365
+ }
81366
+ ]
81367
+ },
81368
+ {
81369
+ gateway: "mutate",
81370
+ domain: "nexus",
81371
+ operation: "transfer",
81372
+ description: "nexus.transfer (mutate) \u2014 transfer tasks between NEXUS projects",
81373
+ tier: 2,
81374
+ idempotent: false,
81375
+ sessionRequired: false,
81376
+ requiredParams: ["taskIds", "sourceProject", "targetProject"],
81377
+ params: [
81378
+ { name: "taskIds", type: "array", required: true, description: "Task IDs to transfer" },
81379
+ {
81380
+ name: "sourceProject",
81381
+ type: "string",
81382
+ required: true,
81383
+ description: "Source project name or hash"
81384
+ },
81385
+ {
81386
+ name: "targetProject",
81387
+ type: "string",
81388
+ required: true,
81389
+ description: "Target project name or hash"
81390
+ },
81391
+ {
81392
+ name: "mode",
81393
+ type: "string",
81394
+ required: false,
81395
+ description: "Transfer mode: 'copy' (default) or 'move'"
81396
+ },
81397
+ {
81398
+ name: "scope",
81399
+ type: "string",
81400
+ required: false,
81401
+ description: "Transfer scope: 'subtree' (default) or 'single'"
81402
+ },
81403
+ {
81404
+ name: "onConflict",
81405
+ type: "string",
81406
+ required: false,
81407
+ description: "Conflict strategy: 'rename' (default), 'skip', 'duplicate', 'fail'"
81408
+ },
81409
+ {
81410
+ name: "transferBrain",
81411
+ type: "boolean",
81412
+ required: false,
81413
+ description: "Whether to transfer brain observations (default: false)"
81414
+ }
81415
+ ]
81416
+ },
81417
+ // ---------------------------------------------------------------------------
81032
81418
  // sticky — Ephemeral notes for quick capture (T5282)
81033
81419
  // ---------------------------------------------------------------------------
81034
81420
  // Query operations
@@ -86584,6 +86970,22 @@ async function nexusShareSnapshotImport(projectRoot, inputPath) {
86584
86970
  return engineError("E_INTERNAL", error40 instanceof Error ? error40.message : String(error40));
86585
86971
  }
86586
86972
  }
86973
+ async function nexusTransferPreview(params) {
86974
+ try {
86975
+ const result = await previewTransfer(params);
86976
+ return engineSuccess(result);
86977
+ } catch (error40) {
86978
+ return engineError("E_INTERNAL", error40 instanceof Error ? error40.message : String(error40));
86979
+ }
86980
+ }
86981
+ async function nexusTransferExecute(params) {
86982
+ try {
86983
+ const result = await executeTransfer(params);
86984
+ return engineSuccess(result);
86985
+ } catch (error40) {
86986
+ return engineError("E_INTERNAL", error40 instanceof Error ? error40.message : String(error40));
86987
+ }
86988
+ }
86587
86989
 
86588
86990
  // packages/cleo/src/dispatch/domains/nexus.ts
86589
86991
  var NexusHandler = class {
@@ -86745,6 +87147,29 @@ var NexusHandler = class {
86745
87147
  const result = await nexusShareStatus(this.projectRoot);
86746
87148
  return wrapResult(result, "query", "nexus", "share.status", startTime);
86747
87149
  }
87150
+ case "transfer.preview": {
87151
+ const taskIds = params?.taskIds;
87152
+ const sourceProject = params?.sourceProject;
87153
+ const targetProject = params?.targetProject;
87154
+ if (!taskIds?.length || !sourceProject || !targetProject) {
87155
+ return errorResult(
87156
+ "query",
87157
+ "nexus",
87158
+ operation,
87159
+ "E_INVALID_INPUT",
87160
+ "taskIds, sourceProject, and targetProject are required",
87161
+ startTime
87162
+ );
87163
+ }
87164
+ const result = await nexusTransferPreview({
87165
+ taskIds,
87166
+ sourceProject,
87167
+ targetProject,
87168
+ mode: params?.mode ?? "copy",
87169
+ scope: params?.scope ?? "subtree"
87170
+ });
87171
+ return wrapResult(result, "query", "nexus", operation, startTime);
87172
+ }
86748
87173
  default:
86749
87174
  return unsupportedOp("query", "nexus", operation, startTime);
86750
87175
  }
@@ -86867,6 +87292,31 @@ var NexusHandler = class {
86867
87292
  const result = await nexusShareSnapshotImport(this.projectRoot, inputPath);
86868
87293
  return wrapResult(result, "mutate", "nexus", "share.snapshot.import", startTime);
86869
87294
  }
87295
+ case "transfer": {
87296
+ const taskIds = params?.taskIds;
87297
+ const sourceProject = params?.sourceProject;
87298
+ const targetProject = params?.targetProject;
87299
+ if (!taskIds?.length || !sourceProject || !targetProject) {
87300
+ return errorResult(
87301
+ "mutate",
87302
+ "nexus",
87303
+ operation,
87304
+ "E_INVALID_INPUT",
87305
+ "taskIds, sourceProject, and targetProject are required",
87306
+ startTime
87307
+ );
87308
+ }
87309
+ const result = await nexusTransferExecute({
87310
+ taskIds,
87311
+ sourceProject,
87312
+ targetProject,
87313
+ mode: params?.mode ?? "copy",
87314
+ scope: params?.scope ?? "subtree",
87315
+ onConflict: params?.onConflict ?? "rename",
87316
+ transferBrain: params?.transferBrain ?? false
87317
+ });
87318
+ return wrapResult(result, "mutate", "nexus", operation, startTime);
87319
+ }
86870
87320
  default:
86871
87321
  return unsupportedOp("mutate", "nexus", operation, startTime);
86872
87322
  }
@@ -86895,7 +87345,8 @@ var NexusHandler = class {
86895
87345
  "blockers.show",
86896
87346
  "orphans.list",
86897
87347
  "discover",
86898
- "search"
87348
+ "search",
87349
+ "transfer.preview"
86899
87350
  ],
86900
87351
  mutate: [
86901
87352
  "share.snapshot.export",
@@ -86905,7 +87356,8 @@ var NexusHandler = class {
86905
87356
  "unregister",
86906
87357
  "sync",
86907
87358
  "permission.set",
86908
- "reconcile"
87359
+ "reconcile",
87360
+ "transfer"
86909
87361
  ]
86910
87362
  };
86911
87363
  }
@@ -90362,7 +90814,7 @@ function getConfig() {
90362
90814
 
90363
90815
  // packages/cleo/src/dispatch/middleware/audit.ts
90364
90816
  init_internal();
90365
- var log8 = getLogger("audit");
90817
+ var log9 = getLogger("audit");
90366
90818
  var cachedProjectHash;
90367
90819
  function resolveProjectHash() {
90368
90820
  if (cachedProjectHash !== void 0) return cachedProjectHash;
@@ -90393,9 +90845,9 @@ async function writeToSqlite(entry, requestId) {
90393
90845
  const { getDb: getDb4 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
90394
90846
  const { auditLog: auditLog2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
90395
90847
  const { AuditLogInsertSchema: AuditLogInsertSchema2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
90396
- const { randomUUID: randomUUID11 } = await import("node:crypto");
90848
+ const { randomUUID: randomUUID12 } = await import("node:crypto");
90397
90849
  const payload = {
90398
- id: randomUUID11(),
90850
+ id: randomUUID12(),
90399
90851
  timestamp: entry.timestamp,
90400
90852
  action: entry.operation,
90401
90853
  taskId: entry.metadata.taskId ?? "system",
@@ -90416,7 +90868,7 @@ async function writeToSqlite(entry, requestId) {
90416
90868
  };
90417
90869
  const parsed = AuditLogInsertSchema2.safeParse(payload);
90418
90870
  if (!parsed.success) {
90419
- log8.warn(
90871
+ log9.warn(
90420
90872
  { issues: parsed.error.issues },
90421
90873
  "Audit payload failed Zod validation; skipping insert"
90422
90874
  );
@@ -90425,7 +90877,7 @@ async function writeToSqlite(entry, requestId) {
90425
90877
  const db = await getDb4(process.cwd());
90426
90878
  await db.insert(auditLog2).values(parsed.data).run();
90427
90879
  } catch (err) {
90428
- log8.warn({ err }, "Failed to write audit entry to SQLite");
90880
+ log9.warn({ err }, "Failed to write audit entry to SQLite");
90429
90881
  }
90430
90882
  }
90431
90883
  function createAudit() {
@@ -90456,7 +90908,7 @@ function createAudit() {
90456
90908
  },
90457
90909
  error: response.error?.message
90458
90910
  };
90459
- log8.info(
90911
+ log9.info(
90460
90912
  {
90461
90913
  domain: entry.domain,
90462
90914
  operation: entry.operation,
@@ -90473,7 +90925,7 @@ function createAudit() {
90473
90925
  await writeToSqlite(entry, req.requestId);
90474
90926
  } else {
90475
90927
  writeToSqlite(entry, req.requestId).catch((err) => {
90476
- log8.error({ err }, "Failed to persist audit entry to SQLite");
90928
+ log9.error({ err }, "Failed to persist audit entry to SQLite");
90477
90929
  });
90478
90930
  }
90479
90931
  return response;
@@ -90954,7 +91406,7 @@ async function handleMcpToolCall(gateway, domain2, operation, params, requestId)
90954
91406
  operation,
90955
91407
  params,
90956
91408
  source: "mcp",
90957
- requestId: requestId || randomUUID9()
91409
+ requestId: requestId || randomUUID10()
90958
91410
  };
90959
91411
  return dispatcher.dispatch(req);
90960
91412
  }
@@ -91045,7 +91497,7 @@ function registerQueryTool() {
91045
91497
  }
91046
91498
 
91047
91499
  // packages/cleo/src/mcp/lib/background-jobs.ts
91048
- import { randomUUID as randomUUID10 } from "crypto";
91500
+ import { randomUUID as randomUUID11 } from "crypto";
91049
91501
  var BackgroundJobManager = class {
91050
91502
  jobs;
91051
91503
  abortControllers;
@@ -91078,7 +91530,7 @@ var BackgroundJobManager = class {
91078
91530
  `Maximum concurrent jobs reached (${this.maxJobs}). Cancel or wait for existing jobs to complete.`
91079
91531
  );
91080
91532
  }
91081
- const jobId = randomUUID10();
91533
+ const jobId = randomUUID11();
91082
91534
  const abortController = new AbortController();
91083
91535
  const job = {
91084
91536
  id: jobId,
@@ -91770,30 +92222,30 @@ async function main() {
91770
92222
  projectInfo?.projectHash
91771
92223
  );
91772
92224
  startupLog = getLogger("mcp:startup");
91773
- const log9 = startupLog;
92225
+ const log10 = startupLog;
91774
92226
  const cleoVersion = getCleoVersion();
91775
- log9.info(
92227
+ log10.info(
91776
92228
  { version: cleoVersion, projectHash: projectInfo?.projectHash, logLevel: config2.logLevel },
91777
92229
  "CLEO MCP server starting"
91778
92230
  );
91779
- log9.info(
92231
+ log10.info(
91780
92232
  { enableMetrics: config2.enableMetrics },
91781
92233
  `Metrics: ${config2.enableMetrics ? "enabled" : "disabled"}`
91782
92234
  );
91783
92235
  Promise.resolve().then(() => (init_internal(), internal_exports)).then(
91784
92236
  ({ loadConfig: loadCoreConfig }) => loadCoreConfig().then((coreConfig) => pruneAuditLog(cleoDir, coreConfig.logging))
91785
- ).catch((err) => log9.warn({ err }, "audit log pruning failed"));
91786
- log9.info("Initializing dispatch layer");
92237
+ ).catch((err) => log10.warn({ err }, "audit log pruning failed"));
92238
+ log10.info("Initializing dispatch layer");
91787
92239
  initMcpDispatcher({
91788
92240
  rateLimiting: config2.rateLimiting,
91789
92241
  strictMode: true
91790
92242
  });
91791
- log9.info("Dispatch layer initialized");
92243
+ log10.info("Dispatch layer initialized");
91792
92244
  const jobManager = new BackgroundJobManager({ maxJobs: 10, retentionMs: 36e5 });
91793
92245
  setJobManager(jobManager);
91794
- log9.info({ maxJobs: 10, retentionMs: 36e5 }, "Background job manager initialized");
92246
+ log10.info({ maxJobs: 10, retentionMs: 36e5 }, "Background job manager initialized");
91795
92247
  const cache = new QueryCache(config2.queryCacheTtl, config2.queryCache);
91796
- log9.info(
92248
+ log10.info(
91797
92249
  { enabled: config2.queryCache, ttlMs: config2.queryCacheTtl },
91798
92250
  `Query cache: ${config2.queryCache ? "enabled" : "disabled"}`
91799
92251
  );
@@ -91810,7 +92262,7 @@ async function main() {
91810
92262
  }
91811
92263
  );
91812
92264
  registerMemoryResources(server);
91813
- log9.info("Memory resources registered");
92265
+ log10.info("Memory resources registered");
91814
92266
  server.setRequestHandler(ListToolsRequestSchema, async () => {
91815
92267
  return {
91816
92268
  tools: [registerQueryTool(), registerMutateTool()]
@@ -92005,11 +92457,11 @@ async function main() {
92005
92457
  cache,
92006
92458
  jobManager
92007
92459
  };
92008
- log9.info("Connecting to stdio transport");
92460
+ log10.info("Connecting to stdio transport");
92009
92461
  const transport = new StdioServerTransport();
92010
92462
  await server.connect(transport);
92011
- log9.info({ transport: "stdio" }, "Server started successfully");
92012
- log9.info("Ready for requests");
92463
+ log10.info({ transport: "stdio" }, "Server started successfully");
92464
+ log10.info("Ready for requests");
92013
92465
  } catch (error40) {
92014
92466
  startupLog.fatal({ err: error40 }, "Failed to start server");
92015
92467
  process.exit(1);