agileflow 2.33.1 → 2.34.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "2.33.1",
3
+ "version": "2.34.0",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -0,0 +1,284 @@
1
+ /**
2
+ * AgileFlow CLI - Config Command
3
+ *
4
+ * Manage AgileFlow configuration without re-running setup.
5
+ */
6
+
7
+ const chalk = require('chalk');
8
+ const path = require('node:path');
9
+ const fs = require('fs-extra');
10
+ const yaml = require('js-yaml');
11
+ const { Installer } = require('../installers/core/installer');
12
+ const { IdeManager } = require('../installers/ide/manager');
13
+ const { displayLogo, displaySection, success, warning, error, info } = require('../lib/ui');
14
+
15
+ const installer = new Installer();
16
+ const ideManager = new IdeManager();
17
+
18
+ module.exports = {
19
+ name: 'config',
20
+ description: 'Manage AgileFlow configuration',
21
+ arguments: [
22
+ ['<subcommand>', 'Subcommand: list, get, set'],
23
+ ['[key]', 'Config key (for get/set)'],
24
+ ['[value]', 'Config value (for set)'],
25
+ ],
26
+ options: [
27
+ ['-d, --directory <path>', 'Project directory (default: current directory)'],
28
+ ],
29
+ action: async (subcommand, keyOrValue, valueOrUndefined, options) => {
30
+ try {
31
+ const directory = path.resolve(options.directory || '.');
32
+
33
+ // Get installation status
34
+ const status = await installer.getStatus(directory);
35
+
36
+ if (!status.installed) {
37
+ displayLogo();
38
+ warning('No AgileFlow installation found');
39
+ console.log(chalk.dim(`\nRun 'npx agileflow setup' to set up AgileFlow\n`));
40
+ process.exit(1);
41
+ }
42
+
43
+ const manifestPath = path.join(status.path, '_cfg', 'manifest.yaml');
44
+
45
+ // Handle subcommands
46
+ switch (subcommand) {
47
+ case 'list':
48
+ await handleList(status);
49
+ break;
50
+
51
+ case 'get':
52
+ await handleGet(status, keyOrValue);
53
+ break;
54
+
55
+ case 'set':
56
+ await handleSet(directory, status, manifestPath, keyOrValue, valueOrUndefined);
57
+ break;
58
+
59
+ default:
60
+ displayLogo();
61
+ console.log(chalk.bold('Usage:\n'));
62
+ console.log(' npx agileflow config list');
63
+ console.log(' npx agileflow config get <key>');
64
+ console.log(' npx agileflow config set <key> <value>\n');
65
+ console.log(chalk.bold('Keys:\n'));
66
+ console.log(' userName Your name for config files');
67
+ console.log(' ides Comma-separated IDE list (claude-code,cursor,windsurf)');
68
+ console.log(' agileflowFolder AgileFlow folder name (e.g., .agileflow)');
69
+ console.log(' docsFolder Documentation folder name (e.g., docs)\n');
70
+ console.log(chalk.bold('Examples:\n'));
71
+ console.log(' npx agileflow config get userName');
72
+ console.log(' npx agileflow config set userName "Jane Developer"');
73
+ console.log(' npx agileflow config set ides "claude-code,cursor"\n');
74
+ process.exit(0);
75
+ }
76
+
77
+ process.exit(0);
78
+ } catch (err) {
79
+ console.error(chalk.red('Error:'), err.message);
80
+ if (process.env.DEBUG) {
81
+ console.error(err.stack);
82
+ }
83
+ process.exit(1);
84
+ }
85
+ },
86
+ };
87
+
88
+ /**
89
+ * Handle list subcommand
90
+ */
91
+ async function handleList(status) {
92
+ displayLogo();
93
+ displaySection('AgileFlow Configuration');
94
+
95
+ console.log(chalk.bold('User Settings:'));
96
+ console.log(` userName: ${chalk.cyan(status.userName || 'Developer')}`);
97
+ console.log();
98
+
99
+ console.log(chalk.bold('IDE Settings:'));
100
+ const ides = status.ides || ['claude-code'];
101
+ console.log(` ides: ${chalk.cyan(ides.join(', '))}`);
102
+ console.log();
103
+
104
+ console.log(chalk.bold('Folder Settings:'));
105
+ console.log(` agileflowFolder: ${chalk.cyan(status.agileflowFolder || '.agileflow')}`);
106
+ console.log(` docsFolder: ${chalk.cyan(status.docsFolder || 'docs')}`);
107
+ console.log();
108
+
109
+ console.log(chalk.bold('System Info:'));
110
+ console.log(` version: ${chalk.cyan(status.version || 'unknown')}`);
111
+ console.log(` installed: ${chalk.cyan(status.installedAt ? new Date(status.installedAt).toLocaleDateString() : 'unknown')}`);
112
+ console.log(` updated: ${chalk.cyan(status.updatedAt ? new Date(status.updatedAt).toLocaleDateString() : 'unknown')}`);
113
+ console.log();
114
+ }
115
+
116
+ /**
117
+ * Handle get subcommand
118
+ */
119
+ async function handleGet(status, key) {
120
+ if (!key) {
121
+ error('Missing key. Usage: npx agileflow config get <key>');
122
+ process.exit(1);
123
+ }
124
+
125
+ const validKeys = ['userName', 'ides', 'agileflowFolder', 'docsFolder', 'version'];
126
+
127
+ if (!validKeys.includes(key)) {
128
+ error(`Invalid key: ${key}`);
129
+ console.log(chalk.dim(`Valid keys: ${validKeys.join(', ')}\n`));
130
+ process.exit(1);
131
+ }
132
+
133
+ let value;
134
+ switch (key) {
135
+ case 'userName':
136
+ value = status.userName || 'Developer';
137
+ break;
138
+ case 'ides':
139
+ value = (status.ides || ['claude-code']).join(',');
140
+ break;
141
+ case 'agileflowFolder':
142
+ value = status.agileflowFolder || '.agileflow';
143
+ break;
144
+ case 'docsFolder':
145
+ value = status.docsFolder || 'docs';
146
+ break;
147
+ case 'version':
148
+ value = status.version || 'unknown';
149
+ break;
150
+ }
151
+
152
+ console.log(value);
153
+ }
154
+
155
+ /**
156
+ * Handle set subcommand
157
+ */
158
+ async function handleSet(directory, status, manifestPath, key, value) {
159
+ if (!key || value === undefined) {
160
+ error('Missing arguments. Usage: npx agileflow config set <key> <value>');
161
+ process.exit(1);
162
+ }
163
+
164
+ const validKeys = ['userName', 'ides', 'agileflowFolder', 'docsFolder'];
165
+
166
+ if (!validKeys.includes(key)) {
167
+ error(`Invalid key: ${key}`);
168
+ console.log(chalk.dim(`Valid keys: ${validKeys.join(', ')}\n`));
169
+ process.exit(1);
170
+ }
171
+
172
+ displayLogo();
173
+ displaySection('Updating Configuration');
174
+
175
+ // Read current manifest
176
+ const manifestContent = await fs.readFile(manifestPath, 'utf8');
177
+ const manifest = yaml.load(manifestContent);
178
+
179
+ // Track if we need to update IDE configs
180
+ let needsIdeUpdate = false;
181
+ const oldIdes = manifest.ides || [];
182
+
183
+ // Update the value
184
+ switch (key) {
185
+ case 'userName':
186
+ manifest.user_name = value;
187
+ info(`Setting userName to: ${chalk.cyan(value)}`);
188
+ break;
189
+
190
+ case 'ides':
191
+ const newIdes = value.split(',').map((ide) => ide.trim());
192
+ const validIdes = ['claude-code', 'cursor', 'windsurf'];
193
+
194
+ // Validate IDEs
195
+ for (const ide of newIdes) {
196
+ if (!validIdes.includes(ide)) {
197
+ error(`Invalid IDE: ${ide}`);
198
+ console.log(chalk.dim(`Valid IDEs: ${validIdes.join(', ')}\n`));
199
+ process.exit(1);
200
+ }
201
+ }
202
+
203
+ manifest.ides = newIdes;
204
+ needsIdeUpdate = true;
205
+ info(`Setting ides to: ${chalk.cyan(newIdes.join(', '))}`);
206
+ break;
207
+
208
+ case 'agileflowFolder':
209
+ warning('Changing agileflowFolder requires moving the installation directory.');
210
+ console.log(chalk.dim('This change will only update the config - you must move files manually.\n'));
211
+ manifest.agileflow_folder = value;
212
+ info(`Setting agileflowFolder to: ${chalk.cyan(value)}`);
213
+ break;
214
+
215
+ case 'docsFolder':
216
+ manifest.docs_folder = value;
217
+ info(`Setting docsFolder to: ${chalk.cyan(value)}`);
218
+ break;
219
+ }
220
+
221
+ // Update timestamp
222
+ manifest.updated_at = new Date().toISOString();
223
+
224
+ // Write manifest
225
+ await fs.writeFile(manifestPath, yaml.dump(manifest), 'utf8');
226
+ success('Configuration updated');
227
+
228
+ // Update IDE configs if needed
229
+ if (needsIdeUpdate) {
230
+ console.log();
231
+ info('Updating IDE configurations...');
232
+
233
+ ideManager.setAgileflowFolder(manifest.agileflow_folder || '.agileflow');
234
+ ideManager.setDocsFolder(manifest.docs_folder || 'docs');
235
+
236
+ // Remove old IDE configs
237
+ for (const ide of oldIdes) {
238
+ if (!manifest.ides.includes(ide)) {
239
+ const configPath = getIdeConfigPath(directory, ide);
240
+ if (await fs.pathExists(configPath)) {
241
+ await fs.remove(configPath);
242
+ info(`Removed ${formatIdeName(ide)} configuration`);
243
+ }
244
+ }
245
+ }
246
+
247
+ // Add new IDE configs
248
+ for (const ide of manifest.ides) {
249
+ await ideManager.setup(ide, directory, status.path);
250
+ success(`Updated ${formatIdeName(ide)} configuration`);
251
+ }
252
+
253
+ console.log();
254
+ success('IDE configurations updated');
255
+ }
256
+
257
+ console.log();
258
+ }
259
+
260
+ /**
261
+ * Get IDE config path
262
+ */
263
+ function getIdeConfigPath(projectDir, ide) {
264
+ const paths = {
265
+ 'claude-code': '.claude/commands/AgileFlow',
266
+ 'cursor': '.cursor/rules/agileflow',
267
+ 'windsurf': '.windsurf/workflows/agileflow',
268
+ };
269
+
270
+ return path.join(projectDir, paths[ide] || '');
271
+ }
272
+
273
+ /**
274
+ * Format IDE name for display
275
+ */
276
+ function formatIdeName(ide) {
277
+ const names = {
278
+ 'claude-code': 'Claude Code',
279
+ 'cursor': 'Cursor',
280
+ 'windsurf': 'Windsurf',
281
+ };
282
+
283
+ return names[ide] || ide;
284
+ }
@@ -8,7 +8,8 @@ const chalk = require('chalk');
8
8
  const path = require('node:path');
