@comfanion/workflow 4.36.17 → 4.36.19

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/bin/cli.js CHANGED
@@ -6,6 +6,7 @@ import inquirer from 'inquirer';
6
6
  import ora from 'ora';
7
7
  import fs from 'fs-extra';
8
8
  import path from 'path';
9
+ import crypto from 'crypto';
9
10
  import { fileURLToPath } from 'url';
10
11
  import { execSync } from 'child_process';
11
12
  import yaml from 'js-yaml';
@@ -274,16 +275,27 @@ program
274
275
  const vectorizerDir = path.join(targetDir, 'vectorizer');
275
276
  const vectorsDir = path.join(targetDir, 'vectors');
276
277
  const tempVectors = path.join(process.cwd(), '.vectors-temp');
278
+ const tempNodeModules = path.join(process.cwd(), '.vectorizer-nm-temp');
277
279
 
278
280
  let hadVectors = false;
281
+ let hadVectorizerModules = false;
282
+ let oldPkgHash = null;
279
283
  let existingConfigContent = null;
280
284
 
281
285
  if (await fs.pathExists(targetDir)) {
282
286
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
283
287
  const backupDir = path.join(process.cwd(), `.opencode.backup-${timestamp}`);
284
288
 
285
- // Check what we need to preserve (only vectors, vectorizer will be reinstalled fresh)
289
+ // Check what we need to preserve
286
290
  hadVectors = await fs.pathExists(vectorsDir);
291
+ hadVectorizerModules = await fs.pathExists(path.join(vectorizerDir, 'node_modules'));
292
+
293
+ // Get old package.json hash
294
+ const oldPkgPath = path.join(vectorizerDir, 'package.json');
295
+ if (await fs.pathExists(oldPkgPath)) {
296
+ const oldPkg = await fs.readFile(oldPkgPath, 'utf8');
297
+ oldPkgHash = crypto.createHash('md5').update(oldPkg).digest('hex');
298
+ }
287
299
 
288
300
  // Read existing config.yaml for merge
289
301
  const existingConfigPath = path.join(targetDir, 'config.yaml');
@@ -291,12 +303,18 @@ program
291
303
  existingConfigContent = await fs.readFile(existingConfigPath, 'utf8');
292
304
  }
293
305
 
294
- // Preserve vector indexes only (not node_modules - will reinstall fresh)
306
+ // Preserve vector indexes
295
307
  if (hadVectors) {
296
308
  spinner.text = 'Preserving vector indexes...';
297
309
  await fs.move(vectorsDir, tempVectors, { overwrite: true });
298
310
  }
299
311
 
312
+ // Preserve vectorizer node_modules
313
+ if (hadVectorizerModules) {
314
+ spinner.text = 'Preserving vectorizer cache...';
315
+ await fs.move(path.join(vectorizerDir, 'node_modules'), tempNodeModules, { overwrite: true });
316
+ }
317
+
300
318
  // Create backup (without node_modules and vectors)
301
319
  spinner.text = 'Creating backup...';
