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.
- package/README.md +249 -76
- package/bin/proagents.js +17 -0
- package/lib/commands/doctor.js +305 -0
- package/lib/commands/init.js +173 -4
- package/lib/commands/upgrade.js +180 -0
- package/package.json +1 -1
- package/proagents/AI_INSTRUCTIONS.md +265 -8
- package/proagents/PROAGENTS.md +26 -0
- package/proagents/activity.log +11 -0
- package/proagents/context.md +39 -0
- package/proagents/custom-commands.yaml +59 -0
- package/proagents/decisions.md +43 -0
- package/proagents/errors.md +36 -0
- package/proagents/feedback.md +49 -0
- package/proagents/handoff.md +33 -0
- package/proagents/history.log +12 -0
- package/proagents/sessions/README.md +5 -0
- package/proagents/watchlist.yaml +39 -0
|
@@ -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
|
+
}
|
package/lib/commands/init.js
CHANGED
|
@@ -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
|
-
//
|
|
781
|
-
|
|
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
|
-
//
|
|
816
|
-
|
|
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
|
+
}
|