proagents 1.0.3 → 1.0.5
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/lib/commands/init.js +264 -10
- package/package.json +3 -2
package/lib/commands/init.js
CHANGED
|
@@ -1,11 +1,93 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, cpSync, writeFileSync, readFileSync } from 'fs';
|
|
1
|
+
import { existsSync, mkdirSync, cpSync, writeFileSync, readFileSync, readdirSync, rmSync } from 'fs';
|
|
2
2
|
import { join, dirname } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
+
import yaml from 'js-yaml';
|
|
5
6
|
|
|
6
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
8
|
const __dirname = dirname(__filename);
|
|
8
9
|
|
|
10
|
+
// Files/folders to preserve during update (user customizations)
|
|
11
|
+
const PRESERVE_PATHS = [
|
|
12
|
+
'active-features', // User's work in progress
|
|
13
|
+
'.learning', // Learned patterns
|
|
14
|
+
'cache', // Cached analysis
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
// Config file is handled specially - merged not preserved
|
|
18
|
+
const CONFIG_FILE = 'proagents.config.yaml';
|
|
19
|
+
|
|
20
|
+
// Files that should always be updated (framework files)
|
|
21
|
+
const FRAMEWORK_FOLDERS = [
|
|
22
|
+
'prompts',
|
|
23
|
+
'templates',
|
|
24
|
+
'checklists',
|
|
25
|
+
'standards',
|
|
26
|
+
'examples',
|
|
27
|
+
'git',
|
|
28
|
+
'ui-integration',
|
|
29
|
+
'workflow-modes',
|
|
30
|
+
'security',
|
|
31
|
+
'performance',
|
|
32
|
+
'testing-standards',
|
|
33
|
+
'patterns',
|
|
34
|
+
'scaffolding',
|
|
35
|
+
'cli',
|
|
36
|
+
'adr',
|
|
37
|
+
'ai-models',
|
|
38
|
+
'ai-training',
|
|
39
|
+
'api-versioning',
|
|
40
|
+
'approval-workflows',
|
|
41
|
+
'automation',
|
|
42
|
+
'changelog',
|
|
43
|
+
'cicd',
|
|
44
|
+
'collaboration',
|
|
45
|
+
'compliance',
|
|
46
|
+
'config-versioning',
|
|
47
|
+
'config',
|
|
48
|
+
'contract-testing',
|
|
49
|
+
'cost',
|
|
50
|
+
'database',
|
|
51
|
+
'dependency-management',
|
|
52
|
+
'disaster-recovery',
|
|
53
|
+
'environments',
|
|
54
|
+
'existing-projects',
|
|
55
|
+
'feature-flags',
|
|
56
|
+
'getting-started',
|
|
57
|
+
'i18n',
|
|
58
|
+
'ide-integration',
|
|
59
|
+
'integrations',
|
|
60
|
+
'learning',
|
|
61
|
+
'logging',
|
|
62
|
+
'mcp',
|
|
63
|
+
'metrics',
|
|
64
|
+
'migrations',
|
|
65
|
+
'monitoring',
|
|
66
|
+
'multi-project',
|
|
67
|
+
'notifications',
|
|
68
|
+
'offline-mode',
|
|
69
|
+
'parallel-features',
|
|
70
|
+
'plugins',
|
|
71
|
+
'pm-integration',
|
|
72
|
+
'reporting',
|
|
73
|
+
'reverse-engineering',
|
|
74
|
+
'rules',
|
|
75
|
+
'runbooks',
|
|
76
|
+
'secrets',
|
|
77
|
+
'team',
|
|
78
|
+
'troubleshooting',
|
|
79
|
+
'webhooks',
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
// Root files to always update
|
|
83
|
+
const FRAMEWORK_FILES = [
|
|
84
|
+
'README.md',
|
|
85
|
+
'WORKFLOW.md',
|
|
86
|
+
'PROAGENTS.md',
|
|
87
|
+
'GETTING-STARTED-STORY.md',
|
|
88
|
+
'slash-commands.json',
|
|
89
|
+
];
|
|
90
|
+
|
|
9
91
|
/**
|
|
10
92
|
* Initialize ProAgents in the current project
|
|
11
93
|
*/
|
|
@@ -18,16 +100,37 @@ export async function initCommand(options = {}) {
|
|
|
18
100
|
console.log(chalk.blue('========================\n'));
|
|
19
101
|
|
|
20
102
|
// Check if already initialized
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
103
|
+
const alreadyInitialized = existsSync(proagentsDir);
|
|
104
|
+
|
|
105
|
+
if (alreadyInitialized && !options.force) {
|
|
106
|
+
// Smart update mode - preserve user files, update framework
|
|
107
|
+
console.log(chalk.cyan('ℹ️ ProAgents detected. Running smart update...'));
|
|
108
|
+
console.log(chalk.gray(' (Preserving your customizations)\n'));
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
await smartUpdate(sourceDir, proagentsDir);
|
|
112
|
+
console.log(chalk.green('\n✓ ProAgents updated successfully!\n'));
|
|
113
|
+
console.log(chalk.gray('Preserved:'));
|
|
114
|
+
console.log(chalk.gray(' • active-features/ (your work in progress)'));
|
|
115
|
+
console.log(chalk.gray(' • proagents.config.yaml (your values + new options merged)'));
|
|
116
|
+
console.log(chalk.gray(' • .learning/ (learned patterns)'));
|
|
117
|
+
console.log(chalk.gray(' • cache/ (analysis cache)\n'));
|
|
118
|
+
return;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(chalk.red('\n✗ Error updating ProAgents:'));
|
|
121
|
+
console.error(chalk.red(error.message));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
25
124
|
}
|
|
26
125
|
|
|
27
126
|
try {
|
|
28
|
-
|
|
127
|
+
if (options.force && alreadyInitialized) {
|
|
128
|
+
console.log(chalk.yellow('⚠️ Force mode: Overwriting all files...'));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Fresh install or force overwrite
|
|
29
132
|
console.log(chalk.gray('Copying framework files...'));
|
|
30
|
-
cpSync(sourceDir, proagentsDir, { recursive: true, force:
|
|
133
|
+
cpSync(sourceDir, proagentsDir, { recursive: true, force: true });
|
|
31
134
|
console.log(chalk.green('✓ Framework files copied to ./proagents/'));
|
|
32
135
|
|
|
33
136
|
// Create config if not skipped
|
|
@@ -52,7 +155,6 @@ export async function initCommand(options = {}) {
|
|
|
52
155
|
}
|
|
53
156
|
if (!existsSync(releasesDir)) {
|
|
54
157
|
mkdirSync(releasesDir, { recursive: true });
|
|
55
|
-
// Create releases index
|
|
56
158
|
writeFileSync(join(releasesDir, 'README.md'),
|
|
57
159
|
`# Release History
|
|
58
160
|
|
|
@@ -143,12 +245,10 @@ For detailed commands, see \`./proagents/PROAGENTS.md\`
|
|
|
143
245
|
if (existsSync(readmePath)) {
|
|
144
246
|
const readmeContent = readFileSync(readmePath, 'utf-8');
|
|
145
247
|
if (!readmeContent.includes('PROAGENTS:START')) {
|
|
146
|
-
// Prepend to existing README
|
|
147
248
|
writeFileSync(readmePath, proagentsSection + readmeContent);
|
|
148
249
|
console.log(chalk.green('✓ Added ProAgents commands to README.md'));
|
|
149
250
|
}
|
|
150
251
|
} else {
|
|
151
|
-
// Create new README with ProAgents section
|
|
152
252
|
writeFileSync(readmePath, proagentsSection + `# Project Name\n\nProject description.\n`);
|
|
153
253
|
console.log(chalk.green('✓ Created README.md with ProAgents commands'));
|
|
154
254
|
}
|
|
@@ -173,3 +273,157 @@ For detailed commands, see \`./proagents/PROAGENTS.md\`
|
|
|
173
273
|
process.exit(1);
|
|
174
274
|
}
|
|
175
275
|
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Smart update - preserves user files, updates framework files
|
|
279
|
+
*/
|
|
280
|
+
async function smartUpdate(sourceDir, targetDir) {
|
|
281
|
+
// Backup preserved paths
|
|
282
|
+
const backups = {};
|
|
283
|
+
for (const path of PRESERVE_PATHS) {
|
|
284
|
+
const fullPath = join(targetDir, path);
|
|
285
|
+
if (existsSync(fullPath)) {
|
|
286
|
+
backups[path] = fullPath;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Update framework folders
|
|
291
|
+
let updatedCount = 0;
|
|
292
|
+
for (const folder of FRAMEWORK_FOLDERS) {
|
|
293
|
+
const sourcePath = join(sourceDir, folder);
|
|
294
|
+
const targetPath = join(targetDir, folder);
|
|
295
|
+
|
|
296
|
+
if (existsSync(sourcePath)) {
|
|
297
|
+
// Remove old folder and copy new
|
|
298
|
+
if (existsSync(targetPath)) {
|
|
299
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
300
|
+
}
|
|
301
|
+
cpSync(sourcePath, targetPath, { recursive: true });
|
|
302
|
+
updatedCount++;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
console.log(chalk.green(`✓ Updated ${updatedCount} framework folders`));
|
|
306
|
+
|
|
307
|
+
// Update framework root files
|
|
308
|
+
let filesUpdated = 0;
|
|
309
|
+
for (const file of FRAMEWORK_FILES) {
|
|
310
|
+
const sourcePath = join(sourceDir, file);
|
|
311
|
+
const targetPath = join(targetDir, file);
|
|
312
|
+
|
|
313
|
+
if (existsSync(sourcePath)) {
|
|
314
|
+
cpSync(sourcePath, targetPath, { force: true });
|
|
315
|
+
filesUpdated++;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
console.log(chalk.green(`✓ Updated ${filesUpdated} framework files`));
|
|
319
|
+
|
|
320
|
+
// Merge config file - keep user values, add new options
|
|
321
|
+
const userConfigPath = join(targetDir, CONFIG_FILE);
|
|
322
|
+
const newConfigPath = join(sourceDir, CONFIG_FILE);
|
|
323
|
+
|
|
324
|
+
if (existsSync(newConfigPath)) {
|
|
325
|
+
const mergeResult = mergeConfigs(userConfigPath, newConfigPath);
|
|
326
|
+
if (mergeResult.newOptions > 0) {
|
|
327
|
+
console.log(chalk.green(`✓ Config merged: ${mergeResult.newOptions} new options added`));
|
|
328
|
+
if (mergeResult.newKeys.length > 0 && mergeResult.newKeys.length <= 5) {
|
|
329
|
+
console.log(chalk.gray(` New: ${mergeResult.newKeys.join(', ')}`));
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
console.log(chalk.green(`✓ Config up to date`));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Ensure preserved paths still exist (they should, but just in case)
|
|
337
|
+
for (const [path, fullPath] of Object.entries(backups)) {
|
|
338
|
+
if (!existsSync(fullPath)) {
|
|
339
|
+
console.log(chalk.yellow(`⚠️ ${path} was removed during update, restoring...`));
|
|
340
|
+
// The backup reference is the same as the original, so if it doesn't exist
|
|
341
|
+
// it means we need to recreate the structure
|
|
342
|
+
if (path === 'active-features') {
|
|
343
|
+
mkdirSync(fullPath, { recursive: true });
|
|
344
|
+
writeFileSync(join(fullPath, '.gitkeep'), '');
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Deep merge two objects - source values override target, but target values are preserved
|
|
352
|
+
* Returns the merged object and list of new keys added
|
|
353
|
+
*/
|
|
354
|
+
function deepMerge(target, source, path = '') {
|
|
355
|
+
const result = { ...target };
|
|
356
|
+
const newKeys = [];
|
|
357
|
+
|
|
358
|
+
for (const key of Object.keys(source)) {
|
|
359
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
360
|
+
|
|
361
|
+
if (!(key in target)) {
|
|
362
|
+
// New key - add it
|
|
363
|
+
result[key] = source[key];
|
|
364
|
+
newKeys.push(currentPath);
|
|
365
|
+
} else if (
|
|
366
|
+
typeof source[key] === 'object' &&
|
|
367
|
+
source[key] !== null &&
|
|
368
|
+
!Array.isArray(source[key]) &&
|
|
369
|
+
typeof target[key] === 'object' &&
|
|
370
|
+
target[key] !== null &&
|
|
371
|
+
!Array.isArray(target[key])
|
|
372
|
+
) {
|
|
373
|
+
// Both are objects - recurse
|
|
374
|
+
const merged = deepMerge(target[key], source[key], currentPath);
|
|
375
|
+
result[key] = merged.result;
|
|
376
|
+
newKeys.push(...merged.newKeys);
|
|
377
|
+
}
|
|
378
|
+
// If key exists in target and is not an object, keep target value
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return { result, newKeys };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Merge user config with new framework config
|
|
386
|
+
* Preserves user values, adds new options from framework
|
|
387
|
+
*/
|
|
388
|
+
function mergeConfigs(userConfigPath, newConfigPath) {
|
|
389
|
+
try {
|
|
390
|
+
const newConfigContent = readFileSync(newConfigPath, 'utf-8');
|
|
391
|
+
const newConfig = yaml.load(newConfigContent);
|
|
392
|
+
|
|
393
|
+
if (!existsSync(userConfigPath)) {
|
|
394
|
+
// No user config - just copy the new one
|
|
395
|
+
writeFileSync(userConfigPath, newConfigContent);
|
|
396
|
+
return { newOptions: Object.keys(newConfig).length, newKeys: ['(new config created)'] };
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const userConfigContent = readFileSync(userConfigPath, 'utf-8');
|
|
400
|
+
const userConfig = yaml.load(userConfigContent);
|
|
401
|
+
|
|
402
|
+
if (!userConfig || typeof userConfig !== 'object') {
|
|
403
|
+
// User config is empty or invalid - use new config
|
|
404
|
+
writeFileSync(userConfigPath, newConfigContent);
|
|
405
|
+
return { newOptions: Object.keys(newConfig).length, newKeys: ['(config replaced)'] };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Deep merge - user values take precedence, new keys added
|
|
409
|
+
const { result: mergedConfig, newKeys } = deepMerge(userConfig, newConfig);
|
|
410
|
+
|
|
411
|
+
if (newKeys.length > 0) {
|
|
412
|
+
// Write merged config with comment
|
|
413
|
+
const header = `# ProAgents Configuration\n# User values preserved, new options added during update\n# Last updated: ${new Date().toISOString().split('T')[0]}\n\n`;
|
|
414
|
+
const mergedYaml = yaml.dump(mergedConfig, {
|
|
415
|
+
indent: 2,
|
|
416
|
+
lineWidth: 120,
|
|
417
|
+
noRefs: true,
|
|
418
|
+
sortKeys: false
|
|
419
|
+
});
|
|
420
|
+
writeFileSync(userConfigPath, header + mergedYaml);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return { newOptions: newKeys.length, newKeys };
|
|
424
|
+
} catch (error) {
|
|
425
|
+
// If merge fails, don't break the update - just warn
|
|
426
|
+
console.log(chalk.yellow(`⚠️ Could not merge config: ${error.message}`));
|
|
427
|
+
return { newOptions: 0, newKeys: [] };
|
|
428
|
+
}
|
|
429
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proagents",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "AI-agnostic development workflow framework that automates the full software development lifecycle",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"commander": "^11.1.0",
|
|
50
|
-
"chalk": "^5.3.0"
|
|
50
|
+
"chalk": "^5.3.0",
|
|
51
|
+
"js-yaml": "^4.1.0"
|
|
51
52
|
}
|
|
52
53
|
}
|