proagents 1.0.16 → 1.0.18

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.
@@ -0,0 +1,305 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import chalk from 'chalk';
4
+ import yaml from 'js-yaml';
5
+
6
+ // Platform file mapping
7
+ const PLATFORM_FILES = {
8
+ claude: 'CLAUDE.md',
9
+ cursor: '.cursorrules',
10
+ windsurf: '.windsurfrules',
11
+ copilot: '.github/copilot-instructions.md',
12
+ chatgpt: 'CHATGPT.md',
13
+ gemini: 'GEMINI.md',
14
+ bolt: 'BOLT.md',
15
+ lovable: 'LOVABLE.md',
16
+ replit: 'REPLIT.md',
17
+ kiro: 'KIRO.md',
18
+ groq: 'GROQ.md',
19
+ antigravity: 'ANTIGRAVITY.md'
20
+ };
21
+
22
+ /**
23
+ * Check if ProAgents is installed
24
+ */
25
+ function checkInstallation(targetDir) {
26
+ const proagentsDir = join(targetDir, 'proagents');
27
+ const configPath = join(targetDir, 'proagents.config.yaml');
28
+
29
+ const checks = [];
30
+
31
+ // Check proagents folder
32
+ if (existsSync(proagentsDir)) {
33
+ checks.push({ name: 'ProAgents folder', status: 'ok', message: './proagents/ exists' });
34
+ } else {
35
+ checks.push({ name: 'ProAgents folder', status: 'error', message: './proagents/ not found. Run: npx proagents init' });
36
+ }
37
+
38
+ // Check config file
39
+ if (existsSync(configPath)) {
40
+ checks.push({ name: 'Config file', status: 'ok', message: 'proagents.config.yaml exists' });
41
+ } else {
42
+ checks.push({ name: 'Config file', status: 'warning', message: 'proagents.config.yaml not found' });
43
+ }
44
+
45
+ return checks;
46
+ }
47
+
48
+ /**
49
+ * Check config validity
50
+ */
51
+ function checkConfig(targetDir) {
52
+ const configPath = join(targetDir, 'proagents.config.yaml');
53
+ const checks = [];
54
+
55
+ if (!existsSync(configPath)) {
56
+ return [{ name: 'Config validation', status: 'skip', message: 'No config file to validate' }];
57
+ }
58
+
59
+ try {
60
+ const content = readFileSync(configPath, 'utf-8');
61
+ const config = yaml.load(content);
62
+
63
+ if (config && typeof config === 'object') {
64
+ checks.push({ name: 'Config syntax', status: 'ok', message: 'YAML is valid' });
65
+
66
+ // Check required sections
67
+ if (config.project?.name) {
68
+ checks.push({ name: 'Project name', status: 'ok', message: `"${config.project.name}"` });
69
+ } else {
70
+ checks.push({ name: 'Project name', status: 'warning', message: 'Not set' });
71
+ }
72
+
73
+ if (config.project?.type) {
74
+ checks.push({ name: 'Project type', status: 'ok', message: `"${config.project.type}"` });
75
+ } else {
76
+ checks.push({ name: 'Project type', status: 'warning', message: 'Not set' });
77
+ }
78
+ }
79
+ } catch (error) {
80
+ checks.push({ name: 'Config syntax', status: 'error', message: `Invalid YAML: ${error.message}` });
81
+ }
82
+
83
+ return checks;
84
+ }
85
+
86
+ /**
87
+ * Check AI platform files sync
88
+ */
89
+ function checkPlatformSync(targetDir) {
90
+ const configPath = join(targetDir, 'proagents.config.yaml');
91
+ const checks = [];
92
+
93
+ let configPlatforms = [];
94
+
95
+ // Read platforms from config
96
+ if (existsSync(configPath)) {
97
+ try {
98
+ const content = readFileSync(configPath, 'utf-8');
99
+ const config = yaml.load(content);
100
+ configPlatforms = config?.platforms || config?.ai_platforms || [];
101
+ } catch { }
102
+ }
103
+
104
+ // Check each platform file
105
+ const existingFiles = [];
106
+ const missingFiles = [];
107
+
108
+ for (const [platformId, fileName] of Object.entries(PLATFORM_FILES)) {
109
+ const filePath = join(targetDir, fileName);
110
+ const inConfig = configPlatforms.includes(platformId);
111
+ const fileExists = existsSync(filePath);
112
+
113
+ if (fileExists) {
114
+ existingFiles.push(platformId);
115
+ if (!inConfig) {
116
+ checks.push({ name: `${platformId}`, status: 'warning', message: `${fileName} exists but not in config` });
117
+ }
118
+ } else if (inConfig) {
119
+ missingFiles.push(platformId);
120
+ checks.push({ name: `${platformId}`, status: 'warning', message: `In config but ${fileName} missing` });
121
+ }
122
+ }
123
+
124
+ if (checks.length === 0 && existingFiles.length > 0) {
125
+ checks.push({ name: 'Platform sync', status: 'ok', message: `${existingFiles.length} platforms synced` });
126
+ } else if (existingFiles.length === 0) {
127
+ checks.push({ name: 'Platform files', status: 'warning', message: 'No AI platform files found' });
128
+ }
129
+
130
+ return checks;
131
+ }
132
+
133
+ /**
134
+ * Check for lock file
135
+ */
136
+ function checkLockFile(targetDir) {
137
+ const lockPath = join(targetDir, 'proagents', '.lock');
138
+ const checks = [];
139
+
140
+ if (existsSync(lockPath)) {
141
+ try {
142
+ const content = readFileSync(lockPath, 'utf-8');
143
+ const lock = yaml.load(content);
144
+
145
+ const expiresAt = new Date(lock.expires);
146
+ const now = new Date();
147
+
148
+ if (expiresAt < now) {
149
+ const lockedBy = lock.model ? `${lock.locked_by}:${lock.model}` : lock.locked_by;
150
+ checks.push({ name: 'Lock file', status: 'warning', message: `Expired lock by ${lockedBy}. Consider removing.` });
151
+ } else {
152
+ const lockedBy = lock.model ? `${lock.locked_by}:${lock.model}` : lock.locked_by;
153
+ checks.push({ name: 'Lock file', status: 'info', message: `Locked by ${lockedBy} for "${lock.task}"` });
154
+ }
155
+ } catch {
156
+ checks.push({ name: 'Lock file', status: 'warning', message: 'Lock file exists but is invalid' });
157
+ }
158
+ } else {
159
+ checks.push({ name: 'Lock file', status: 'ok', message: 'No active lock' });
160
+ }
161
+
162
+ return checks;
163
+ }
164
+
165
+ /**
166
+ * Check version
167
+ */
168
+ async function checkVersion() {
169
+ const checks = [];
170
+
171
+ try {
172
+ // Get current version from package.json
173
+ const packagePath = new URL('../../package.json', import.meta.url);
174
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
175
+ const currentVersion = packageJson.version;
176
+
177
+ checks.push({ name: 'Installed version', status: 'info', message: `v${currentVersion}` });
178
+
179
+ // Try to fetch latest version from npm (with timeout)
180
+ try {
181
+ const controller = new AbortController();
182
+ const timeout = setTimeout(() => controller.abort(), 3000);
183
+
184
+ const response = await fetch('https://registry.npmjs.org/proagents/latest', {
185
+ signal: controller.signal
186
+ });
187
+ clearTimeout(timeout);
188
+
189
+ if (response.ok) {
190
+ const data = await response.json();
191
+ const latestVersion = data.version;
192
+
193
+ if (currentVersion === latestVersion) {
194
+ checks.push({ name: 'Latest version', status: 'ok', message: 'Up to date' });
195
+ } else {
196
+ checks.push({ name: 'Latest version', status: 'warning', message: `v${latestVersion} available. Run: npm update -g proagents` });
197
+ }
198
+ }
199
+ } catch {
200
+ checks.push({ name: 'Version check', status: 'skip', message: 'Could not check npm registry' });
201
+ }
202
+ } catch {
203
+ checks.push({ name: 'Version', status: 'error', message: 'Could not read version' });
204
+ }
205
+
206
+ return checks;
207
+ }
208
+
209
+ /**
210
+ * Print check results
211
+ */
212
+ function printChecks(title, checks) {
213
+ console.log(chalk.bold(`\n${title}`));
214
+ console.log(chalk.gray('─'.repeat(40)));
215
+
216
+ for (const check of checks) {
217
+ let icon, color;
218
+ switch (check.status) {
219
+ case 'ok':
220
+ icon = '✓';
221
+ color = chalk.green;
222
+ break;
223
+ case 'error':
224
+ icon = '✗';
225
+ color = chalk.red;
226
+ break;
227
+ case 'warning':
228
+ icon = '⚠';
229
+ color = chalk.yellow;
230
+ break;
231
+ case 'info':
232
+ icon = 'ℹ';
233
+ color = chalk.blue;
234
+ break;
235
+ case 'skip':
236
+ icon = '○';
237
+ color = chalk.gray;
238
+ break;
239
+ default:
240
+ icon = '•';
241
+ color = chalk.white;
242
+ }
243
+
244
+ console.log(` ${color(icon)} ${check.name}: ${color(check.message)}`);
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Doctor command - check health of ProAgents installation
250
+ */
251
+ export async function doctorCommand() {
252
+ const targetDir = process.cwd();
253
+
254
+ console.log(chalk.bold('\nProAgents Doctor'));
255
+ console.log(chalk.gray('================'));
256
+ console.log(chalk.gray(`Checking: ${targetDir}\n`));
257
+
258
+ let hasErrors = false;
259
+ let hasWarnings = false;
260
+
261
+ // Run all checks
262
+ const installChecks = checkInstallation(targetDir);
263
+ printChecks('Installation', installChecks);
264
+
265
+ const configChecks = checkConfig(targetDir);
266
+ printChecks('Configuration', configChecks);
267
+
268
+ const platformChecks = checkPlatformSync(targetDir);
269
+ printChecks('AI Platforms', platformChecks);
270
+
271
+ const lockChecks = checkLockFile(targetDir);
272
+ printChecks('Lock Status', lockChecks);
273
+
274
+ const versionChecks = await checkVersion();
275
+ printChecks('Version', versionChecks);
276
+
277
+ // Count issues
278
+ const allChecks = [...installChecks, ...configChecks, ...platformChecks, ...lockChecks, ...versionChecks];
279
+ const errors = allChecks.filter(c => c.status === 'error').length;
280
+ const warnings = allChecks.filter(c => c.status === 'warning').length;
281
+
282
+ // Summary
283
+ console.log(chalk.bold('\nSummary'));
284
+ console.log(chalk.gray('─'.repeat(40)));
285
+
286
+ if (errors === 0 && warnings === 0) {
287
+ console.log(chalk.green(' ✓ All checks passed!\n'));
288
+ } else {
289
+ if (errors > 0) {
290
+ console.log(chalk.red(` ✗ ${errors} error(s) found`));
291
+ }
292
+ if (warnings > 0) {
293
+ console.log(chalk.yellow(` ⚠ ${warnings} warning(s) found`));
294
+ }
295
+ console.log('');
296
+
297
+ if (errors > 0) {
298
+ console.log(chalk.gray('Run `npx proagents init` to fix installation issues.'));
299
+ }
300
+ if (platformChecks.some(c => c.status === 'warning')) {
301
+ console.log(chalk.gray('Run `pa:ai-sync` to fix platform sync issues.'));
302
+ }
303
+ console.log('');
304
+ }
305
+ }
@@ -191,6 +191,115 @@ const PROJECT_TYPES = [
191
191
  }
192
192
  ];
193
193
 
194
+ // Project templates - pre-configured settings for common stacks
195
+ const PROJECT_TEMPLATES = {
196
+ 'nextjs-saas': {
197
+ name: 'Next.js SaaS Starter',
198
+ description: 'Full-stack SaaS with authentication, database, and payments',
199
+ project: { type: 'nextjs' },
200
+ techStack: {
201
+ api_style: 'rest',
202
+ state_management: 'zustand',
203
+ styling: 'tailwind',
204
+ database: 'postgresql',
205
+ orm: 'prisma',
206
+ auth_method: 'nextauth',
207
+ test_framework: 'vitest'
208
+ },
209
+ platforms: ['claude', 'cursor', 'copilot']
210
+ },
211
+ 'react-spa': {
212
+ name: 'React Single Page App',
213
+ description: 'Frontend-focused React application with modern tooling',
214
+ project: { type: 'react' },
215
+ techStack: {
216
+ api_style: 'rest',
217
+ state_management: 'zustand',
218
+ styling: 'tailwind',
219
+ database: 'none',
220
+ orm: 'none',
221
+ auth_method: 'jwt',
222
+ test_framework: 'vitest'
223
+ },
224
+ platforms: ['claude', 'cursor']
225
+ },
226
+ 'react-native-app': {
227
+ name: 'React Native Mobile App',
228
+ description: 'Cross-platform mobile app with Expo',
229
+ project: { type: 'react-native' },
230
+ techStack: {
231
+ api_style: 'rest',
232
+ state_management: 'zustand',
233
+ styling: 'tailwind',
234
+ database: 'supabase',
235
+ orm: 'none',
236
+ auth_method: 'supabase',
237
+ test_framework: 'jest'
238
+ },
239
+ platforms: ['claude', 'cursor']
240
+ },
241
+ 'express-api': {
242
+ name: 'Express.js REST API',
243
+ description: 'Backend API with Express, PostgreSQL, and JWT auth',
244
+ project: { type: 'express' },
245
+ techStack: {
246
+ api_style: 'rest',
247
+ state_management: 'none',
248
+ styling: 'none',
249
+ database: 'postgresql',
250
+ orm: 'prisma',
251
+ auth_method: 'jwt',
252
+ test_framework: 'jest'
253
+ },
254
+ platforms: ['claude', 'cursor']
255
+ },
256
+ 'nestjs-api': {
257
+ name: 'NestJS Enterprise API',
258
+ description: 'Enterprise-grade API with NestJS and TypeORM',
259
+ project: { type: 'nestjs' },
260
+ techStack: {
261
+ api_style: 'rest',
262
+ state_management: 'none',
263
+ styling: 'none',
264
+ database: 'postgresql',
265
+ orm: 'typeorm',
266
+ auth_method: 'jwt',
267
+ test_framework: 'jest'
268
+ },
269
+ platforms: ['claude', 'cursor']
270
+ },
271
+ 'vue-spa': {
272
+ name: 'Vue.js Single Page App',
273
+ description: 'Vue 3 with Composition API and Pinia',
274
+ project: { type: 'vue' },
275
+ techStack: {
276
+ api_style: 'rest',
277
+ state_management: 'none',
278
+ styling: 'tailwind',
279
+ database: 'none',
280
+ orm: 'none',
281
+ auth_method: 'jwt',
282
+ test_framework: 'vitest'
283
+ },
284
+ platforms: ['claude', 'cursor']
285
+ },
286
+ 'python-fastapi': {
287
+ name: 'Python FastAPI',
288
+ description: 'Modern Python API with FastAPI and SQLAlchemy',
289
+ project: { type: 'python' },
290
+ techStack: {
291
+ api_style: 'rest',
292
+ state_management: 'none',
293
+ styling: 'none',
294
+ database: 'postgresql',
295
+ orm: 'none',
296
+ auth_method: 'jwt',
297
+ test_framework: 'pytest'
298
+ },
299
+ platforms: ['claude', 'cursor']
300
+ }
301
+ };
302
+
194
303
  // Tech stack configuration options
195
304
  const TECH_STACK_OPTIONS = {
196
305
  api_style: {
@@ -592,6 +701,41 @@ async function promptProjectConfig(targetDir) {
592
701
  };
593
702
  }
594
703
 
704
+ /**
705
+ * List available templates
706
+ */
707
+ export function listTemplates() {
708
+ console.log(chalk.bold('\nAvailable Project Templates'));
709
+ console.log(chalk.gray('─'.repeat(50)));
710
+
711
+ for (const [id, template] of Object.entries(PROJECT_TEMPLATES)) {
712
+ console.log(`\n ${chalk.cyan(id)}`);
713
+ console.log(` ${template.name}`);
714
+ console.log(chalk.gray(` ${template.description}`));
715
+ }
716
+
717
+ console.log(chalk.gray('\n\nUsage: npx proagents init --template <name>\n'));
718
+ }
719
+
720
+ /**
721
+ * Apply a project template
722
+ */
723
+ function applyTemplate(templateId, targetDir) {
724
+ const template = PROJECT_TEMPLATES[templateId];
725
+ if (!template) {
726
+ return null;
727
+ }
728
+
729
+ // Return config object that matches what promptProjectConfig returns
730
+ return {
731
+ name: basename(targetDir),
732
+ type: template.project.type,
733
+ typeName: PROJECT_TYPES.find(t => t.id === template.project.type)?.name || template.project.type,
734
+ techStack: template.techStack,
735
+ platforms: template.platforms
736
+ };
737
+ }
738
+
595
739
  /**
596
740
  * Save project config to proagents.config.yaml
597
741
  */
@@ -777,8 +921,27 @@ No releases yet. Use \`pa:release\` to generate release notes.
777
921
  console.log(chalk.green('✓ Created RELEASE_NOTES.md'));
778
922
  }
779
923
 
780
- // Interactive project configuration
781
- const projectConfig = await promptProjectConfig(targetDir);
924
+ // Project configuration - either from template or interactive prompt
925
+ let projectConfig;
926
+
927
+ if (options.listTemplates) {
928
+ listTemplates();
929
+ return;
930
+ }
931
+
932
+ if (options.template) {
933
+ // Use template
934
+ projectConfig = applyTemplate(options.template, targetDir);
935
+ if (!projectConfig) {
936
+ console.log(chalk.red(`\nError: Template "${options.template}" not found.`));
937
+ listTemplates();
938
+ return;
939
+ }
940
+ console.log(chalk.green(`\n✓ Using template: ${PROJECT_TEMPLATES[options.template].name}`));
941
+ } else {
942
+ // Interactive prompts
943
+ projectConfig = await promptProjectConfig(targetDir);
944
+ }
782
945
 
783
946
  // Add ProAgents section to README.md (AI tools auto-read this)
784
947
  const readmePath = join(targetDir, 'README.md');
@@ -812,8 +975,14 @@ For detailed commands, see \`./proagents/PROAGENTS.md\`
812
975
  console.log(chalk.green('✓ Created README.md with ProAgents commands'));
813
976
  }
814
977
 
815
- // Interactive AI platform selection
816
- const selectedPlatforms = await selectPlatforms();
978
+ // AI platform selection - from template or interactive
979
+ let selectedPlatforms;
980
+ if (options.template && projectConfig.platforms) {
981
+ selectedPlatforms = projectConfig.platforms;
982
+ console.log(chalk.gray(` Using template platforms: ${selectedPlatforms.join(', ')}`));
983
+ } else {
984
+ selectedPlatforms = await selectPlatforms();
985
+ }
817
986
 
818
987
  // Copy AI instruction files for selected platforms (merges with existing files)
819
988
  const aiResults = copyPlatformFiles(selectedPlatforms, sourceDir, targetDir);
@@ -0,0 +1,180 @@
1
+ import { existsSync, readFileSync, writeFileSync, cpSync, mkdirSync, rmSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import chalk from 'chalk';
5
+ import { createInterface } from 'readline';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ // Files to preserve during upgrade (user customizations)
11
+ const PRESERVE_FILES = [
12
+ 'proagents.config.yaml',
13
+ 'activity.log',
14
+ 'handoff.md',
15
+ '.lock',
16
+ 'active-features/_index.json',
17
+ 'active-features/.gitkeep'
18
+ ];
19
+
20
+ // Folders to preserve (user data)
21
+ const PRESERVE_FOLDERS = [
22
+ 'active-features'
23
+ ];
24
+
25
+ /**
26
+ * Backup user files before upgrade
27
+ */
28
+ function backupUserFiles(targetDir) {
29
+ const backups = {};
30
+
31
+ for (const file of PRESERVE_FILES) {
32
+ const filePath = join(targetDir, 'proagents', file);
33
+ if (existsSync(filePath)) {
34
+ try {
35
+ backups[file] = readFileSync(filePath, 'utf-8');
36
+ } catch { }
37
+ }
38
+ }
39
+
40
+ return backups;
41
+ }
42
+
43
+ /**
44
+ * Restore user files after upgrade
45
+ */
46
+ function restoreUserFiles(targetDir, backups) {
47
+ for (const [file, content] of Object.entries(backups)) {
48
+ const filePath = join(targetDir, 'proagents', file);
49
+ try {
50
+ // Ensure directory exists
51
+ mkdirSync(dirname(filePath), { recursive: true });
52
+ writeFileSync(filePath, content);
53
+ } catch (error) {
54
+ console.log(chalk.yellow(` ⚠ Could not restore ${file}: ${error.message}`));
55
+ }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Get current installed version
61
+ */
62
+ function getCurrentVersion(targetDir) {
63
+ // Try to read version from a marker file or package
64
+ const markerPath = join(targetDir, 'proagents', '.version');
65
+ if (existsSync(markerPath)) {
66
+ try {
67
+ return readFileSync(markerPath, 'utf-8').trim();
68
+ } catch { }
69
+ }
70
+ return 'unknown';
71
+ }
72
+
73
+ /**
74
+ * Get package version
75
+ */
76
+ function getPackageVersion() {
77
+ try {
78
+ const packagePath = join(__dirname, '..', '..', 'package.json');
79
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
80
+ return packageJson.version;
81
+ } catch {
82
+ return 'unknown';
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Upgrade command - upgrade proagents folder to latest version
88
+ */
89
+ export async function upgradeCommand(options = {}) {
90
+ const targetDir = process.cwd();
91
+ const proagentsDir = join(targetDir, 'proagents');
92
+ const sourceDir = join(__dirname, '..', '..', 'proagents');
93
+
94
+ console.log(chalk.bold('\nProAgents Upgrade'));
95
+ console.log(chalk.gray('=================\n'));
96
+
97
+ // Check if proagents folder exists
98
+ if (!existsSync(proagentsDir)) {
99
+ console.log(chalk.red('Error: ProAgents not found in this project.'));
100
+ console.log(chalk.gray('Run `npx proagents init` to initialize.\n'));
101
+ return;
102
+ }
103
+
104
+ const currentVersion = getCurrentVersion(targetDir);
105
+ const packageVersion = getPackageVersion();
106
+
107
+ console.log(`Current version: ${chalk.yellow(currentVersion)}`);
108
+ console.log(`Package version: ${chalk.green(packageVersion)}`);
109
+ console.log('');
110
+
111
+ // Confirm upgrade unless --force
112
+ if (!options.force) {
113
+ const rl = createInterface({
114
+ input: process.stdin,
115
+ output: process.stdout
116
+ });
117
+
118
+ const answer = await new Promise(resolve => {
119
+ rl.question(chalk.yellow('Upgrade proagents folder? (y/N) '), resolve);
120
+ });
121
+ rl.close();
122
+
123
+ if (answer.toLowerCase() !== 'y') {
124
+ console.log(chalk.gray('\nUpgrade cancelled.\n'));
125
+ return;
126
+ }
127
+ }
128
+
129
+ console.log('');
130
+
131
+ // Step 1: Backup user files
132
+ console.log(chalk.cyan('Backing up user files...'));
133
+ const backups = backupUserFiles(targetDir);
134
+ const backedUpCount = Object.keys(backups).length;
135
+ console.log(chalk.gray(` Backed up ${backedUpCount} file(s)`));
136
+
137
+ // Step 2: Remove old proagents folder (except active-features)
138
+ console.log(chalk.cyan('Removing old files...'));
139
+ try {
140
+ // We'll do a selective removal to preserve active-features folder structure
141
+ rmSync(proagentsDir, { recursive: true, force: true });
142
+ console.log(chalk.gray(' Old proagents folder removed'));
143
+ } catch (error) {
144
+ console.log(chalk.yellow(` ⚠ Could not remove old folder: ${error.message}`));
145
+ }
146
+
147
+ // Step 3: Copy new proagents folder
148
+ console.log(chalk.cyan('Installing new version...'));
149
+ try {
150
+ cpSync(sourceDir, proagentsDir, { recursive: true });
151
+ console.log(chalk.gray(' New proagents folder installed'));
152
+ } catch (error) {
153
+ console.log(chalk.red(` ✗ Error: ${error.message}`));
154
+ return;
155
+ }
156
+
157
+ // Step 4: Restore user files
158
+ console.log(chalk.cyan('Restoring user files...'));
159
+ restoreUserFiles(targetDir, backups);
160
+ console.log(chalk.gray(` Restored ${backedUpCount} file(s)`));
161
+
162
+ // Step 5: Write version marker
163
+ try {
164
+ writeFileSync(join(proagentsDir, '.version'), packageVersion);
165
+ } catch { }
166
+
167
+ // Summary
168
+ console.log(chalk.bold('\nUpgrade Complete'));
169
+ console.log(chalk.gray('─'.repeat(40)));
170
+ console.log(chalk.green(` ✓ Upgraded to v${packageVersion}`));
171
+ console.log('');
172
+
173
+ console.log(chalk.bold('Preserved files:'));
174
+ for (const file of Object.keys(backups)) {
175
+ console.log(chalk.gray(` • ${file}`));
176
+ }
177
+ console.log('');
178
+
179
+ console.log(chalk.gray('Run `npx proagents doctor` to verify installation.\n'));
180
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proagents",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
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",