agileflow 2.43.0 → 2.45.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.
@@ -50,6 +50,22 @@ class Installer {
50
50
  this.packageRoot = getPackageRoot();
51
51
  }
52
52
 
53
+ /**
54
+ * Clean up existing content directories before installing.
55
+ * Removes agents/, commands/, skills/, templates/ but preserves _cfg/ and config.yaml.
56
+ * @param {string} agileflowDir - AgileFlow installation directory
57
+ */
58
+ async cleanup(agileflowDir) {
59
+ const dirsToRemove = ['agents', 'commands', 'skills', 'templates'];
60
+
61
+ for (const dir of dirsToRemove) {
62
+ const dirPath = path.join(agileflowDir, dir);
63
+ if (await fs.pathExists(dirPath)) {
64
+ await fs.remove(dirPath);
65
+ }
66
+ }
67
+ }
68
+
53
69
  /**
54
70
  * Install AgileFlow to a project
55
71
  * @param {Object} config - Installation configuration
@@ -91,10 +107,13 @@ class Installer {
91
107
  backupPath = await this.createBackup(agileflowDir, cfgDir, timestamp);
92
108
  }
93
109
 
94
- const fileIndex = existingFileIndex || { schema: 1, files: {} };
95
- if (!fileIndex.files || typeof fileIndex.files !== 'object') {
96
- fileIndex.files = {};
97
- }
110
+ // Clean up existing content directories to remove stale files
111
+ // This happens AFTER backup so we can restore if needed
112
+ spinner.text = 'Cleaning up old content...';
113
+ await this.cleanup(agileflowDir);
114
+
115
+ // Reset file index since we removed all content - start fresh
116
+ const fileIndex = { schema: 1, files: {} };
98
117
 
99
118
  const fileOps = {
100
119
  created: 0,
@@ -132,6 +151,10 @@ class Installer {
132
151
  });
133
152
  }
134
153
 
154
+ // Copy essential scripts to user's scripts/ directory
155
+ spinner.text = 'Installing scripts...';
156
+ await this.installScripts(directory, { force: effectiveForce });
157
+
135
158
  // Create config.yaml
136
159
  spinner.text = 'Creating configuration...';
137
160
  await this.createConfig(agileflowDir, userName, agileflowFolder, { force: effectiveForce });
@@ -575,6 +598,76 @@ class Installer {
575
598
  return counts;
576
599
  }
577
600
 
601
+ /**
602
+ * Install all scripts from packages/cli/scripts/ to user's project scripts/ directory
603
+ * Copies everything automatically - no manual list to maintain
604
+ * @param {string} directory - Project directory
605
+ * @param {Object} options - Installation options
606
+ * @param {boolean} options.force - Overwrite existing scripts
607
+ */
608
+ async installScripts(directory, options = {}) {
609
+ const { force = false } = options;
610
+ const scriptsSourceDir = path.join(this.packageRoot, 'scripts');
611
+ const scriptsDestDir = path.join(directory, 'scripts');
612
+
613
+ // Skip if source scripts directory doesn't exist
614
+ if (!(await fs.pathExists(scriptsSourceDir))) {
615
+ return;
616
+ }
617
+
618
+ // Ensure destination scripts directory exists
619
+ await fs.ensureDir(scriptsDestDir);
620
+
621
+ // Copy all scripts recursively
622
+ await this.copyScriptsRecursive(scriptsSourceDir, scriptsDestDir, force);
623
+ }
624
+
625
+ /**
626
+ * Recursively copy scripts from source to destination
627
+ * @param {string} srcDir - Source directory
628
+ * @param {string} destDir - Destination directory
629
+ * @param {boolean} force - Overwrite existing files
630
+ */
631
+ async copyScriptsRecursive(srcDir, destDir, force) {
632
+ const entries = await fs.readdir(srcDir, { withFileTypes: true });
633
+
634
+ for (const entry of entries) {
635
+ const srcPath = path.join(srcDir, entry.name);
636
+ const destPath = path.join(destDir, entry.name);
637
+
638
+ if (entry.isDirectory()) {
639
+ // Recursively copy subdirectories
640
+ await fs.ensureDir(destPath);
641
+ await this.copyScriptsRecursive(srcPath, destPath, force);
642
+ } else {
643
+ // Copy file
644
+ const destExists = await fs.pathExists(destPath);
645
+
646
+ // If destination exists and not forcing, check if identical
647
+ if (destExists && !force) {
648
+ const srcContent = await fs.readFile(srcPath);
649
+ const destContent = await fs.readFile(destPath);
650
+ if (srcContent.equals(destContent)) {
651
+ continue; // Identical, skip
652
+ }
653
+ // Different content but not forcing, skip to preserve user changes
654
+ continue;
655
+ }
656
+
657
+ // Copy the file
658
+ await fs.copy(srcPath, destPath);
659
+
660
+ // Make executable on Unix for shell scripts and JS files
661
+ if (process.platform !== 'win32') {
662
+ const ext = path.extname(entry.name).toLowerCase();
663
+ if (['.sh', '.js'].includes(ext)) {
664
+ await fs.chmod(destPath, 0o755);
665
+ }
666
+ }
667
+ }
668
+ }
669
+ }
670
+
578
671
  /**
579
672
  * Get installation status
580
673
  * @param {string} directory - Project directory