@claude-flow/cli 3.19.0 → 3.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"neural.d.ts","sourceRoot":"","sources":["../../../src/commands/neural.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,aAAa,CAAC;AA2tI1E,eAAO,MAAM,aAAa,EAAE,OAmB3B,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"neural.d.ts","sourceRoot":"","sources":["../../../src/commands/neural.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,aAAa,CAAC;AA0wI1E,eAAO,MAAM,aAAa,EAAE,OAmB3B,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -24,6 +24,8 @@ const trainCommand = {
24
24
  { name: 'contrastive', type: 'boolean', description: 'Use contrastive learning (InfoNCE)', default: 'true' },
25
25
  { name: 'curriculum', type: 'boolean', description: 'Enable curriculum learning', default: 'false' },
26
26
  { name: 'backend', type: 'string', description: 'Training backend: auto (native when available), native (@ruvector/ruvllm TrainingPipeline, disk checkpoints), wasm (RuVector MicroLoRA/InfoNCE)', default: 'auto' },
27
+ { name: 'val-split', type: 'number', description: 'Validation holdout fraction 0..1 (native backend). >0 reports Best Val Loss + early stopping; 0 disables', default: '0.1' },
28
+ { name: 'resume', type: 'string', description: 'Resume native training from a checkpoint path (weights on 2.5.7; epoch position on >=2.6.0). Native backend only', default: '' },
27
29
  ],
28
30
  examples: [
29
31
  { command: 'claude-flow neural train -p coordination -e 100', description: 'Train coordination patterns' },
@@ -41,6 +43,17 @@ const trainCommand = {
41
43
  // 'wasm' = RuVector MicroLoRA/InfoNCE (pre-3.19 behavior),
42
44
  // 'auto' = native when the module resolves, else wasm.
43
45
  const backendFlag = String(ctx.flags.backend || 'auto');
46
+ // Feature: validation split + resume (native TrainingPipeline leg).
47
+ const valSplitRaw = parseFloat(ctx.flags['val-split'] ?? '0.1');
48
+ const valSplit = Number.isFinite(valSplitRaw) ? Math.max(0, Math.min(1, valSplitRaw)) : 0.1;
49
+ const resumePath = ctx.flags.resume ? String(ctx.flags.resume) : undefined;
50
+ // --resume is a native-only capability; refuse the WASM combination up
51
+ // front so the user gets a clear error rather than a silently-ignored flag.
52
+ if (resumePath && backendFlag === 'wasm') {
53
+ output.writeln();
54
+ output.writeln(output.error('--resume is only supported by the native backend; drop --backend wasm.'));
55
+ return { success: false, exitCode: 1 };
56
+ }
44
57
  const useWasm = ctx.flags.wasm !== false;
45
58
  const useFlash = ctx.flags.flash !== false;
46
59
  const useMoE = ctx.flags.moe === true;
@@ -195,18 +208,34 @@ const trainCommand = {
195
208
  const nativeTraining = await import('../services/native-training.js');
196
209
  const useNative = backendFlag === 'native'
197
210
  || (backendFlag === 'auto' && nativeTraining.nativeTrainingAvailable());
211
+ // --resume only works on the native pipeline; if native is unavailable
212
+ // (module absent), fail loudly rather than silently fresh-train.
213
+ if (resumePath && !useNative) {
214
+ spinner.fail('--resume requires the native @ruvector/ruvllm backend, which is not available');
215
+ return { success: false, exitCode: 1 };
216
+ }
198
217
  let nativeResult = null;
199
218
  if (useNative) {
200
219
  spinner.setText(`Training ${patternType} on native @ruvector/ruvllm pipeline...`);
201
220
  const path = await import('path');
202
- nativeResult = await nativeTraining.runNativeTraining({
203
- embeddings,
204
- epochs,
205
- batchSize,
206
- learningRate,
207
- dim,
208
- checkpointPath: path.join(process.cwd(), '.claude-flow', 'neural', `lora-checkpoint-${Date.now()}.json`),
209
- });
221
+ try {
222
+ nativeResult = await nativeTraining.runNativeTraining({
223
+ embeddings,
224
+ epochs,
225
+ batchSize,
226
+ learningRate,
227
+ dim,
228
+ validationSplit: valSplit,
229
+ resumeFrom: resumePath,
230
+ checkpointPath: path.join(process.cwd(), '.claude-flow', 'neural', `lora-checkpoint-${Date.now()}.json`),
231
+ });
232
+ }
233
+ catch (err) {
234
+ // ResumeFailedError — an explicit --resume that could not load is a
235
+ // loud, exit-1 failure, never a silent fall-through to fresh training.
236
+ spinner.fail(`Resume failed: ${err.message}`);
237
+ return { success: false, exitCode: 1 };
238
+ }
210
239
  if (!nativeResult && backendFlag === 'native') {
211
240
  spinner.fail('Native backend requested (--backend native) but @ruvector/ruvllm training failed');
212
241
  return { success: false, exitCode: 1 };
@@ -325,7 +354,20 @@ const trainCommand = {
325
354
  ];
326
355
  // Native pipeline metrics (#2549 — the LoRA leg trained on ruvllm)
327
356
  if (nativeResult) {
328
- tableData.push({ metric: 'Backend', value: 'native (@ruvector/ruvllm TrainingPipeline)' }, { metric: 'Native Steps', value: String(nativeResult.steps) }, { metric: 'Final Loss', value: nativeResult.finalLoss.toExponential(3) }, { metric: 'Early Stopped', value: nativeResult.earlyStopped ? 'yes' : 'no' });
357
+ tableData.push({ metric: 'Backend', value: 'native (@ruvector/ruvllm TrainingPipeline)' }, { metric: 'Native Steps', value: String(nativeResult.steps) }, { metric: 'Final Loss', value: nativeResult.finalLoss.toExponential(3) });
358
+ // Validation metrics only surface when a holdout actually ran
359
+ // (bestValLoss is non-null); Early Stopped is only meaningful then.
360
+ if (nativeResult.bestValLoss !== null && nativeResult.bestValLoss !== undefined) {
361
+ tableData.push({ metric: 'Best Val Loss', value: nativeResult.bestValLoss.toExponential(3) }, { metric: 'Early Stopped', value: nativeResult.earlyStopped ? 'yes' : 'no' });
362
+ }
363
+ if (nativeResult.resumed) {
364
+ tableData.push({
365
+ metric: 'Resumed',
366
+ value: nativeResult.resumeMode === 'resumeFrom'
367
+ ? `${resumePath} (epoch position restored)`
368
+ : `${resumePath} (weights only — epoch-position resume needs @ruvector/ruvllm >=2.6.0)`,
369
+ });
370
+ }
329
371
  if (nativeResult.checkpointPath) {
330
372
  tableData.push({
331
373
  metric: 'Checkpoint',
@@ -523,10 +565,14 @@ const statusCommand = {
523
565
  details: stats._trainingBackend === 'ruvllm'
524
566
  ? await (async () => {
525
567
  try {
526
- const { nativeCheckpointsSupported } = await import('../ruvector/lora-adapter.js');
527
- return nativeCheckpointsSupported()
568
+ const { nativeCheckpointsSupported, latestCheckpointInfo } = await import('../ruvector/lora-adapter.js');
569
+ // Most important info first (truncation-friendly): backend
570
+ // capability, then the newest checkpoint + age when one exists.
571
+ const base = nativeCheckpointsSupported()
528
572
  ? 'native @ruvector/ruvllm pipeline + disk checkpoints'
529
573
  : 'native @ruvector/ruvllm pipeline (checkpoints need >=2.5.7)';
574
+ const cp = latestCheckpointInfo();
575
+ return cp ? `${base} · latest: ${cp.filename} (${cp.ageLabel})` : base;
530
576
  }
531
577
  catch {
532
578
  return 'native @ruvector/ruvllm pipeline';