agileflow 2.33.0 → 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 +1 -1
- package/tools/cli/commands/config.js +284 -0
- package/tools/cli/commands/doctor.js +100 -5
- package/tools/cli/commands/status.js +1 -1
- package/tools/cli/commands/update.js +42 -12
- package/tools/cli/installers/core/installer.js +18 -4
- package/tools/cli/lib/npm-utils.js +62 -0
- package/tools/postinstall.js +75 -17
package/package.json
CHANGED
|
@@ -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:'));
|
|
@@ -57,7 +60,7 @@ module.exports = {
|
|
|
57
60
|
|
|
58
61
|
if (!status.installed) {
|
|
59
62
|
warning('No AgileFlow installation found');
|
|
60
|
-
console.log(chalk.dim(` Run 'npx agileflow
|
|
63
|
+
console.log(chalk.dim(` Run 'npx agileflow setup' to set up\n`));
|
|
61
64
|
|
|
62
65
|
// Summary
|
|
63
66
|
printSummary(issues, warnings);
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
|
@@ -31,7 +31,7 @@ module.exports = {
|
|
|
31
31
|
|
|
32
32
|
if (!status.installed) {
|
|
33
33
|
warning('No AgileFlow installation found in this directory');
|
|
34
|
-
console.log(chalk.dim(`\nRun 'npx agileflow
|
|
34
|
+
console.log(chalk.dim(`\nRun 'npx agileflow setup' to set up AgileFlow\n`));
|
|
35
35
|
process.exit(0);
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -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();
|
|
@@ -32,27 +34,55 @@ module.exports = {
|
|
|
32
34
|
|
|
33
35
|
if (!status.installed) {
|
|
34
36
|
warning('No AgileFlow installation found');
|
|
35
|
-
console.log(chalk.dim(`\nRun 'npx agileflow
|
|
37
|
+
console.log(chalk.dim(`\nRun 'npx agileflow setup' to set up AgileFlow\n`));
|
|
36
38
|
process.exit(1);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
displaySection('Updating AgileFlow', `Current version: ${status.version}`);
|
|
40
42
|
|
|
41
|
-
// Get
|
|
43
|
+
// Get local CLI version and npm registry version
|
|
42
44
|
const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
|
|
43
|
-
const
|
|
45
|
+
const localCliVersion = packageJson.version;
|
|
44
46
|
|
|
45
|
-
console.log(chalk.
|
|
46
|
-
|
|
47
|
+
console.log(chalk.dim('Checking npm registry for latest version...'));
|
|
48
|
+
const npmLatestVersion = await getLatestVersion('agileflow');
|
|
47
49
|
|
|
48
|
-
if (
|
|
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${
|
|
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',
|
|
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} → ${
|
|
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 {
|
|
203
|
+
* @param {Object} config - Manifest configuration
|
|
204
204
|
*/
|
|
205
|
-
async createManifest(cfgDir,
|
|
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
|
+
};
|
package/tools/postinstall.js
CHANGED
|
@@ -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,33 +111,76 @@ 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
|
|
|
107
117
|
if (!fs.existsSync(cliPath)) {
|
|
108
|
-
log('⚠️ Could not find AgileFlow CLI. Run "npx agileflow
|
|
118
|
+
log('⚠️ Could not find AgileFlow CLI. Run "npx agileflow setup" manually.', 'yellow');
|
|
109
119
|
return;
|
|
110
120
|
}
|
|
111
121
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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');
|
|
120
176
|
console.log('');
|
|
121
|
-
log('To skip auto-
|
|
177
|
+
log('To skip auto-setup in the future, set: AGILEFLOW_SKIP_INSTALL=true', 'dim');
|
|
122
178
|
console.log('');
|
|
123
179
|
|
|
124
180
|
} catch (error) {
|
|
125
181
|
console.log('');
|
|
126
|
-
log('⚠️ Auto-
|
|
127
|
-
log(' You can run "npx agileflow
|
|
182
|
+
log('⚠️ Auto-setup encountered an issue.', 'yellow');
|
|
183
|
+
log(' You can run "npx agileflow setup" manually to complete setup.', 'yellow');
|
|
128
184
|
console.log('');
|
|
129
185
|
|
|
130
186
|
if (process.env.DEBUG) {
|
|
@@ -134,8 +190,10 @@ function runAutoInstall() {
|
|
|
134
190
|
}
|
|
135
191
|
|
|
136
192
|
// Main execution
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
193
|
+
(async () => {
|
|
194
|
+
if (shouldSkipInstall()) {
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}
|
|
140
197
|
|
|
141
|
-
runAutoInstall();
|
|
198
|
+
await runAutoInstall();
|
|
199
|
+
})();
|