9
9
  const fs = require('fs-extra');
10
10
  const { Installer } = require('../installers/core/installer');
11
- const { displayLogo, displaySection, success, warning, error, info } = require('../lib/ui');
11
+ const { displayLogo, displaySection, success, warning, error, info, confirm } = require('../lib/ui');
12
+ const { IdeManager } = require('../installers/ide/manager');
12
13
  const { getCurrentVersion } = require('../lib/version-checker');
13
14
 
14
15
  const installer = new Installer();
@@ -18,16 +19,18 @@ module.exports = {
18
19
  description: 'Diagnose AgileFlow installation issues',
19
20
  options: [
20
21
  ['-d, --directory <path>', 'Project directory (default: current directory)'],
22
+ ['--fix', 'Automatically fix detected issues'],
21
23
  ],
22
24
  action: async (options) => {
23
25
  try {
24
26
  const directory = path.resolve(options.directory || '.');
25
27
 
26
28
  displayLogo();
27
- displaySection('AgileFlow Diagnostics');
29
+ displaySection(options.fix ? 'AgileFlow Auto-Repair' : 'AgileFlow Diagnostics');
28
30
 
29
31
  let issues = 0;
30
32
  let warnings = 0;
33
+ const repairs = []; // Track fixable issues
31
34
 
32
35
  // Check Node.js version
33
36
  console.log(chalk.bold('Environment:'));
@@ -73,16 +76,44 @@ module.exports = {
73
76
  } else {
74
77
  error('manifest.yaml missing');
75
78
  issues++;
79
+ repairs.push({
80
+ type: 'missing-manifest',
81
+ message: 'Recreate missing manifest.yaml',
82
+ fix: async () => {
83
+ info('Recreating manifest.yaml...');
84
+ const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
85
+ const cfgDir = path.join(status.path, '_cfg');
86
+ await fs.ensureDir(cfgDir);
87
+
88
+ const yaml = require('js-yaml');
89
+ const manifest = {
90
+ version: packageJson.version,
91
+ installed_at: new Date().toISOString(),
92
+ updated_at: new Date().toISOString(),
93
+ ides: status.ides || ['claude-code'],
94
+ modules: ['core'],
95
+ user_name: status.userName || 'Developer',
96
+ agileflow_folder: path.basename(status.path),
97
+ docs_folder: 'docs',
98
+ };
99
+
100
+ await fs.writeFile(manifestPath, yaml.dump(manifest), 'utf8');
101
+ success('Created manifest.yaml');
102
+ },
103
+ });
76
104
  }
77
105
 
78
106
  // Check core content
79
107
  const counts = await installer.countInstalledItems(status.path);
80
108
 
109
+ let missingCore = false;
110
+
81
111
  if (counts.agents > 0) {
82
112
  success(`Core agents: ${counts.agents} files`);
83
113
  } else {
84
114
  error('Core agents: Missing');
85
115
  issues++;
116
+ missingCore = true;
86
117
  }
87
118
 
88
119
  if (counts.commands > 0) {
@@ -90,6 +121,27 @@ module.exports = {
90
121
  } else {
91
122
  error('Commands: Missing');
92
123
  issues++;
124
+ missingCore = true;
125
+ }
126
+
127
+ if (missingCore) {
128
+ repairs.push({
129
+ type: 'missing-core',
130
+ message: 'Reinstall missing core content',
131
+ fix: async () => {
132
+ info('Reinstalling core content...');
133
+ const config = {
134
+ directory,
135
+ ides: status.ides || ['claude-code'],
136
+ userName: status.userName || 'Developer',
137
+ agileflowFolder: path.basename(status.path),
138
+ docsFolder: status.docsFolder || 'docs',
139
+ };
140
+
141
+ await installer.install(config);
142
+ success('Reinstalled core content');
143
+ },
144
+ });
93
145
  }
94
146
 
95
147
  if (counts.skills > 0) {
@@ -103,6 +155,10 @@ module.exports = {
103
155
  if (status.ides && status.ides.length > 0) {
104
156
  console.log(chalk.bold('\nIDE Configurations:'));
105
157
 
158
+ const ideManager = new IdeManager();
159
+ ideManager.setAgileflowFolder(path.basename(status.path));
160
+ ideManager.setDocsFolder(status.docsFolder || 'docs');
161
+
106
162
  for (const ide of status.ides) {
107
163
  const configPath = getIdeConfigPath(directory, ide);
108
164
  const ideName = formatIdeName(ide);
@@ -112,8 +168,17 @@ module.exports = {
112
168
  const files = await countFilesInDir(configPath);
113
169
  success(`${ideName}: ${files} files`);
114
170
  } else {
115
- warning(`${ideName}: Config missing (run 'npx agileflow update')`);
171
+ warning(`${ideName}: Config missing`);
116
172
  warnings++;
173
+ repairs.push({
174
+ type: 'missing-ide-config',
175
+ message: `Reinstall ${ideName} configuration`,
176
+ fix: async () => {
177
+ info(`Reinstalling ${ideName} configuration...`);
178
+ await ideManager.setup(ide, directory, status.path);
179
+ success(`Reinstalled ${ideName} configuration`);
180
+ },
181
+ });
117
182
  }
118
183
  }
119
184
  }
@@ -127,9 +192,19 @@ module.exports = {
127
192
  if (!status.ides || !status.ides.includes(ide)) {
128
193
  const configPath = getIdeConfigPath(directory, ide);
129
194
  if (await fs.pathExists(configPath)) {
130
- warning(`${formatIdeName(ide)}: Config exists but not in manifest`);
195
+ const ideName = formatIdeName(ide);
196
+ warning(`${ideName}: Config exists but not in manifest`);
131
197
  orphansFound = true;
132
198
  warnings++;
199
+ repairs.push({
200
+ type: 'orphaned-config',
201
+ message: `Remove orphaned ${ideName} configuration`,
202
+ fix: async () => {
203
+ info(`Removing orphaned ${ideName} configuration...`);
204
+ await fs.remove(configPath);
205
+ success(`Removed ${ideName} configuration`);
206
+ },
207
+ });
133
208
  }
134
209
  }
135
210
  }
@@ -138,6 +213,26 @@ module.exports = {
138
213
  success('No orphaned configurations');
139
214
  }
140
215
 
216
+ // Execute repairs if --fix is enabled
217
+ if (options.fix && repairs.length > 0) {
218
+ console.log();
219
+ displaySection('Applying Fixes');
220
+
221
+ for (const repair of repairs) {
222
+ try {
223
+ await repair.fix();
224
+ } catch (err) {
225
+ error(`Failed to ${repair.message.toLowerCase()}: ${err.message}`);
226
+ }
227
+ }
228
+
229
+ console.log();
230
+ success(`Applied ${repairs.length} fix(es)`);
231
+ } else if (repairs.length > 0 && !options.fix) {
232
+ console.log();
233
+ info(`Found ${repairs.length} fixable issue(s). Run with --fix to auto-repair.`);
234
+ }
235
+
141
236
  // Print summary
142
237
  printSummary(issues, warnings);
143
238
 
@@ -6,10 +6,12 @@
6
6
 
7
7
  const chalk = require('chalk');
8
8
  const path = require('node:path');
9
+ const semver = require('semver');
9
10
  const { Installer } = require('../installers/core/installer');
10
11
  const { IdeManager } = require('../installers/ide/manager');
11
12
  const { displayLogo, displaySection, success, warning, error, info, confirm } = require('../lib/ui');
12
13
  const { createDocsStructure, getDocsFolderName } = require('../lib/docs-setup');
14
+ const { getLatestVersion } = require('../lib/npm-utils');
13
15
 
14
16
  const installer = new Installer();
15
17
  const ideManager = new IdeManager();
@@ -38,21 +40,49 @@ module.exports = {
38
40
 
39
41
  displaySection('Updating AgileFlow', `Current version: ${status.version}`);
40
42
 
41
- // Get package version
43
+ // Get local CLI version and npm registry version
42
44
  const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
43
- const newVersion = packageJson.version;
45
+ const localCliVersion = packageJson.version;
44
46
 
45
- console.log(chalk.bold('Current: '), status.version);
46
- console.log(chalk.bold('Latest: '), newVersion);
47
+ console.log(chalk.dim('Checking npm registry for latest version...'));
48
+ const npmLatestVersion = await getLatestVersion('agileflow');
47
49
 
48
- if (status.version === newVersion && !options.force) {
50
+ if (!npmLatestVersion) {
51
+ warning('Could not check npm registry for latest version');
52
+ console.log(chalk.dim('Continuing with local CLI version...\n'));
53
+ }
54
+
55
+ const latestVersion = npmLatestVersion || localCliVersion;
56
+
57
+ console.log(chalk.bold('Installed: '), status.version);
58
+ console.log(chalk.bold('CLI version: '), localCliVersion);
59
+ if (npmLatestVersion) {
60
+ console.log(chalk.bold('Latest (npm):'), npmLatestVersion);
61
+ }
62
+
63
+ // Check if CLI itself is outdated
64
+ if (npmLatestVersion && semver.lt(localCliVersion, npmLatestVersion)) {
65
+ console.log();
66
+ warning('Your CLI is outdated!');
67
+ console.log(chalk.dim(` To update your installation, run:\n`));
68
+ console.log(chalk.cyan(` npx agileflow@latest update\n`));
69
+
70
+ const useOutdated = await confirm('Continue with outdated CLI anyway?');
71
+ if (!useOutdated) {
72
+ console.log(chalk.dim('\nUpdate cancelled\n'));
73
+ process.exit(0);
74
+ }
75
+ }
76
+
77
+ // Check if project installation is up to date
78
+ if (status.version === latestVersion && !options.force) {
49
79
  success('Already on the latest version');
50
80
  process.exit(0);
51
81
  }
52
82
 
53
83
  // Confirm update
54
84
  if (!options.force) {
55
- const proceed = await confirm(`Update to v${newVersion}?`);
85
+ const proceed = await confirm(`Update to v${latestVersion}?`);
56
86
  if (!proceed) {
57
87
  console.log(chalk.dim('\nUpdate cancelled\n'));
58
88
  process.exit(0);
@@ -64,13 +94,13 @@ module.exports = {
64
94
  // Get docs folder name from metadata (or default to 'docs')
65
95
  const docsFolder = await getDocsFolderName(directory);
66
96
 
67
- // Re-run installation with existing config
97
+ // Re-run installation with existing config from manifest
68
98
  const config = {
69
99
  directory,
70
100
  ides: status.ides || ['claude-code'],
71
- userName: 'Developer', // Could read from existing config
72
- agileflowFolder: path.basename(status.path),
73
- docsFolder,
101
+ userName: status.userName || 'Developer',
102
+ agileflowFolder: status.agileflowFolder || path.basename(status.path),
103
+ docsFolder: status.docsFolder || docsFolder,
74
104
  };
75
105
 
76
106
  // Run core installation
@@ -102,7 +132,7 @@ module.exports = {
102
132
  }
103
133
  }
104
134
 
105
- console.log(chalk.green(`\n✨ Update complete! (${status.version} → ${newVersion})\n`));
135
+ console.log(chalk.green(`\n✨ Update complete! (${status.version} → ${latestVersion})\n`));
106
136
 
107
137
  process.exit(0);
108
138
  } catch (err) {
@@ -41,7 +41,7 @@ class Installer {
41
41
  * @returns {Promise<Object>} Installation result
42
42
  */
43
43
  async install(config) {
44
- const { directory, ides, userName, agileflowFolder } = config;
44
+ const { directory, ides, userName, agileflowFolder, docsFolder } = config;
45
45
 
46
46
  const agileflowDir = path.join(directory, agileflowFolder);
47
47
  const spinner = ora('Installing AgileFlow...').start();
@@ -72,7 +72,7 @@ class Installer {
72
72
 
73
73
  // Create manifest
74
74
  spinner.text = 'Creating manifest...';
75
- await this.createManifest(cfgDir, ides);
75
+ await this.createManifest(cfgDir, {ides, userName, agileflowFolder, docsFolder});
76
76
 
77
77
  // Count installed items
78
78
  const counts = await this.countInstalledItems(agileflowDir);
@@ -200,9 +200,10 @@ class Installer {
200
200
  /**
201
201
  * Create manifest file
202
202
  * @param {string} cfgDir - Config directory
203
- * @param {string[]} ides - Selected IDEs
203
+ * @param {Object} config - Manifest configuration
204
204
  */
205
- async createManifest(cfgDir, ides) {
205
+ async createManifest(cfgDir, config) {
206
+ const { ides, userName, agileflowFolder, docsFolder } = config;
206
207
  const packageJson = require(path.join(this.packageRoot, 'package.json'));
207
208
 
208
209
  const manifest = {
@@ -211,6 +212,9 @@ class Installer {
211
212
  updated_at: new Date().toISOString(),
212
213
  ides: ides,
213
214
  modules: ['core'],
215
+ user_name: userName,
216
+ agileflow_folder: agileflowFolder || '.agileflow',
217
+ docs_folder: docsFolder || 'docs',
214
218
  };
215
219
 
216
220
  const manifestPath = path.join(cfgDir, 'manifest.yaml');
@@ -265,6 +269,11 @@ class Installer {
265
269
  version: null,
266
270
  ides: [],
267
271
  modules: [],
272
+ userName: null,
273
+ agileflowFolder: null,
274
+ docsFolder: null,
275
+ installedAt: null,
276
+ updatedAt: null,
268
277
  };
269
278
 
270
279
  // Look for AgileFlow installation
@@ -284,6 +293,11 @@ class Installer {
284
293
  status.version = manifest.version;
285
294
  status.ides = manifest.ides || [];
286
295
  status.modules = manifest.modules || [];
296
+ status.userName = manifest.user_name || 'Developer';
297
+ status.agileflowFolder = manifest.agileflow_folder || folder;
298
+ status.docsFolder = manifest.docs_folder || 'docs';
299
+ status.installedAt = manifest.installed_at;
300
+ status.updatedAt = manifest.updated_at;
287
301
 
288
302
  break;
289
303
  }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * AgileFlow CLI - npm Registry Utilities
3
+ *
4
+ * Utilities for interacting with the npm registry.
5
+ */
6
+
7
+ const https = require('https');
8
+
9
+ /**
10
+ * Get the latest version of a package from npm registry
11
+ * @param {string} packageName - Name of the package
12
+ * @returns {Promise<string|null>} Latest version or null if error
13
+ */
14
+ async function getLatestVersion(packageName) {
15
+ return new Promise((resolve) => {
16
+ const options = {
17
+ hostname: 'registry.npmjs.org',
18
+ port: 443,
19
+ path: `/${packageName}/latest`,
20
+ method: 'GET',
21
+ headers: {
22
+ 'User-Agent': 'agileflow-cli',
23
+ },
24
+ };
25
+
26
+ const req = https.request(options, (res) => {
27
+ let data = '';
28
+
29
+ res.on('data', (chunk) => {
30
+ data += chunk;
31
+ });
32
+
33
+ res.on('end', () => {
34
+ try {
35
+ if (res.statusCode === 200) {
36
+ const json = JSON.parse(data);
37
+ resolve(json.version || null);
38
+ } else {
39
+ resolve(null);
40
+ }
41
+ } catch (err) {
42
+ resolve(null);
43
+ }
44
+ });
45
+ });
46
+
47
+ req.on('error', () => {
48
+ resolve(null);
49
+ });
50
+
51
+ req.setTimeout(5000, () => {
52
+ req.destroy();
53
+ resolve(null);
54
+ });
55
+
56
+ req.end();
57
+ });
58
+ }
59
+
60
+ module.exports = {
61
+ getLatestVersion,
62
+ };
@@ -31,6 +31,19 @@ function shouldSkipInstall() {
31
31
  return true;
32
32
  }
33
33
 
34
+ // Skip in CI environments
35
+ if (process.env.CI === 'true' || process.env.CI === '1') {
36
+ log('ℹ️ Skipping auto-install (CI environment detected)', 'dim');
37
+ return true;
38
+ }
39
+
40
+ // Skip if installed globally
41
+ if (process.env.npm_config_global === 'true') {
42
+ log('ℹ️ Skipping auto-install (global installation)', 'dim');
43
+ log(' Run "agileflow setup" in your project directory instead', 'dim');
44
+ return true;
45
+ }
46
+
34
47
  // Skip if running in npm cache or npx temp directory
35
48
  if (__dirname.includes('_npx') || __dirname.includes('.npm')) {
36
49
  log('ℹ️ Skipping auto-install (npm cache or npx temp directory)', 'dim');
@@ -90,7 +103,7 @@ function shouldSkipInstall() {
90
103
  return false;
91
104
  }
92
105
 
93
- function runAutoInstall() {
106
+ async function runAutoInstall() {
94
107
  try {
95
108
  console.log(''); // Blank line for spacing
96
109
  log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'blue');
@@ -98,9 +111,6 @@ function runAutoInstall() {
98
111
  log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'blue');
99
112
  console.log('');
100
113
 
101
- log('🚀 Setting up AgileFlow in your project...', 'green');
102
- console.log('');
103
-
104
114
  // Path to the CLI
105
115
  const cliPath = path.join(__dirname, 'cli', 'agileflow-cli.js');
106
116
 
@@ -109,11 +119,57 @@ function runAutoInstall() {
109
119
  return;
110
120
  }
111
121
 
112
- // Run setup command with --yes flag (non-interactive)
113
- execSync(`node "${cliPath}" setup --yes`, {
114
- stdio: 'inherit',
115
- cwd: process.cwd(),
116
- });
122
+ // Check if running in interactive mode (TTY)
123
+ const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
124
+
125
+ if (isInteractive) {
126
+ // Interactive mode: ask for confirmation
127
+ console.log('AgileFlow can set up automatically in your project now.');
128
+ console.log('');
129
+ console.log('Options:');
130
+ console.log(' 1) Run setup now (recommended)');
131
+ console.log(' 2) Skip setup for now (run "npx agileflow setup" later)');
132
+ console.log('');
133
+
134
+ // Use readline for simple input
135
+ const readline = require('readline').createInterface({
136
+ input: process.stdin,
137
+ output: process.stdout,
138
+ });
139
+
140
+ const answer = await new Promise((resolve) => {
141
+ readline.question('Choose an option (1 or 2): ', (ans) => {
142
+ readline.close();
143
+ resolve(ans.trim());
144
+ });
145
+ });
146
+
147
+ console.log('');
148
+
149
+ if (answer !== '1') {
150
+ log('ℹ️ Setup skipped. Run "npx agileflow setup" when ready.', 'dim');
151
+ console.log('');
152
+ return;
153
+ }
154
+
155
+ log('🚀 Setting up AgileFlow in your project...', 'green');
156
+ console.log('');
157
+
158
+ // Run setup command (interactive mode, no --yes flag)
159
+ execSync(`node "${cliPath}" setup`, {
160
+ stdio: 'inherit',
161
+ cwd: process.cwd(),
162
+ });
163
+ } else {
164
+ // Non-interactive mode (e.g., npm install in scripts): run with --yes
165
+ log('🚀 Setting up AgileFlow in your project...', 'green');
166
+ console.log('');
167
+
168
+ execSync(`node "${cliPath}" setup --yes`, {
169
+ stdio: 'inherit',
170
+ cwd: process.cwd(),
171
+ });
172
+ }
117
173
 
118
174
  console.log('');
119
175
  log('✨ AgileFlow is ready to use!', 'green');
@@ -134,8 +190,10 @@ function runAutoInstall() {
134
190
  }
135
191
 
136
192
  // Main execution
137
- if (shouldSkipInstall()) {
138
- process.exit(0);
139
- }
193
+ (async () => {
194
+ if (shouldSkipInstall()) {
195
+ process.exit(0);
196
+ }
140
197
 
141
- runAutoInstall();
198
+ await runAutoInstall();
199
+ })();