@cleocode/core 2026.3.44 → 2026.3.46

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.
Files changed (166) hide show
  1. package/dist/admin/import-tasks.d.ts +10 -2
  2. package/dist/admin/import-tasks.d.ts.map +1 -1
  3. package/dist/bootstrap.d.ts.map +1 -1
  4. package/dist/index.js +1890 -504
  5. package/dist/index.js.map +4 -4
  6. package/dist/init.d.ts.map +1 -1
  7. package/dist/injection.d.ts +1 -1
  8. package/dist/injection.d.ts.map +1 -1
  9. package/dist/internal.d.ts +3 -1
  10. package/dist/internal.d.ts.map +1 -1
  11. package/dist/nexus/index.d.ts +2 -0
  12. package/dist/nexus/index.d.ts.map +1 -1
  13. package/dist/nexus/transfer-types.d.ts +123 -0
  14. package/dist/nexus/transfer-types.d.ts.map +1 -0
  15. package/dist/nexus/transfer.d.ts +31 -0
  16. package/dist/nexus/transfer.d.ts.map +1 -0
  17. package/dist/routing/capability-matrix.d.ts +6 -4
  18. package/dist/routing/capability-matrix.d.ts.map +1 -1
  19. package/dist/scaffold.d.ts +16 -9
  20. package/dist/scaffold.d.ts.map +1 -1
  21. package/dist/skills/agents/install.d.ts.map +1 -1
  22. package/dist/skills/routing-table.d.ts +17 -16
  23. package/dist/skills/routing-table.d.ts.map +1 -1
  24. package/dist/skills/skill-paths.d.ts.map +1 -1
  25. package/dist/store/brain-sqlite.d.ts +4 -1
  26. package/dist/store/brain-sqlite.d.ts.map +1 -1
  27. package/dist/store/nexus-sqlite.d.ts +4 -1
  28. package/dist/store/nexus-sqlite.d.ts.map +1 -1
  29. package/dist/store/sqlite.d.ts +4 -1
  30. package/dist/store/sqlite.d.ts.map +1 -1
  31. package/dist/store/tasks-schema.d.ts +3 -3
  32. package/dist/store/tasks-schema.d.ts.map +1 -1
  33. package/dist/store/validation-schemas.d.ts +5 -4
  34. package/dist/store/validation-schemas.d.ts.map +1 -1
  35. package/dist/system/health.d.ts.map +1 -1
  36. package/dist/ui/index.d.ts +0 -1
  37. package/dist/ui/index.d.ts.map +1 -1
  38. package/package.json +9 -4
  39. package/schemas/adr-frontmatter.schema.json +72 -0
  40. package/schemas/agent-configs.schema.json +120 -0
  41. package/schemas/agent-registry.json +243 -0
  42. package/schemas/agent-registry.schema.json +132 -0
  43. package/schemas/archive/research-manifest.schema.json +257 -0
  44. package/schemas/archive.schema.json +450 -0
  45. package/schemas/brain-decision.schema.json +69 -0
  46. package/schemas/brain-learning.schema.json +57 -0
  47. package/schemas/brain-pattern.schema.json +72 -0
  48. package/schemas/config.schema.json +2606 -0
  49. package/schemas/context-state.schema.json +137 -0
  50. package/schemas/contribution.schema.json +722 -0
  51. package/schemas/critical-path.schema.json +246 -0
  52. package/schemas/deps-cache.schema.json +97 -0
  53. package/schemas/doctor-output.schema.json +283 -0
  54. package/schemas/error.schema.json +161 -0
  55. package/schemas/export-package.schema.json +375 -0
  56. package/schemas/global-config.schema.json +219 -0
  57. package/schemas/grade.schema.json +49 -0
  58. package/schemas/log.schema.json +250 -0
  59. package/schemas/metrics.schema.json +328 -0
  60. package/schemas/migrations.schema.json +150 -0
  61. package/schemas/nexus-registry.schema.json +90 -0
  62. package/schemas/operation-constitution.schema.json +438 -0
  63. package/schemas/output.schema.json +164 -0
  64. package/schemas/project-context.schema.json +164 -0
  65. package/schemas/project-info.schema.json +180 -0
  66. package/schemas/projects-registry.schema.json +107 -0
  67. package/schemas/protocol-frontmatter.schema.json +72 -0
  68. package/schemas/rcasd-consensus-report.schema.json +10 -0
  69. package/schemas/rcasd-evidence.schema.json +42 -0
  70. package/schemas/rcasd-gate-result.schema.json +46 -0
  71. package/schemas/rcasd-hitl-resolution.schema.json +10 -0
  72. package/schemas/rcasd-index.schema.json +10 -0
  73. package/schemas/rcasd-manifest.schema.json +10 -0
  74. package/schemas/rcasd-research-output.schema.json +10 -0
  75. package/schemas/rcasd-spec-frontmatter.schema.json +10 -0
  76. package/schemas/rcasd-stage-transition.schema.json +38 -0
  77. package/schemas/releases.schema.json +267 -0
  78. package/schemas/skills-manifest.schema.json +91 -0
  79. package/schemas/skillsmp.schema.json +208 -0
  80. package/schemas/spec-index.schema.json +196 -0
  81. package/schemas/system-flow-atlas.schema.json +125 -0
  82. package/src/__tests__/injection-chain.test.ts +11 -10
  83. package/src/__tests__/injection-mvi-tiers.test.ts +4 -2
  84. package/src/admin/import-tasks.ts +53 -29
  85. package/src/agents/__tests__/capacity.test.d.ts +7 -0
  86. package/src/agents/__tests__/capacity.test.d.ts.map +1 -0
  87. package/src/agents/__tests__/capacity.test.js +173 -0
  88. package/src/agents/__tests__/capacity.test.js.map +1 -0
  89. package/src/agents/__tests__/registry.test.d.ts +8 -0
  90. package/src/agents/__tests__/registry.test.d.ts.map +1 -0
  91. package/src/agents/__tests__/registry.test.js +348 -0
  92. package/src/agents/__tests__/registry.test.js.map +1 -0
  93. package/src/agents/__tests__/retry.test.d.ts +7 -0
  94. package/src/agents/__tests__/retry.test.d.ts.map +1 -0
  95. package/src/agents/__tests__/retry.test.js +225 -0
  96. package/src/agents/__tests__/retry.test.js.map +1 -0
  97. package/src/bootstrap.ts +3 -1
  98. package/src/init.ts +63 -18
  99. package/src/injection.ts +11 -5
  100. package/src/intelligence/__tests__/impact.test.d.ts +15 -0
  101. package/src/intelligence/__tests__/impact.test.d.ts.map +1 -0
  102. package/src/intelligence/__tests__/impact.test.js +384 -0
  103. package/src/intelligence/__tests__/impact.test.js.map +1 -0
  104. package/src/intelligence/__tests__/patterns.test.d.ts +8 -0
  105. package/src/intelligence/__tests__/patterns.test.d.ts.map +1 -0
  106. package/src/intelligence/__tests__/patterns.test.js +370 -0
  107. package/src/intelligence/__tests__/patterns.test.js.map +1 -0
  108. package/src/intelligence/__tests__/prediction.test.d.ts +8 -0
  109. package/src/intelligence/__tests__/prediction.test.d.ts.map +1 -0
  110. package/src/intelligence/__tests__/prediction.test.js +314 -0
  111. package/src/intelligence/__tests__/prediction.test.js.map +1 -0
  112. package/src/internal.ts +7 -1
  113. package/src/nexus/__tests__/nexus-e2e.test.d.ts +12 -0
  114. package/src/nexus/__tests__/nexus-e2e.test.d.ts.map +1 -0
  115. package/src/nexus/__tests__/nexus-e2e.test.js +1220 -0
  116. package/src/nexus/__tests__/nexus-e2e.test.js.map +1 -0
  117. package/src/nexus/__tests__/transfer.test.d.ts +8 -0
  118. package/src/nexus/__tests__/transfer.test.d.ts.map +1 -0
  119. package/src/nexus/__tests__/transfer.test.js +372 -0
  120. package/src/nexus/__tests__/transfer.test.js.map +1 -0
  121. package/src/nexus/__tests__/transfer.test.ts +446 -0
  122. package/src/nexus/index.ts +14 -0
  123. package/src/nexus/transfer-types.ts +129 -0
  124. package/src/nexus/transfer.ts +314 -0
  125. package/src/routing/capability-matrix.ts +1435 -205
  126. package/src/scaffold.ts +18 -11
  127. package/src/skills/__tests__/routing-table.test.ts +53 -33
  128. package/src/skills/agents/install.ts +9 -1
  129. package/src/skills/routing-table.ts +39 -253
  130. package/src/skills/skill-paths.ts +3 -2
  131. package/src/store/__tests__/project-detect.test.ts +1 -1
  132. package/src/store/brain-sqlite.ts +7 -3
  133. package/src/store/nexus-sqlite.ts +7 -3
  134. package/src/store/sqlite.ts +9 -3
  135. package/src/store/tasks-schema.ts +1 -1
  136. package/src/system/health.ts +18 -7
  137. package/src/ui/index.ts +0 -6
  138. package/src/validation/operation-gate-validators.ts +2 -2
  139. package/templates/CLEO-INJECTION.md +120 -0
  140. package/templates/README.md +29 -0
  141. package/templates/agent-registry.json +305 -0
  142. package/templates/cleo-gitignore +74 -0
  143. package/templates/config.template.json +187 -0
  144. package/templates/git-hooks/commit-msg +149 -0
  145. package/templates/git-hooks/pre-commit +40 -0
  146. package/templates/git-hooks/pre-push +79 -0
  147. package/templates/github/ISSUE_TEMPLATE/bug_report.yml +143 -0
  148. package/templates/github/ISSUE_TEMPLATE/config.yml +8 -0
  149. package/templates/github/ISSUE_TEMPLATE/feature_request.yml +125 -0
  150. package/templates/github/ISSUE_TEMPLATE/help_question.yml +99 -0
  151. package/templates/global-config.template.json +56 -0
  152. package/templates/hooks/precompact-safestop.sh +89 -0
  153. package/templates/issue-templates/bug_report.yml +143 -0
  154. package/templates/issue-templates/config.yml +8 -0
  155. package/templates/issue-templates/feature_request.yml +125 -0
  156. package/templates/issue-templates/help_question.yml +99 -0
  157. package/templates/skillsmp.json.example +28 -0
  158. package/templates/skillsmp.json.example.md +214 -0
  159. package/dist/ui/injection-legacy.d.ts +0 -26
  160. package/dist/ui/injection-legacy.d.ts.map +0 -1
  161. package/src/ui/__tests__/injection-registry.test.d.ts +0 -11
  162. package/src/ui/__tests__/injection-registry.test.d.ts.map +0 -1
  163. package/src/ui/__tests__/injection-registry.test.js +0 -46
  164. package/src/ui/__tests__/injection-registry.test.js.map +0 -1
  165. package/src/ui/__tests__/injection-registry.test.ts +0 -57
  166. package/src/ui/injection-legacy.ts +0 -44
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Cross-project task transfer engine for NEXUS.
3
+ *
4
+ * Provides executeTransfer() and previewTransfer() for moving/copying
5
+ * tasks between registered NEXUS projects with full provenance tracking.
6
+ *
7
+ * @task T046, T049, T050, T051, T052, T053
8
+ * @epic T4540
9
+ */
10
+
11
+ import { randomUUID } from 'node:crypto';
12
+ import { importFromPackage } from '../admin/import-tasks.js';
13
+ import { getLogger } from '../logger.js';
14
+ import { createLink } from '../reconciliation/link-store.js';
15
+ import { BrainDataAccessor } from '../store/brain-accessor.js';
16
+ import { getBrainDb } from '../store/brain-sqlite.js';
17
+ import { getAccessor } from '../store/data-accessor.js';
18
+ import { exportSingle, exportSubtree } from '../store/export.js';
19
+ import { requirePermission } from './permissions.js';
20
+ import { nexusGetProject } from './registry.js';
21
+ import type {
22
+ TransferManifest,
23
+ TransferManifestEntry,
24
+ TransferParams,
25
+ TransferResult,
26
+ } from './transfer-types.js';
27
+
28
+ const log = getLogger('nexus:transfer');
29
+
30
+ /**
31
+ * Preview a transfer without writing any data.
32
+ * Validates projects, permissions, and builds the manifest.
33
+ */
34
+ export async function previewTransfer(params: TransferParams): Promise<TransferResult> {
35
+ return executeTransferInternal({ ...params, dryRun: true });
36
+ }
37
+
38
+ /**
39
+ * Execute a cross-project task transfer.
40
+ *
41
+ * Pipeline:
42
+ * 1. Validate source/target projects via nexusGetProject()
43
+ * 2. Check permissions: read on source, write on target
44
+ * 3. Read source tasks
45
+ * 4. Build ExportPackage via exportSubtree/exportSingle
46
+ * 5. Import into target via importFromPackage()
47
+ * 6. Create bidirectional external_task_links
48
+ * 7. Write nexus_audit_log entry
49
+ * 8. If move mode: archive source tasks
50
+ * 9. If transferBrain: copy brain observations
51
+ */
52
+ export async function executeTransfer(params: TransferParams): Promise<TransferResult> {
53
+ return executeTransferInternal(params);
54
+ }
55
+
56
+ async function executeTransferInternal(params: TransferParams): Promise<TransferResult> {
57
+ const {
58
+ taskIds,
59
+ sourceProject: sourceProjectRef,
60
+ targetProject: targetProjectRef,
61
+ mode = 'copy',
62
+ scope = 'subtree',
63
+ onConflict = 'rename',
64
+ onMissingDep = 'strip',
65
+ provenance = true,
66
+ targetParent,
67
+ transferBrain = false,
68
+ dryRun = false,
69
+ } = params;
70
+
71
+ if (!taskIds.length) {
72
+ throw new Error('No task IDs specified for transfer');
73
+ }
74
+
75
+ // Step 1: Validate projects
76
+ const sourceProject = await nexusGetProject(sourceProjectRef);
77
+ if (!sourceProject) {
78
+ throw new Error(`Source project not found: ${sourceProjectRef}`);
79
+ }
80
+
81
+ const targetProject = await nexusGetProject(targetProjectRef);
82
+ if (!targetProject) {
83
+ throw new Error(`Target project not found: ${targetProjectRef}`);
84
+ }
85
+
86
+ if (sourceProject.hash === targetProject.hash) {
87
+ throw new Error('Source and target projects must be different');
88
+ }
89
+
90
+ // Step 2: Check permissions
91
+ await requirePermission(sourceProject.hash, 'read', 'nexus.transfer');
92
+ await requirePermission(targetProject.hash, 'write', 'nexus.transfer');
93
+
94
+ // Step 3: Read source tasks
95
+ const sourceAccessor = await getAccessor(sourceProject.path);
96
+ const { tasks: allSourceTasks } = await sourceAccessor.queryTasks({});
97
+
98
+ // Step 4: Build ExportPackage for each task
99
+ const exportPackages = [];
100
+ for (const taskId of taskIds) {
101
+ const pkg =
102
+ scope === 'subtree'
103
+ ? exportSubtree(taskId, allSourceTasks, sourceProject.name)
104
+ : exportSingle(taskId, allSourceTasks, sourceProject.name);
105
+
106
+ if (!pkg) {
107
+ throw new Error(`Task not found in source project: ${taskId}`);
108
+ }
109
+ exportPackages.push(pkg);
110
+ }
111
+
112
+ // Merge all packages into one (dedup by task ID)
113
+ const seenIds = new Set<string>();
114
+ const mergedTasks = [];
115
+ for (const pkg of exportPackages) {
116
+ for (const task of pkg.tasks) {
117
+ if (!seenIds.has(task.id)) {
118
+ seenIds.add(task.id);
119
+ mergedTasks.push(task);
120
+ }
121
+ }
122
+ }
123
+
124
+ // Use the first package as base and replace tasks
125
+ const mergedPkg = { ...exportPackages[0]! };
126
+ mergedPkg.tasks = mergedTasks;
127
+ mergedPkg._meta = { ...mergedPkg._meta, taskCount: mergedTasks.length };
128
+
129
+ // Step 5: Import into target
130
+ const importResult = await importFromPackage(mergedPkg, {
131
+ cwd: targetProject.path,
132
+ dryRun,
133
+ parent: targetParent,
134
+ provenance,
135
+ onConflict,
136
+ onMissingDep,
137
+ });
138
+
139
+ // Build manifest
140
+ const entries: TransferManifestEntry[] = mergedTasks.map((t) => ({
141
+ sourceId: t.id,
142
+ targetId: importResult.idRemap[t.id] ?? t.id,
143
+ title: t.title,
144
+ type: t.type ?? 'task',
145
+ }));
146
+
147
+ const manifest: TransferManifest = {
148
+ sourceProject: sourceProject.name,
149
+ targetProject: targetProject.name,
150
+ mode,
151
+ scope,
152
+ entries,
153
+ idRemap: importResult.idRemap,
154
+ brainObservationsTransferred: 0,
155
+ };
156
+
157
+ const result: TransferResult = {
158
+ dryRun,
159
+ transferred: importResult.imported,
160
+ skipped: importResult.skipped,
161
+ archived: 0,
162
+ linksCreated: 0,
163
+ brainObservationsTransferred: 0,
164
+ manifest,
165
+ };
166
+
167
+ if (dryRun) {
168
+ return result;
169
+ }
170
+
171
+ // Step 6: Create bidirectional external_task_links
172
+ // Only create links for tasks that were actually imported (verify they exist in target)
173
+ let linksCreated = 0;
174
+ const targetAccessor = await getAccessor(targetProject.path);
175
+ const { tasks: targetTasks } = await targetAccessor.queryTasks({});
176
+ const targetTaskIds = new Set(targetTasks.map((t) => t.id));
177
+
178
+ for (const entry of entries) {
179
+ if (importResult.idRemap[entry.sourceId] && targetTaskIds.has(entry.targetId)) {
180
+ // Link in target: points back to source
181
+ await createLink(
182
+ {
183
+ taskId: entry.targetId,
184
+ providerId: `nexus:${sourceProject.name}`,
185
+ externalId: entry.sourceId,
186
+ externalTitle: entry.title,
187
+ linkType: 'transferred',
188
+ syncDirection: 'inbound',
189
+ metadata: {
190
+ transferMode: mode,
191
+ transferScope: scope,
192
+ sourceProject: sourceProject.name,
193
+ transferredAt: new Date().toISOString(),
194
+ },
195
+ },
196
+ targetProject.path,
197
+ );
198
+ linksCreated++;
199
+
200
+ // Link in source: points to target
201
+ await createLink(
202
+ {
203
+ taskId: entry.sourceId,
204
+ providerId: `nexus:${targetProject.name}`,
205
+ externalId: entry.targetId,
206
+ externalTitle: entry.title,
207
+ linkType: 'transferred',
208
+ syncDirection: 'outbound',
209
+ metadata: {
210
+ transferMode: mode,
211
+ transferScope: scope,
212
+ targetProject: targetProject.name,
213
+ transferredAt: new Date().toISOString(),
214
+ },
215
+ },
216
+ sourceProject.path,
217
+ );
218
+ linksCreated++;
219
+ }
220
+ }
221
+ result.linksCreated = linksCreated;
222
+
223
+ // Step 7: Write audit log
224
+ try {
225
+ const { getNexusDb } = await import('../store/nexus-sqlite.js');
226
+ const { nexusAuditLog } = await import('../store/nexus-schema.js');
227
+ const db = await getNexusDb();
228
+ await db.insert(nexusAuditLog).values({
229
+ id: randomUUID(),
230
+ action: 'transfer',
231
+ projectHash: sourceProject.hash,
232
+ projectId: sourceProject.projectId,
233
+ domain: 'nexus',
234
+ operation: 'transfer',
235
+ success: 1,
236
+ detailsJson: JSON.stringify({
237
+ sourceProject: sourceProject.name,
238
+ targetProject: targetProject.name,
239
+ mode,
240
+ scope,
241
+ taskCount: result.transferred,
242
+ idRemap: importResult.idRemap,
243
+ }),
244
+ });
245
+ } catch (err) {
246
+ log.warn({ err }, 'nexus transfer audit write failed');
247
+ }
248
+
249
+ // Step 8: Move mode — archive source tasks
250
+ if (mode === 'move') {
251
+ let archived = 0;
252
+ for (const entry of entries) {
253
+ if (importResult.idRemap[entry.sourceId]) {
254
+ try {
255
+ await sourceAccessor.archiveSingleTask(entry.sourceId, {
256
+ archivedAt: new Date().toISOString(),
257
+ archiveReason: `Transferred to ${targetProject.name} as ${entry.targetId}`,
258
+ });
259
+ archived++;
260
+ } catch (err) {
261
+ log.warn({ err, taskId: entry.sourceId }, 'failed to archive source task after transfer');
262
+ }
263
+ }
264
+ }
265
+ result.archived = archived;
266
+ }
267
+
268
+ // Step 9: Brain observation transfer
269
+ if (transferBrain) {
270
+ let brainTransferred = 0;
271
+ try {
272
+ const sourceBrainDb = await getBrainDb(sourceProject.path);
273
+ const targetBrainDb = await getBrainDb(targetProject.path);
274
+ const sourceBrain = new BrainDataAccessor(sourceBrainDb);
275
+ const targetBrain = new BrainDataAccessor(targetBrainDb);
276
+
277
+ for (const entry of entries) {
278
+ if (!importResult.idRemap[entry.sourceId]) continue;
279
+
280
+ const links = await sourceBrain.getLinksForTask(entry.sourceId);
281
+ for (const link of links) {
282
+ if (link.memoryType !== 'observation') continue;
283
+
284
+ const observation = await sourceBrain.getObservation(link.memoryId);
285
+ if (!observation) continue;
286
+
287
+ const newObsId = `O-${randomUUID().slice(0, 8)}`;
288
+ await targetBrain.addObservation({
289
+ ...observation,
290
+ id: newObsId,
291
+ createdAt: observation.createdAt,
292
+ updatedAt: new Date().toISOString().replace('T', ' ').slice(0, 19),
293
+ });
294
+
295
+ await targetBrain.addLink({
296
+ memoryType: 'observation',
297
+ memoryId: newObsId,
298
+ taskId: entry.targetId,
299
+ linkType: 'applies_to',
300
+ createdAt: new Date().toISOString().replace('T', ' ').slice(0, 19),
301
+ });
302
+
303
+ brainTransferred++;
304
+ }
305
+ }
306
+ } catch (err) {
307
+ log.warn({ err }, 'brain observation transfer failed');
308
+ }
309
+ result.brainObservationsTransferred = brainTransferred;
310
+ result.manifest.brainObservationsTransferred = brainTransferred;
311
+ }
312
+
313
+ return result;
314
+ }