agileflow 2.89.2 → 2.90.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/CHANGELOG.md +10 -0
- package/README.md +3 -3
- package/lib/content-sanitizer.js +463 -0
- package/lib/error-codes.js +544 -0
- package/lib/errors.js +336 -5
- package/lib/feedback.js +561 -0
- package/lib/path-resolver.js +396 -0
- package/lib/placeholder-registry.js +617 -0
- package/lib/session-registry.js +461 -0
- package/lib/smart-json-file.js +653 -0
- package/lib/table-formatter.js +504 -0
- package/lib/transient-status.js +374 -0
- package/lib/ui-manager.js +612 -0
- package/lib/validate-args.js +213 -0
- package/lib/validate-names.js +143 -0
- package/lib/validate-paths.js +434 -0
- package/lib/validate.js +38 -584
- package/package.json +4 -1
- package/scripts/agileflow-configure.js +40 -1440
- package/scripts/agileflow-welcome.js +2 -1
- package/scripts/check-update.js +16 -3
- package/scripts/lib/configure-detect.js +383 -0
- package/scripts/lib/configure-features.js +811 -0
- package/scripts/lib/configure-repair.js +314 -0
- package/scripts/lib/configure-utils.js +115 -0
- package/scripts/lib/frontmatter-parser.js +3 -3
- package/scripts/lib/sessionRegistry.js +682 -0
- package/scripts/obtain-context.js +417 -113
- package/scripts/ralph-loop.js +1 -1
- package/scripts/session-manager.js +77 -10
- package/scripts/tui/App.js +176 -0
- package/scripts/tui/index.js +75 -0
- package/scripts/tui/lib/crashRecovery.js +302 -0
- package/scripts/tui/lib/eventStream.js +316 -0
- package/scripts/tui/lib/keyboard.js +252 -0
- package/scripts/tui/lib/loopControl.js +371 -0
- package/scripts/tui/panels/OutputPanel.js +278 -0
- package/scripts/tui/panels/SessionPanel.js +178 -0
- package/scripts/tui/panels/TracePanel.js +333 -0
- package/src/core/commands/tui.md +91 -0
- package/tools/cli/commands/config.js +10 -33
- package/tools/cli/commands/doctor.js +48 -40
- package/tools/cli/commands/list.js +49 -37
- package/tools/cli/commands/status.js +13 -37
- package/tools/cli/commands/uninstall.js +12 -41
- package/tools/cli/installers/core/installer.js +75 -12
- package/tools/cli/installers/ide/_interface.js +238 -0
- package/tools/cli/installers/ide/codex.js +2 -2
- package/tools/cli/installers/ide/manager.js +15 -0
- package/tools/cli/lib/command-context.js +374 -0
- package/tools/cli/lib/config-manager.js +394 -0
- package/tools/cli/lib/content-injector.js +69 -16
- package/tools/cli/lib/ide-errors.js +163 -29
- package/tools/cli/lib/ide-registry.js +186 -0
- package/tools/cli/lib/npm-utils.js +16 -3
- package/tools/cli/lib/self-update.js +148 -0
- package/tools/cli/lib/validation-middleware.js +491 -0
|
@@ -21,6 +21,15 @@ const {
|
|
|
21
21
|
const { IdeManager } = require('../installers/ide/manager');
|
|
22
22
|
const { getCurrentVersion } = require('../lib/version-checker');
|
|
23
23
|
const { ErrorHandler } = require('../lib/error-handler');
|
|
24
|
+
const {
|
|
25
|
+
ErrorCodes,
|
|
26
|
+
getErrorCodeFromError,
|
|
27
|
+
getSuggestedFix,
|
|
28
|
+
isRecoverable,
|
|
29
|
+
} = require('../../../lib/error-codes');
|
|
30
|
+
const { safeDump } = require('../../../lib/yaml-utils');
|
|
31
|
+
const { IdeRegistry } = require('../lib/ide-registry');
|
|
32
|
+
const { formatKeyValue, formatList, isTTY } = require('../../../lib/table-formatter');
|
|
24
33
|
|
|
25
34
|
const installer = new Installer();
|
|
26
35
|
|
|
@@ -88,6 +97,7 @@ module.exports = {
|
|
|
88
97
|
issues++;
|
|
89
98
|
repairs.push({
|
|
90
99
|
type: 'missing-manifest',
|
|
100
|
+
errorCode: 'ENOENT',
|
|
91
101
|
message: 'Recreate missing manifest.yaml',
|
|
92
102
|
fix: async () => {
|
|
93
103
|
info('Recreating manifest.yaml...');
|
|
@@ -95,7 +105,6 @@ module.exports = {
|
|
|
95
105
|
const cfgDir = path.join(status.path, '_cfg');
|
|
96
106
|
await fs.ensureDir(cfgDir);
|
|
97
107
|
|
|
98
|
-
const yaml = require('js-yaml');
|
|
99
108
|
const manifest = {
|
|
100
109
|
version: packageJson.version,
|
|
101
110
|
installed_at: new Date().toISOString(),
|
|
@@ -107,7 +116,7 @@ module.exports = {
|
|
|
107
116
|
docs_folder: 'docs',
|
|
108
117
|
};
|
|
109
118
|
|
|
110
|
-
await fs.writeFile(manifestPath,
|
|
119
|
+
await fs.writeFile(manifestPath, safeDump(manifest), 'utf8');
|
|
111
120
|
success('Created manifest.yaml');
|
|
112
121
|
},
|
|
113
122
|
});
|
|
@@ -142,6 +151,7 @@ module.exports = {
|
|
|
142
151
|
warnings++;
|
|
143
152
|
repairs.push({
|
|
144
153
|
type: 'invalid-file-index',
|
|
154
|
+
errorCode: 'EPARSE',
|
|
145
155
|
message: 'Recreate files.json safe-update index',
|
|
146
156
|
fix: async () => {
|
|
147
157
|
await createProtectedFileIndex(status.path, fileIndexPath);
|
|
@@ -154,6 +164,7 @@ module.exports = {
|
|
|
154
164
|
warnings++;
|
|
155
165
|
repairs.push({
|
|
156
166
|
type: 'missing-file-index',
|
|
167
|
+
errorCode: 'ENOENT',
|
|
157
168
|
message: 'Create files.json safe-update index',
|
|
158
169
|
fix: async () => {
|
|
159
170
|
await createProtectedFileIndex(status.path, fileIndexPath);
|
|
@@ -196,6 +207,7 @@ module.exports = {
|
|
|
196
207
|
if (missingCore) {
|
|
197
208
|
repairs.push({
|
|
198
209
|
type: 'missing-core',
|
|
210
|
+
errorCode: 'EEMPTYDIR',
|
|
199
211
|
message: 'Reinstall missing core content',
|
|
200
212
|
fix: async () => {
|
|
201
213
|
info('Reinstalling core content...');
|
|
@@ -229,8 +241,8 @@ module.exports = {
|
|
|
229
241
|
ideManager.setDocsFolder(status.docsFolder || 'docs');
|
|
230
242
|
|
|
231
243
|
for (const ide of status.ides) {
|
|
232
|
-
const configPath =
|
|
233
|
-
const ideName =
|
|
244
|
+
const configPath = IdeRegistry.getConfigPath(ide, directory);
|
|
245
|
+
const ideName = IdeRegistry.getDisplayName(ide);
|
|
234
246
|
|
|
235
247
|
if (await fs.pathExists(configPath)) {
|
|
236
248
|
// Count files in config
|
|
@@ -241,6 +253,7 @@ module.exports = {
|
|
|
241
253
|
warnings++;
|
|
242
254
|
repairs.push({
|
|
243
255
|
type: 'missing-ide-config',
|
|
256
|
+
errorCode: 'ENODIR',
|
|
244
257
|
message: `Reinstall ${ideName} configuration`,
|
|
245
258
|
fix: async () => {
|
|
246
259
|
info(`Reinstalling ${ideName} configuration...`);
|
|
@@ -254,19 +267,20 @@ module.exports = {
|
|
|
254
267
|
|
|
255
268
|
// Check for orphaned configs
|
|
256
269
|
console.log(chalk.bold('\nOrphan Check:'));
|
|
257
|
-
const allIdes =
|
|
270
|
+
const allIdes = IdeRegistry.getAll();
|
|
258
271
|
let orphansFound = false;
|
|
259
272
|
|
|
260
273
|
for (const ide of allIdes) {
|
|
261
274
|
if (!status.ides || !status.ides.includes(ide)) {
|
|
262
|
-
const configPath =
|
|
275
|
+
const configPath = IdeRegistry.getConfigPath(ide, directory);
|
|
263
276
|
if (await fs.pathExists(configPath)) {
|
|
264
|
-
const ideName =
|
|
277
|
+
const ideName = IdeRegistry.getDisplayName(ide);
|
|
265
278
|
warning(`${ideName}: Config exists but not in manifest`);
|
|
266
279
|
orphansFound = true;
|
|
267
280
|
warnings++;
|
|
268
281
|
repairs.push({
|
|
269
282
|
type: 'orphaned-config',
|
|
283
|
+
errorCode: 'ECONFLICT',
|
|
270
284
|
message: `Remove orphaned ${ideName} configuration`,
|
|
271
285
|
fix: async () => {
|
|
272
286
|
info(`Removing orphaned ${ideName} configuration...`);
|
|
@@ -291,7 +305,13 @@ module.exports = {
|
|
|
291
305
|
try {
|
|
292
306
|
await repair.fix();
|
|
293
307
|
} catch (err) {
|
|
308
|
+
// Use error codes for better diagnosis
|
|
309
|
+
const codeData = getErrorCodeFromError(err);
|
|
294
310
|
error(`Failed to ${repair.message.toLowerCase()}: ${err.message}`);
|
|
311
|
+
if (codeData.code !== 'EUNKNOWN') {
|
|
312
|
+
console.log(chalk.dim(` Error code: ${codeData.code}`));
|
|
313
|
+
console.log(chalk.dim(` Suggestion: ${codeData.suggestedFix}`));
|
|
314
|
+
}
|
|
295
315
|
}
|
|
296
316
|
}
|
|
297
317
|
|
|
@@ -300,6 +320,16 @@ module.exports = {
|
|
|
300
320
|
} else if (repairs.length > 0 && !options.fix) {
|
|
301
321
|
console.log();
|
|
302
322
|
info(`Found ${repairs.length} fixable issue(s). Run with --fix to auto-repair.`);
|
|
323
|
+
|
|
324
|
+
// Show summary of fixable issues with error codes
|
|
325
|
+
console.log(chalk.bold('\nFixable Issues:'));
|
|
326
|
+
for (const repair of repairs) {
|
|
327
|
+
const errorCode = repair.errorCode || 'ECONFIG';
|
|
328
|
+
const codeData = ErrorCodes[errorCode] || ErrorCodes.ECONFIG;
|
|
329
|
+
console.log(
|
|
330
|
+
` ${chalk.yellow('!')} ${repair.message} ${chalk.dim(`[${codeData.code}]`)}`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
303
333
|
}
|
|
304
334
|
|
|
305
335
|
// Print summary
|
|
@@ -339,36 +369,6 @@ function compareVersions(a, b) {
|
|
|
339
369
|
return 0;
|
|
340
370
|
}
|
|
341
371
|
|
|
342
|
-
/**
|
|
343
|
-
* Get IDE config path
|
|
344
|
-
* @param {string} projectDir - Project directory
|
|
345
|
-
* @param {string} ide - IDE name
|
|
346
|
-
* @returns {string}
|
|
347
|
-
*/
|
|
348
|
-
function getIdeConfigPath(projectDir, ide) {
|
|
349
|
-
const paths = {
|
|
350
|
-
'claude-code': '.claude/commands/agileflow',
|
|
351
|
-
cursor: '.cursor/rules/agileflow',
|
|
352
|
-
windsurf: '.windsurf/workflows/agileflow',
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
return path.join(projectDir, paths[ide] || '');
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Format IDE name for display
|
|
360
|
-
* @param {string} ide - IDE name
|
|
361
|
-
* @returns {string}
|
|
362
|
-
*/
|
|
363
|
-
function formatIdeName(ide) {
|
|
364
|
-
const names = {
|
|
365
|
-
'claude-code': 'Claude Code',
|
|
366
|
-
cursor: 'Cursor',
|
|
367
|
-
windsurf: 'Windsurf',
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
return names[ide] || ide;
|
|
371
|
-
}
|
|
372
372
|
|
|
373
373
|
/**
|
|
374
374
|
* Count files in directory recursively
|
|
@@ -392,7 +392,7 @@ async function countFilesInDir(dirPath) {
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
/**
|
|
395
|
-
* Print summary
|
|
395
|
+
* Print summary using formatKeyValue for consistent output
|
|
396
396
|
* @param {number} issues - Issue count
|
|
397
397
|
* @param {number} warnings - Warning count
|
|
398
398
|
*/
|
|
@@ -402,9 +402,17 @@ function printSummary(issues, warnings) {
|
|
|
402
402
|
if (issues === 0 && warnings === 0) {
|
|
403
403
|
console.log(chalk.green.bold('No issues found.\n'));
|
|
404
404
|
} else if (issues === 0) {
|
|
405
|
-
console.log(
|
|
405
|
+
console.log(formatKeyValue({
|
|
406
|
+
Warnings: chalk.yellow(warnings),
|
|
407
|
+
Issues: chalk.green('0'),
|
|
408
|
+
}, { separator: ':', alignValues: false }));
|
|
409
|
+
console.log();
|
|
406
410
|
} else {
|
|
407
|
-
console.log(
|
|
411
|
+
console.log(formatKeyValue({
|
|
412
|
+
Issues: chalk.red(issues),
|
|
413
|
+
Warnings: chalk.yellow(warnings),
|
|
414
|
+
}, { separator: ':', alignValues: false }));
|
|
415
|
+
console.log();
|
|
408
416
|
}
|
|
409
417
|
}
|
|
410
418
|
|
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const path = require('node:path');
|
|
9
9
|
const fs = require('fs-extra');
|
|
10
|
-
const
|
|
10
|
+
const { safeLoad } = require('../../../lib/yaml-utils');
|
|
11
11
|
const { Installer } = require('../installers/core/installer');
|
|
12
12
|
const { displayLogo, displaySection, success, warning, info } = require('../lib/ui');
|
|
13
13
|
const {
|
|
14
14
|
parseFrontmatter: parseYamlFrontmatter,
|
|
15
15
|
} = require('../../../scripts/lib/frontmatter-parser');
|
|
16
|
+
const { formatList, formatKeyValue, formatHeader, isTTY } = require('../../../lib/table-formatter');
|
|
16
17
|
|
|
17
18
|
const installer = new Installer();
|
|
18
19
|
|
|
@@ -233,7 +234,7 @@ async function listExperts(agileflowPath) {
|
|
|
233
234
|
|
|
234
235
|
try {
|
|
235
236
|
const content = await fs.readFile(expertiseFile, 'utf8');
|
|
236
|
-
const parsed =
|
|
237
|
+
const parsed = safeLoad(content);
|
|
237
238
|
|
|
238
239
|
experts.push({
|
|
239
240
|
name: entry.name,
|
|
@@ -300,73 +301,84 @@ function extractFirstLine(content) {
|
|
|
300
301
|
}
|
|
301
302
|
|
|
302
303
|
/**
|
|
303
|
-
* Display compact output
|
|
304
|
+
* Display compact output using formatKeyValue
|
|
304
305
|
*/
|
|
305
306
|
function displayCompact(result, showCommands, showAgents, showSkills, showExperts) {
|
|
307
|
+
const data = {};
|
|
308
|
+
|
|
306
309
|
if (showCommands && result.commands?.length > 0) {
|
|
307
|
-
|
|
310
|
+
data.Commands = result.commands.map(c => c.name).join(', ');
|
|
308
311
|
}
|
|
309
312
|
|
|
310
313
|
if (showAgents && result.agents?.length > 0) {
|
|
311
|
-
|
|
314
|
+
data.Agents = result.agents.map(a => a.name).join(', ');
|
|
312
315
|
}
|
|
313
316
|
|
|
314
317
|
if (showSkills && result.skills?.length > 0) {
|
|
315
|
-
|
|
318
|
+
data.Skills = result.skills.map(s => s.name).join(', ');
|
|
316
319
|
}
|
|
317
320
|
|
|
318
321
|
if (showExperts && result.experts?.length > 0) {
|
|
319
|
-
|
|
322
|
+
data.Experts = result.experts.map(e => e.name).join(', ');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (Object.keys(data).length > 0) {
|
|
326
|
+
console.log(formatKeyValue(data, { alignValues: false }));
|
|
320
327
|
}
|
|
321
328
|
}
|
|
322
329
|
|
|
323
330
|
/**
|
|
324
|
-
* Display full output with descriptions
|
|
331
|
+
* Display full output with descriptions using formatList
|
|
325
332
|
*/
|
|
326
333
|
function displayFull(result, showCommands, showAgents, showSkills, showExperts) {
|
|
327
|
-
|
|
328
|
-
displaySection(`Commands (${result.commands.length})`);
|
|
334
|
+
const { BRAND_HEX } = require('../../../lib/colors');
|
|
329
335
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
336
|
+
if (showCommands && result.commands?.length > 0) {
|
|
337
|
+
console.log(formatHeader(`Commands (${result.commands.length})`));
|
|
338
|
+
const items = result.commands.map(cmd => ({
|
|
339
|
+
text: `${chalk.hex(BRAND_HEX)(cmd.name)}\n ${chalk.dim(cmd.description)}`,
|
|
340
|
+
status: 'active',
|
|
341
|
+
}));
|
|
342
|
+
console.log(formatList(items, { indent: ' ' }));
|
|
334
343
|
}
|
|
335
344
|
|
|
336
345
|
if (showAgents && result.agents?.length > 0) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
for (const agent of result.agents) {
|
|
346
|
+
console.log(formatHeader(`Agents (${result.agents.length})`));
|
|
347
|
+
const items = result.agents.map(agent => {
|
|
340
348
|
const modelBadge = agent.model !== 'default' ? chalk.dim(` [${agent.model}]`) : '';
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
349
|
+
return {
|
|
350
|
+
text: `${chalk.hex(BRAND_HEX)(agent.name)}${modelBadge}\n ${chalk.dim(agent.description)}`,
|
|
351
|
+
status: 'active',
|
|
352
|
+
};
|
|
353
|
+
});
|
|
354
|
+
console.log(formatList(items, { indent: ' ' }));
|
|
344
355
|
}
|
|
345
356
|
|
|
346
357
|
if (showSkills && result.skills?.length > 0) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
console.log(chalk.hex('#e8683a')(` ${skill.name}`));
|
|
351
|
-
console.log(chalk.dim(` ${skill.description}`));
|
|
358
|
+
console.log(formatHeader(`Skills (${result.skills.length})`));
|
|
359
|
+
const items = result.skills.map(skill => {
|
|
360
|
+
let desc = chalk.dim(skill.description);
|
|
352
361
|
if (skill.triggers?.length > 0) {
|
|
353
|
-
|
|
354
|
-
chalk.dim(
|
|
355
|
-
` Triggers: ${skill.triggers.slice(0, 3).join(', ')}${skill.triggers.length > 3 ? '...' : ''}`
|
|
356
|
-
)
|
|
357
|
-
);
|
|
362
|
+
desc += `\n ${chalk.dim(`Triggers: ${skill.triggers.slice(0, 3).join(', ')}${skill.triggers.length > 3 ? '...' : ''}`)}`;
|
|
358
363
|
}
|
|
359
|
-
|
|
364
|
+
return {
|
|
365
|
+
text: `${chalk.hex(BRAND_HEX)(skill.name)}\n ${desc}`,
|
|
366
|
+
status: 'active',
|
|
367
|
+
};
|
|
368
|
+
});
|
|
369
|
+
console.log(formatList(items, { indent: ' ' }));
|
|
360
370
|
}
|
|
361
371
|
|
|
362
372
|
if (showExperts && result.experts?.length > 0) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
for (const expert of result.experts) {
|
|
373
|
+
console.log(formatHeader(`Experts (${result.experts.length})`));
|
|
374
|
+
const items = result.experts.map(expert => {
|
|
366
375
|
const versionBadge = expert.version !== 'unknown' ? chalk.dim(` v${expert.version}`) : '';
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
376
|
+
return {
|
|
377
|
+
text: `${chalk.hex(BRAND_HEX)(expert.name)}${versionBadge}\n ${chalk.dim(expert.description)}`,
|
|
378
|
+
status: 'active',
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
console.log(formatList(items, { indent: ' ' }));
|
|
370
382
|
}
|
|
371
383
|
|
|
372
384
|
console.log(); // Final newline
|
|
@@ -11,6 +11,8 @@ const ora = require('ora');
|
|
|
11
11
|
const { Installer } = require('../installers/core/installer');
|
|
12
12
|
const { displayLogo, displaySection, success, warning, info } = require('../lib/ui');
|
|
13
13
|
const { checkForUpdate } = require('../lib/version-checker');
|
|
14
|
+
const { IdeRegistry } = require('../lib/ide-registry');
|
|
15
|
+
const { formatKeyValue, formatList, isTTY } = require('../../../lib/table-formatter');
|
|
14
16
|
|
|
15
17
|
const installer = new Installer();
|
|
16
18
|
|
|
@@ -33,14 +35,18 @@ module.exports = {
|
|
|
33
35
|
process.exit(0);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
// Show installation info
|
|
37
|
-
console.log(
|
|
38
|
-
|
|
38
|
+
// Show installation info using formatKeyValue
|
|
39
|
+
console.log(formatKeyValue({
|
|
40
|
+
Location: status.path,
|
|
41
|
+
Version: status.version,
|
|
42
|
+
}));
|
|
39
43
|
|
|
40
44
|
// Count installed items
|
|
41
45
|
const counts = await installer.countInstalledItems(status.path);
|
|
42
46
|
|
|
43
|
-
console.log(
|
|
47
|
+
console.log(formatKeyValue({
|
|
48
|
+
'\nCore': chalk.green('✓ Installed'),
|
|
49
|
+
}, { alignValues: false }));
|
|
44
50
|
info(`${counts.agents} agents`);
|
|
45
51
|
info(`${counts.commands} commands`);
|
|
46
52
|
info(`${counts.skills} skills`);
|
|
@@ -50,13 +56,13 @@ module.exports = {
|
|
|
50
56
|
console.log(chalk.bold('\nConfigured IDEs:'));
|
|
51
57
|
for (const ide of status.ides) {
|
|
52
58
|
// Check if IDE config exists
|
|
53
|
-
const ideConfigPath =
|
|
59
|
+
const ideConfigPath = IdeRegistry.getConfigPath(ide, directory);
|
|
54
60
|
const exists = await fs.pathExists(ideConfigPath);
|
|
55
61
|
|
|
56
62
|
if (exists) {
|
|
57
|
-
success(
|
|
63
|
+
success(IdeRegistry.getDisplayName(ide));
|
|
58
64
|
} else {
|
|
59
|
-
warning(`${
|
|
65
|
+
warning(`${IdeRegistry.getDisplayName(ide)} (config missing)`);
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
68
|
}
|
|
@@ -87,33 +93,3 @@ module.exports = {
|
|
|
87
93
|
},
|
|
88
94
|
};
|
|
89
95
|
|
|
90
|
-
/**
|
|
91
|
-
* Get IDE config path
|
|
92
|
-
* @param {string} projectDir - Project directory
|
|
93
|
-
* @param {string} ide - IDE name
|
|
94
|
-
* @returns {string}
|
|
95
|
-
*/
|
|
96
|
-
function getIdeConfigPath(projectDir, ide) {
|
|
97
|
-
const paths = {
|
|
98
|
-
'claude-code': '.claude/commands/agileflow',
|
|
99
|
-
cursor: '.cursor/rules/agileflow',
|
|
100
|
-
windsurf: '.windsurf/workflows/agileflow',
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
return path.join(projectDir, paths[ide] || '');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Format IDE name for display
|
|
108
|
-
* @param {string} ide - IDE name
|
|
109
|
-
* @returns {string}
|
|
110
|
-
*/
|
|
111
|
-
function formatIdeName(ide) {
|
|
112
|
-
const names = {
|
|
113
|
-
'claude-code': 'Claude Code',
|
|
114
|
-
cursor: 'Cursor',
|
|
115
|
-
windsurf: 'Windsurf',
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
return names[ide] || ide;
|
|
119
|
-
}
|
|
@@ -11,6 +11,7 @@ const { Installer } = require('../installers/core/installer');
|
|
|
11
11
|
const { IdeManager } = require('../installers/ide/manager');
|
|
12
12
|
const { displayLogo, displaySection, success, warning, error, confirm } = require('../lib/ui');
|
|
13
13
|
const { ErrorHandler } = require('../lib/error-handler');
|
|
14
|
+
const { IdeRegistry } = require('../lib/ide-registry');
|
|
14
15
|
|
|
15
16
|
const installer = new Installer();
|
|
16
17
|
const ideManager = new IdeManager();
|
|
@@ -40,17 +41,17 @@ module.exports = {
|
|
|
40
41
|
// Check if removing just one IDE
|
|
41
42
|
if (options.ide) {
|
|
42
43
|
const ideName = options.ide.toLowerCase();
|
|
43
|
-
displaySection('Removing IDE Configuration', `IDE: ${
|
|
44
|
+
displaySection('Removing IDE Configuration', `IDE: ${IdeRegistry.getDisplayName(ideName)}`);
|
|
44
45
|
|
|
45
46
|
if (!status.ides || !status.ides.includes(ideName)) {
|
|
46
|
-
warning(`${
|
|
47
|
+
warning(`${IdeRegistry.getDisplayName(ideName)} is not configured in this installation`);
|
|
47
48
|
console.log(chalk.dim(`Configured IDEs: ${(status.ides || []).join(', ') || 'none'}\n`));
|
|
48
49
|
process.exit(0);
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
// Confirm removal
|
|
52
53
|
if (!options.force) {
|
|
53
|
-
const proceed = await confirm(`Remove ${
|
|
54
|
+
const proceed = await confirm(`Remove ${IdeRegistry.getDisplayName(ideName)} configuration?`, false);
|
|
54
55
|
if (!proceed) {
|
|
55
56
|
console.log(chalk.dim('\nCancelled\n'));
|
|
56
57
|
process.exit(0);
|
|
@@ -60,10 +61,10 @@ module.exports = {
|
|
|
60
61
|
console.log();
|
|
61
62
|
|
|
62
63
|
// Remove the IDE configuration
|
|
63
|
-
const configPath =
|
|
64
|
+
const configPath = IdeRegistry.getConfigPath(ideName, directory);
|
|
64
65
|
if (await fs.pathExists(configPath)) {
|
|
65
66
|
await fs.remove(configPath);
|
|
66
|
-
success(`Removed ${
|
|
67
|
+
success(`Removed ${IdeRegistry.getDisplayName(ideName)} configuration`);
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
// Also remove spawnable agents for claude-code
|
|
@@ -78,16 +79,16 @@ module.exports = {
|
|
|
78
79
|
// Update the manifest to remove this IDE
|
|
79
80
|
const manifestPath = path.join(status.path, '_cfg', 'manifest.yaml');
|
|
80
81
|
if (await fs.pathExists(manifestPath)) {
|
|
81
|
-
const
|
|
82
|
+
const { safeLoad, safeDump } = require('../../../lib/yaml-utils');
|
|
82
83
|
const manifestContent = await fs.readFile(manifestPath, 'utf8');
|
|
83
|
-
const manifest =
|
|
84
|
+
const manifest = safeLoad(manifestContent);
|
|
84
85
|
manifest.ides = (manifest.ides || []).filter(ide => ide !== ideName);
|
|
85
86
|
manifest.updated_at = new Date().toISOString();
|
|
86
|
-
await fs.writeFile(manifestPath,
|
|
87
|
+
await fs.writeFile(manifestPath, safeDump(manifest), 'utf8');
|
|
87
88
|
success('Updated manifest');
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
console.log(chalk.green(`\n${
|
|
91
|
+
console.log(chalk.green(`\n${IdeRegistry.getDisplayName(ideName)} has been removed.\n`));
|
|
91
92
|
if (status.ides.length > 1) {
|
|
92
93
|
console.log(
|
|
93
94
|
chalk.dim(`Remaining IDEs: ${status.ides.filter(i => i !== ideName).join(', ')}\n`)
|
|
@@ -114,10 +115,10 @@ module.exports = {
|
|
|
114
115
|
// Remove IDE configurations
|
|
115
116
|
if (status.ides && status.ides.length > 0) {
|
|
116
117
|
for (const ide of status.ides) {
|
|
117
|
-
const configPath =
|
|
118
|
+
const configPath = IdeRegistry.getConfigPath(ide, directory);
|
|
118
119
|
if (await fs.pathExists(configPath)) {
|
|
119
120
|
await fs.remove(configPath);
|
|
120
|
-
success(`Removed ${
|
|
121
|
+
success(`Removed ${IdeRegistry.getDisplayName(ide)} configuration`);
|
|
121
122
|
}
|
|
122
123
|
// Also remove spawnable agents for claude-code
|
|
123
124
|
if (ide === 'claude-code') {
|
|
@@ -150,33 +151,3 @@ module.exports = {
|
|
|
150
151
|
},
|
|
151
152
|
};
|
|
152
153
|
|
|
153
|
-
/**
|
|
154
|
-
* Get IDE config path
|
|
155
|
-
* @param {string} projectDir - Project directory
|
|
156
|
-
* @param {string} ide - IDE name
|
|
157
|
-
* @returns {string}
|
|
158
|
-
*/
|
|
159
|
-
function getIdeConfigPath(projectDir, ide) {
|
|
160
|
-
const paths = {
|
|
161
|
-
'claude-code': '.claude/commands/agileflow',
|
|
162
|
-
cursor: '.cursor/rules/agileflow',
|
|
163
|
-
windsurf: '.windsurf/workflows/agileflow',
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
return path.join(projectDir, paths[ide] || '');
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Format IDE name for display
|
|
171
|
-
* @param {string} ide - IDE name
|
|
172
|
-
* @returns {string}
|
|
173
|
-
*/
|
|
174
|
-
function formatIdeName(ide) {
|
|
175
|
-
const names = {
|
|
176
|
-
'claude-code': 'Claude Code',
|
|
177
|
-
cursor: 'Cursor',
|
|
178
|
-
windsurf: 'Windsurf',
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
return names[ide] || ide;
|
|
182
|
-
}
|