302
320
  await fs.copy(targetDir, backupDir, {
@@ -314,41 +332,59 @@ program
314
332
  // Copy .opencode structure (fresh, no old files)
315
333
  await fs.copy(OPENCODE_SRC, targetDir);
316
334
 
317
- // Copy vectorizer source files (always copy, install only if enabled)
335
+ // Copy vectorizer source files
318
336
  if (await fs.pathExists(VECTORIZER_SRC)) {
319
337
  const newVectorizerDir = path.join(targetDir, 'vectorizer');
320
338
  await fs.ensureDir(newVectorizerDir);
321
339
  await fs.copy(path.join(VECTORIZER_SRC, 'index.js'), path.join(newVectorizerDir, 'index.js'));
322
340
  await fs.copy(path.join(VECTORIZER_SRC, 'package.json'), path.join(newVectorizerDir, 'package.json'));
323
341
 
324
- // Install dependencies only if vectorizer is enabled
325
342
  if (config.vectorizer_enabled) {
326
- spinner.text = 'Installing vectorizer dependencies...';
327
- try {
328
- // Show progress during npm install
329
- const steps = [
330
- 'Installing vectorizer dependencies...',
331
- 'Downloading @xenova/transformers (~50MB)...',
332
- 'Installing vectordb...',
333
- 'Installing glob...',
334
- 'Finalizing vectorizer setup...'
335
- ];
336
- let stepIndex = 0;
337
- const progressInterval = setInterval(() => {
338
- stepIndex = (stepIndex + 1) % steps.length;
339
- spinner.text = steps[stepIndex];
340
- }, 3000);
341
-
342
- execSync('npm install', {
343
- cwd: newVectorizerDir,
344
- stdio: 'pipe',
345
- timeout: 180000
346
- });
347
-
348
- clearInterval(progressInterval);
349
- spinner.text = 'Vectorizer installed!';
350
- } catch (e) {
351
- // Non-fatal, will show warning below
343
+ // Check if package.json changed
344
+ const newPkgPath = path.join(newVectorizerDir, 'package.json');
345
+ const newPkg = await fs.readFile(newPkgPath, 'utf8');
346
+ const newPkgHash = crypto.createHash('md5').update(newPkg).digest('hex');
347
+
348
+ if (hadVectorizerModules && oldPkgHash === newPkgHash) {
349
+ // Dependencies unchanged - restore cached node_modules
350
+ spinner.text = 'Restoring vectorizer cache (deps unchanged)...';
351
+ await fs.move(tempNodeModules, path.join(newVectorizerDir, 'node_modules'), { overwrite: true });
352
+ } else {
353
+ // Dependencies changed or new install
354
+ spinner.text = 'Installing vectorizer dependencies...';
355
+ if (await fs.pathExists(tempNodeModules)) {
356
+ await fs.remove(tempNodeModules);
357
+ }
358
+ try {
359
+ const steps = [
360
+ 'Installing vectorizer dependencies...',
361
+ 'Downloading @xenova/transformers (~50MB)...',
362
+ 'Installing vectordb...',
363
+ 'Installing glob...',
364
+ 'Finalizing vectorizer setup...'
365
+ ];
366
+ let stepIndex = 0;
367
+ const progressInterval = setInterval(() => {
368
+ stepIndex = (stepIndex + 1) % steps.length;
369
+ spinner.text = steps[stepIndex];
370
+ }, 3000);
371
+
372
+ execSync('npm install', {
373
+ cwd: newVectorizerDir,
374
+ stdio: 'pipe',
375
+ timeout: 180000
376
+ });
377
+
378
+ clearInterval(progressInterval);
379
+ spinner.text = 'Vectorizer installed!';
380
+ } catch (e) {
381
+ // Non-fatal
382
+ }
383
+ }
384
+ } else {
385
+ // Vectorizer disabled - clean up temp
386
+ if (await fs.pathExists(tempNodeModules)) {
387
+ await fs.remove(tempNodeModules);
352
388
  }
353
389
  }
354
390
  }
@@ -576,8 +612,17 @@ program
576
612
  spinner.text = 'Reading config.yaml...';
577
613
  const configBackup = await fs.readFile(configPath, 'utf8');
578
614
 
579
- // Check if vectors exist (we preserve only indexes, reinstall vectorizer fresh)
615
+ // Check what exists
580
616
  const hasVectors = await fs.pathExists(vectorsDir);
617
+ const hasVectorizerModules = await fs.pathExists(path.join(vectorizerDir, 'node_modules'));
618
+
619
+ // Get old package.json hash (to check if deps changed)
620
+ let oldPkgHash = null;
621
+ const oldPkgPath = path.join(vectorizerDir, 'package.json');
622
+ if (await fs.pathExists(oldPkgPath)) {
623
+ const oldPkg = await fs.readFile(oldPkgPath, 'utf8');
624
+ oldPkgHash = crypto.createHash('md5').update(oldPkg).digest('hex');
625
+ }
581
626
 
582
627
  // Create full backup (unless --no-backup)
583
628
  if (options.backup !== false) {
@@ -587,13 +632,20 @@ program
587
632
  });
588
633
  }
589
634
 
590
- // Preserve only vector indexes (not node_modules - will reinstall fresh)
635
+ // Preserve vector indexes
591
636
  const tempVectors = path.join(process.cwd(), '.vectors-temp');
592
637
  if (hasVectors) {
593
638
  spinner.text = 'Preserving vector indexes...';
594
639
  await fs.move(vectorsDir, tempVectors, { overwrite: true });
595
640
  }
596
641
 
642
+ // Preserve vectorizer node_modules (will restore if package.json unchanged)
643
+ const tempNodeModules = path.join(process.cwd(), '.vectorizer-nm-temp');
644
+ if (hasVectorizerModules) {
645
+ spinner.text = 'Preserving vectorizer cache...';
646
+ await fs.move(path.join(vectorizerDir, 'node_modules'), tempNodeModules, { overwrite: true });
647
+ }
648
+
597
649
  // Remove old .opencode directory
598
650
  spinner.text = 'Removing old files...';
599
651
  await fs.remove(targetDir);
@@ -602,7 +654,7 @@ program
602
654
  spinner.text = 'Installing new version...';
603
655
  await fs.copy(OPENCODE_SRC, targetDir);
604
656
 
605
- // Copy vectorizer source files (always copy, install only if enabled)
657
+ // Copy vectorizer source files
606
658
  if (await fs.pathExists(VECTORIZER_SRC)) {
607
659
  const newVectorizerDir = path.join(targetDir, 'vectorizer');
608
660
  await fs.ensureDir(newVectorizerDir);
@@ -612,34 +664,53 @@ program
612
664
  // Check if vectorizer is enabled in user's config
613
665
  const vectorizerEnabled = /vectorizer:[\s\S]*?enabled:\s*true/i.test(configBackup);
614
666
 
615
- // Install dependencies only if vectorizer is enabled
616
667
  if (vectorizerEnabled) {
617
- spinner.text = 'Installing vectorizer dependencies...';
618
- try {
619
- // Show progress during npm install
620
- const steps = [
621
- 'Installing vectorizer dependencies...',
622
- 'Downloading @xenova/transformers (~50MB)...',
623
- 'Installing vectordb...',
624
- 'Installing glob...',
625
- 'Finalizing vectorizer setup...'
626
- ];
627
- let stepIndex = 0;
628
- const progressInterval = setInterval(() => {
629
- stepIndex = (stepIndex + 1) % steps.length;
630
- spinner.text = steps[stepIndex];
631
- }, 3000);
632
-
633
- execSync('npm install', {
634
- cwd: newVectorizerDir,
635
- stdio: 'pipe',
636
- timeout: 180000
637
- });
638
-
639
- clearInterval(progressInterval);
640
- spinner.text = 'Vectorizer installed!';
641
- } catch (e) {
642
- // Non-fatal, will show warning below
668
+ // Check if package.json changed
669
+ const newPkgPath = path.join(newVectorizerDir, 'package.json');
670
+ const newPkg = await fs.readFile(newPkgPath, 'utf8');
671
+ const newPkgHash = crypto.createHash('md5').update(newPkg).digest('hex');
672
+
673
+ if (hasVectorizerModules && oldPkgHash === newPkgHash) {
674
+ // Dependencies unchanged - restore cached node_modules
675
+ spinner.text = 'Restoring vectorizer cache (deps unchanged)...';
676
+ await fs.move(tempNodeModules, path.join(newVectorizerDir, 'node_modules'), { overwrite: true });
677
+ } else {
678
+ // Dependencies changed or new install - run npm install
679
+ spinner.text = 'Installing vectorizer dependencies...';
680
+ // Clean up temp if exists
681
+ if (await fs.pathExists(tempNodeModules)) {
682
+ await fs.remove(tempNodeModules);
683
+ }
684
+ try {
685
+ const steps = [
686
+ 'Installing vectorizer dependencies...',
687
+ 'Downloading @xenova/transformers (~50MB)...',
688
+ 'Installing vectordb...',
689
+ 'Installing glob...',
690
+ 'Finalizing vectorizer setup...'
691
+ ];
692
+ let stepIndex = 0;
693
+ const progressInterval = setInterval(() => {
694
+ stepIndex = (stepIndex + 1) % steps.length;
695
+ spinner.text = steps[stepIndex];
696
+ }, 3000);
697
+
698
+ execSync('npm install', {
699
+ cwd: newVectorizerDir,
700
+ stdio: 'pipe',
701
+ timeout: 180000
702
+ });
703
+
704
+ clearInterval(progressInterval);
705
+ spinner.text = 'Vectorizer installed!';
706
+ } catch (e) {
707
+ // Non-fatal
708
+ }
709
+ }
710
+ } else {
711
+ // Vectorizer disabled - clean up temp
712
+ if (await fs.pathExists(tempNodeModules)) {
713
+ await fs.remove(tempNodeModules);
643
714
  }
644
715
  }
645
716
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfanion/workflow",
3
- "version": "4.36.17",
3
+ "version": "4.36.19",
4
4
  "description": "Initialize OpenCode Workflow system for AI-assisted development with semantic code search",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "3.0.0",
3
- "buildDate": "2026-01-24T16:04:01.348Z",
3
+ "buildDate": "2026-01-24T16:08:15.479Z",
4
4
  "files": [
5
5
  "config.yaml",
6
6
  "FLOW.yaml",