agileflow 2.89.3 → 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 +5 -0
- package/README.md +3 -3
- package/lib/placeholder-registry.js +617 -0
- package/lib/smart-json-file.js +205 -1
- 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 +37 -737
- package/package.json +4 -1
- package/scripts/check-update.js +16 -3
- package/scripts/lib/sessionRegistry.js +682 -0
- 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 +7 -30
- package/tools/cli/commands/doctor.js +18 -38
- package/tools/cli/commands/list.js +47 -35
- package/tools/cli/commands/status.js +13 -37
- package/tools/cli/commands/uninstall.js +9 -38
- package/tools/cli/installers/core/installer.js +13 -0
- package/tools/cli/lib/command-context.js +374 -0
- package/tools/cli/lib/config-manager.js +394 -0
- 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
|
@@ -28,6 +28,8 @@ const {
|
|
|
28
28
|
isRecoverable,
|
|
29
29
|
} = require('../../../lib/error-codes');
|
|
30
30
|
const { safeDump } = require('../../../lib/yaml-utils');
|
|
31
|
+
const { IdeRegistry } = require('../lib/ide-registry');
|
|
32
|
+
const { formatKeyValue, formatList, isTTY } = require('../../../lib/table-formatter');
|
|
31
33
|
|
|
32
34
|
const installer = new Installer();
|
|
33
35
|
|
|
@@ -239,8 +241,8 @@ module.exports = {
|
|
|
239
241
|
ideManager.setDocsFolder(status.docsFolder || 'docs');
|
|
240
242
|
|
|
241
243
|
for (const ide of status.ides) {
|
|
242
|
-
const configPath =
|
|
243
|
-
const ideName =
|
|
244
|
+
const configPath = IdeRegistry.getConfigPath(ide, directory);
|
|
245
|
+
const ideName = IdeRegistry.getDisplayName(ide);
|
|
244
246
|
|
|
245
247
|
if (await fs.pathExists(configPath)) {
|
|
246
248
|
// Count files in config
|
|
@@ -265,14 +267,14 @@ module.exports = {
|
|
|
265
267
|
|
|
266
268
|
// Check for orphaned configs
|
|
267
269
|
console.log(chalk.bold('\nOrphan Check:'));
|
|
268
|
-
const allIdes =
|
|
270
|
+
const allIdes = IdeRegistry.getAll();
|
|
269
271
|
let orphansFound = false;
|
|
270
272
|
|
|
271
273
|
for (const ide of allIdes) {
|
|
272
274
|
if (!status.ides || !status.ides.includes(ide)) {
|
|
273
|
-
const configPath =
|
|
275
|
+
const configPath = IdeRegistry.getConfigPath(ide, directory);
|
|
274
276
|
if (await fs.pathExists(configPath)) {
|
|
275
|
-
const ideName =
|
|
277
|
+
const ideName = IdeRegistry.getDisplayName(ide);
|
|
276
278
|
warning(`${ideName}: Config exists but not in manifest`);
|
|
277
279
|
orphansFound = true;
|
|
278
280
|
warnings++;
|
|
@@ -367,36 +369,6 @@ function compareVersions(a, b) {
|
|
|
367
369
|
return 0;
|
|
368
370
|
}
|
|
369
371
|
|
|
370
|
-
/**
|
|
371
|
-
* Get IDE config path
|
|
372
|
-
* @param {string} projectDir - Project directory
|
|
373
|
-
* @param {string} ide - IDE name
|
|
374
|
-
* @returns {string}
|
|
375
|
-
*/
|
|
376
|
-
function getIdeConfigPath(projectDir, ide) {
|
|
377
|
-
const paths = {
|
|
378
|
-
'claude-code': '.claude/commands/agileflow',
|
|
379
|
-
cursor: '.cursor/rules/agileflow',
|
|
380
|
-
windsurf: '.windsurf/workflows/agileflow',
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
return path.join(projectDir, paths[ide] || '');
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Format IDE name for display
|
|
388
|
-
* @param {string} ide - IDE name
|
|
389
|
-
* @returns {string}
|
|
390
|
-
*/
|
|
391
|
-
function formatIdeName(ide) {
|
|
392
|
-
const names = {
|
|
393
|
-
'claude-code': 'Claude Code',
|
|
394
|
-
cursor: 'Cursor',
|
|
395
|
-
windsurf: 'Windsurf',
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
return names[ide] || ide;
|
|
399
|
-
}
|
|
400
372
|
|
|
401
373
|
/**
|
|
402
374
|
* Count files in directory recursively
|
|
@@ -420,7 +392,7 @@ async function countFilesInDir(dirPath) {
|
|
|
420
392
|
}
|
|
421
393
|
|
|
422
394
|
/**
|
|
423
|
-
* Print summary
|
|
395
|
+
* Print summary using formatKeyValue for consistent output
|
|
424
396
|
* @param {number} issues - Issue count
|
|
425
397
|
* @param {number} warnings - Warning count
|
|
426
398
|
*/
|
|
@@ -430,9 +402,17 @@ function printSummary(issues, warnings) {
|
|
|
430
402
|
if (issues === 0 && warnings === 0) {
|
|
431
403
|
console.log(chalk.green.bold('No issues found.\n'));
|
|
432
404
|
} else if (issues === 0) {
|
|
433
|
-
console.log(
|
|
405
|
+
console.log(formatKeyValue({
|
|
406
|
+
Warnings: chalk.yellow(warnings),
|
|
407
|
+
Issues: chalk.green('0'),
|
|
408
|
+
}, { separator: ':', alignValues: false }));
|
|
409
|
+
console.log();
|
|
434
410
|
} else {
|
|
435
|
-
console.log(
|
|
411
|
+
console.log(formatKeyValue({
|
|
412
|
+
Issues: chalk.red(issues),
|
|
413
|
+
Warnings: chalk.yellow(warnings),
|
|
414
|
+
}, { separator: ':', alignValues: false }));
|
|
415
|
+
console.log();
|
|
436
416
|
}
|
|
437
417
|
}
|
|
438
418
|
|
|
@@ -13,6 +13,7 @@ const { displayLogo, displaySection, success, warning, info } = require('../lib/
|
|
|
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
|
|
|
@@ -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
|
|
@@ -87,7 +88,7 @@ module.exports = {
|
|
|
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
|
-
}
|
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
getErrorCodeFromError,
|
|
18
18
|
attachErrorCode,
|
|
19
19
|
} = require('../../../../lib/error-codes');
|
|
20
|
+
const { setSecurePermissions, SECURE_FILE_MODE } = require('../../../../lib/smart-json-file');
|
|
20
21
|
|
|
21
22
|
const TEXT_EXTENSIONS = new Set(['.md', '.yaml', '.yml', '.txt', '.json']);
|
|
22
23
|
|
|
@@ -504,6 +505,8 @@ class Installer {
|
|
|
504
505
|
};
|
|
505
506
|
|
|
506
507
|
await fs.writeFile(configPath, safeDump(config), 'utf8');
|
|
508
|
+
// Security: Set secure permissions (0o600) on config file
|
|
509
|
+
setSecurePermissions(configPath);
|
|
507
510
|
return;
|
|
508
511
|
}
|
|
509
512
|
|
|
@@ -531,6 +534,8 @@ class Installer {
|
|
|
531
534
|
};
|
|
532
535
|
|
|
533
536
|
await fs.writeFile(configPath, safeDump(next), 'utf8');
|
|
537
|
+
// Security: Set secure permissions (0o600) on config file
|
|
538
|
+
setSecurePermissions(configPath);
|
|
534
539
|
} catch (err) {
|
|
535
540
|
// If it's a typed parse error and not forcing, re-throw
|
|
536
541
|
if (err.errorCode === 'EPARSE' && !options.force) {
|
|
@@ -547,6 +552,8 @@ class Installer {
|
|
|
547
552
|
};
|
|
548
553
|
|
|
549
554
|
await fs.writeFile(configPath, safeDump(config), 'utf8');
|
|
555
|
+
// Security: Set secure permissions (0o600) on config file
|
|
556
|
+
setSecurePermissions(configPath);
|
|
550
557
|
}
|
|
551
558
|
}
|
|
552
559
|
}
|
|
@@ -578,6 +585,8 @@ class Installer {
|
|
|
578
585
|
};
|
|
579
586
|
|
|
580
587
|
await fs.writeFile(manifestPath, safeDump(manifest), 'utf8');
|
|
588
|
+
// Security: Set secure permissions (0o600) on manifest file
|
|
589
|
+
setSecurePermissions(manifestPath);
|
|
581
590
|
return;
|
|
582
591
|
}
|
|
583
592
|
|
|
@@ -608,6 +617,8 @@ class Installer {
|
|
|
608
617
|
};
|
|
609
618
|
|
|
610
619
|
await fs.writeFile(manifestPath, safeDump(manifest), 'utf8');
|
|
620
|
+
// Security: Set secure permissions (0o600) on manifest file
|
|
621
|
+
setSecurePermissions(manifestPath);
|
|
611
622
|
} catch (err) {
|
|
612
623
|
// If it's a typed parse error and not forcing, re-throw
|
|
613
624
|
if (err.errorCode === 'EPARSE' && !options.force) {
|
|
@@ -627,6 +638,8 @@ class Installer {
|
|
|
627
638
|
};
|
|
628
639
|
|
|
629
640
|
await fs.writeFile(manifestPath, safeDump(manifest), 'utf8');
|
|
641
|
+
// Security: Set secure permissions (0o600) on manifest file
|
|
642
|
+
setSecurePermissions(manifestPath);
|
|
630
643
|
}
|
|
631
644
|
}
|
|
632
645
|
}
|