proagents 1.0.10 → 1.0.12

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/bin/proagents.js CHANGED
@@ -6,6 +6,9 @@ import { featureCommand } from '../lib/commands/feature.js';
6
6
  import { fixCommand } from '../lib/commands/fix.js';
7
7
  import { statusCommand } from '../lib/commands/status.js';
8
8
  import { helpCommand } from '../lib/commands/help.js';
9
+ import { aiAddCommand, aiListCommand, aiRemoveCommand } from '../lib/commands/ai.js';
10
+ import { uninstallCommand } from '../lib/commands/uninstall.js';
11
+ import { configListCommand, configShowCommand, configEditCommand, configSetCommand, configGetCommand, configSetupCommand, configCustomizeCommand } from '../lib/commands/config.js';
9
12
  import { readFileSync } from 'fs';
10
13
  import { fileURLToPath } from 'url';
11
14
  import { dirname, join } from 'path';
@@ -81,6 +84,73 @@ program
81
84
  console.log('GitHub: https://github.com/prakashpro3/proAgents\n');
82
85
  });
83
86
 
87
+ // AI platform commands
88
+ const ai = program
89
+ .command('ai')
90
+ .description('Manage AI platform instruction files');
91
+
92
+ ai
93
+ .command('add')
94
+ .description('Add more AI platforms')
95
+ .action(aiAddCommand);
96
+
97
+ ai
98
+ .command('list')
99
+ .description('List installed AI platforms')
100
+ .action(aiListCommand);
101
+
102
+ ai
103
+ .command('remove')
104
+ .description('Remove AI platforms from config')
105
+ .action(aiRemoveCommand);
106
+
107
+ // Config commands
108
+ const config = program
109
+ .command('config')
110
+ .description('Manage ProAgents configuration');
111
+
112
+ config
113
+ .command('list')
114
+ .description('Show all configurable options')
115
+ .action(configListCommand);
116
+
117
+ config
118
+ .command('show')
119
+ .description('Show current config values')
120
+ .action(configShowCommand);
121
+
122
+ config
123
+ .command('edit')
124
+ .description('Info on how to edit config')
125
+ .action(configEditCommand);
126
+
127
+ config
128
+ .command('set <key> <value>')
129
+ .description('Set a config value (e.g., checkpoints.after_analysis true)')
130
+ .action(configSetCommand);
131
+
132
+ config
133
+ .command('get <key>')
134
+ .description('Get a config value (e.g., checkpoints.after_analysis)')
135
+ .action(configGetCommand);
136
+
137
+ config
138
+ .command('setup')
139
+ .description('Interactive configuration wizard')
140
+ .action(configSetupCommand);
141
+
142
+ config
143
+ .command('customize')
144
+ .description('Copy templates to create custom configurations')
145
+ .action(configCustomizeCommand);
146
+
147
+ // Uninstall command
148
+ program
149
+ .command('uninstall')
150
+ .description('Remove ProAgents from current project')
151
+ .option('-f, --force', 'Skip confirmation prompt')
152
+ .action(uninstallCommand);
153
+
84
154
  // Help command (custom)
85
155
  program
86
156
  .command('commands')
@@ -0,0 +1,454 @@
1
+ import { existsSync, cpSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { createInterface } from 'readline';
5
+ import chalk from 'chalk';
6
+ import yaml from 'js-yaml';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ // AI Platform definitions grouped by type
12
+ export const AI_PLATFORMS = {
13
+ ide: {
14
+ label: 'IDE-based AI Assistants',
15
+ platforms: [
16
+ { id: 'claude', name: 'Claude Code', file: 'CLAUDE.md', desc: 'Anthropic Claude in terminal/IDE' },
17
+ { id: 'cursor', name: 'Cursor', file: '.cursorrules', desc: 'Cursor AI IDE' },
18
+ { id: 'windsurf', name: 'Windsurf', file: '.windsurfrules', desc: 'Codeium Windsurf IDE' },
19
+ { id: 'copilot', name: 'GitHub Copilot', file: '.github/copilot-instructions.md', desc: 'GitHub Copilot' },
20
+ { id: 'kiro', name: 'AWS Kiro', file: 'KIRO.md', desc: 'AWS Kiro IDE' },
21
+ { id: 'antigravity', name: 'Antigravity', file: 'ANTIGRAVITY.md', desc: 'Antigravity IDE (Gemini/Claude)' },
22
+ ]
23
+ },
24
+ web: {
25
+ label: 'Web-based AI Platforms',
26
+ platforms: [
27
+ { id: 'chatgpt', name: 'ChatGPT / Codex', file: 'CHATGPT.md', desc: 'OpenAI ChatGPT' },
28
+ { id: 'gemini', name: 'Gemini', file: 'GEMINI.md', desc: 'Google Gemini' },
29
+ { id: 'replit', name: 'Replit AI', file: 'REPLIT.md', desc: 'Replit Ghostwriter' },
30
+ { id: 'bolt', name: 'Bolt.new', file: 'BOLT.md', desc: 'StackBlitz Bolt' },
31
+ { id: 'lovable', name: 'Lovable', file: 'LOVABLE.md', desc: 'Lovable (GPT Engineer)' },
32
+ { id: 'groq', name: 'Groq', file: 'GROQ.md', desc: 'Groq fast inference' },
33
+ ]
34
+ }
35
+ };
36
+
37
+ // Get all platforms as flat array
38
+ export function getAllPlatforms() {
39
+ return [
40
+ ...AI_PLATFORMS.ide.platforms,
41
+ ...AI_PLATFORMS.web.platforms,
42
+ ];
43
+ }
44
+
45
+ // Get platform by ID
46
+ export function getPlatformById(id) {
47
+ return getAllPlatforms().find(p => p.id === id);
48
+ }
49
+
50
+ /**
51
+ * Interactive platform selection using readline
52
+ */
53
+ export async function selectPlatforms() {
54
+ const rl = createInterface({
55
+ input: process.stdin,
56
+ output: process.stdout
57
+ });
58
+
59
+ const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
60
+
61
+ console.log('\n' + chalk.bold('Which AI platform(s) do you use?'));
62
+ console.log(chalk.gray('(Enter numbers separated by commas, or "all" for all platforms)\n'));
63
+
64
+ let index = 1;
65
+ const indexMap = {};
66
+
67
+ // IDE-based platforms
68
+ console.log(chalk.cyan.bold(` ${AI_PLATFORMS.ide.label}:`));
69
+ for (const platform of AI_PLATFORMS.ide.platforms) {
70
+ console.log(chalk.white(` ${index}. ${platform.name}`) + chalk.gray(` - ${platform.desc}`));
71
+ indexMap[index] = platform.id;
72
+ index++;
73
+ }
74
+
75
+ console.log('');
76
+
77
+ // Web-based platforms
78
+ console.log(chalk.cyan.bold(` ${AI_PLATFORMS.web.label}:`));
79
+ for (const platform of AI_PLATFORMS.web.platforms) {
80
+ console.log(chalk.white(` ${index}. ${platform.name}`) + chalk.gray(` - ${platform.desc}`));
81
+ indexMap[index] = platform.id;
82
+ index++;
83
+ }
84
+
85
+ console.log('');
86
+
87
+ const answer = await question(chalk.yellow('Your selection (e.g., 1,2,3 or "all"): '));
88
+ rl.close();
89
+
90
+ if (answer.toLowerCase() === 'all') {
91
+ return getAllPlatforms().map(p => p.id);
92
+ }
93
+
94
+ const selected = [];
95
+ const numbers = answer.split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
96
+
97
+ for (const num of numbers) {
98
+ if (indexMap[num]) {
99
+ selected.push(indexMap[num]);
100
+ }
101
+ }
102
+
103
+ return selected.length > 0 ? selected : ['claude']; // Default to Claude if nothing selected
104
+ }
105
+
106
+ // ProAgents section markers
107
+ const PROAGENTS_START = '<!-- PROAGENTS:START -->';
108
+ const PROAGENTS_END = '<!-- PROAGENTS:END -->';
109
+
110
+ /**
111
+ * Wrap ProAgents content with markers
112
+ */
113
+ function wrapWithMarkers(content) {
114
+ return `${PROAGENTS_START}\n${content}\n${PROAGENTS_END}`;
115
+ }
116
+
117
+ /**
118
+ * Extract ProAgents section from content
119
+ */
120
+ function extractProagentsSection(content) {
121
+ const startIdx = content.indexOf(PROAGENTS_START);
122
+ const endIdx = content.indexOf(PROAGENTS_END);
123
+
124
+ if (startIdx !== -1 && endIdx !== -1) {
125
+ return {
126
+ before: content.substring(0, startIdx),
127
+ proagents: content.substring(startIdx, endIdx + PROAGENTS_END.length),
128
+ after: content.substring(endIdx + PROAGENTS_END.length)
129
+ };
130
+ }
131
+ return null;
132
+ }
133
+
134
+ /**
135
+ * Merge ProAgents instructions with existing file content
136
+ * - If file doesn't exist: create with ProAgents content
137
+ * - If file exists without ProAgents section: append ProAgents section
138
+ * - If file exists with ProAgents section: update only ProAgents section
139
+ */
140
+ function mergeAIInstructions(sourcePath, targetPath) {
141
+ const sourceContent = readFileSync(sourcePath, 'utf-8');
142
+ const wrappedSource = wrapWithMarkers(sourceContent);
143
+
144
+ if (!existsSync(targetPath)) {
145
+ // File doesn't exist - create new with wrapped content
146
+ writeFileSync(targetPath, wrappedSource);
147
+ return 'created';
148
+ }
149
+
150
+ const existingContent = readFileSync(targetPath, 'utf-8');
151
+ const sections = extractProagentsSection(existingContent);
152
+
153
+ if (sections) {
154
+ // ProAgents section exists - update it only
155
+ const newContent = sections.before + wrappedSource + sections.after;
156
+ writeFileSync(targetPath, newContent);
157
+ return 'updated';
158
+ } else {
159
+ // No ProAgents section - append to existing content
160
+ const newContent = existingContent.trim() + '\n\n' + wrappedSource + '\n';
161
+ writeFileSync(targetPath, newContent);
162
+ return 'merged';
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Copy AI instruction files for selected platforms
168
+ * Merges with existing files instead of replacing them
169
+ * @param {string[]} selectedIds - Platform IDs to copy
170
+ * @param {string} sourceDir - Source directory (proagents folder)
171
+ * @param {string} targetDir - Target directory (project root)
172
+ */
173
+ export function copyPlatformFiles(selectedIds, sourceDir, targetDir) {
174
+ const results = { created: [], updated: [], merged: [], failed: [] };
175
+
176
+ for (const id of selectedIds) {
177
+ const platform = getPlatformById(id);
178
+ if (!platform) continue;
179
+
180
+ const sourcePath = join(sourceDir, platform.file);
181
+ let targetPath;
182
+
183
+ // Handle .github/copilot-instructions.md specially
184
+ if (platform.file.startsWith('.github/')) {
185
+ const githubDir = join(targetDir, '.github');
186
+ targetPath = join(targetDir, platform.file);
187
+
188
+ if (!existsSync(githubDir)) {
189
+ mkdirSync(githubDir, { recursive: true });
190
+ }
191
+ } else {
192
+ targetPath = join(targetDir, platform.file);
193
+ }
194
+
195
+ try {
196
+ if (existsSync(sourcePath)) {
197
+ const result = mergeAIInstructions(sourcePath, targetPath);
198
+ if (result === 'created') {
199
+ results.created.push(platform.name);
200
+ } else if (result === 'updated') {
201
+ results.updated.push(platform.name);
202
+ } else if (result === 'merged') {
203
+ results.merged.push(platform.name);
204
+ }
205
+ }
206
+ } catch (error) {
207
+ results.failed.push(platform.name);
208
+ }
209
+ }
210
+
211
+ return results;
212
+ }
213
+
214
+ /**
215
+ * Save selected platforms to config
216
+ */
217
+ export function savePlatformConfig(selectedIds, configPath) {
218
+ let config = {};
219
+
220
+ if (existsSync(configPath)) {
221
+ try {
222
+ const content = readFileSync(configPath, 'utf-8');
223
+ config = yaml.load(content) || {};
224
+ } catch {
225
+ config = {};
226
+ }
227
+ }
228
+
229
+ config.ai_platforms = selectedIds;
230
+
231
+ const yamlContent = yaml.dump(config, { indent: 2, lineWidth: 120 });
232
+ writeFileSync(configPath, yamlContent);
233
+ }
234
+
235
+ /**
236
+ * Load selected platforms from config
237
+ */
238
+ export function loadPlatformConfig(configPath) {
239
+ if (!existsSync(configPath)) return [];
240
+
241
+ try {
242
+ const content = readFileSync(configPath, 'utf-8');
243
+ const config = yaml.load(content) || {};
244
+ return config.ai_platforms || [];
245
+ } catch {
246
+ return [];
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Show available platforms that can be added
252
+ */
253
+ export function showAvailablePlatforms(currentIds) {
254
+ console.log('\n' + chalk.bold('Available AI Platforms:\n'));
255
+
256
+ let index = 1;
257
+ const available = [];
258
+
259
+ // IDE-based platforms
260
+ console.log(chalk.cyan.bold(` ${AI_PLATFORMS.ide.label}:`));
261
+ for (const platform of AI_PLATFORMS.ide.platforms) {
262
+ const status = currentIds.includes(platform.id)
263
+ ? chalk.green(' ✓ (installed)')
264
+ : chalk.gray(' (not installed)');
265
+ console.log(chalk.white(` ${index}. ${platform.name}`) + status);
266
+ if (!currentIds.includes(platform.id)) {
267
+ available.push({ index, platform });
268
+ }
269
+ index++;
270
+ }
271
+
272
+ console.log('');
273
+
274
+ // Web-based platforms
275
+ console.log(chalk.cyan.bold(` ${AI_PLATFORMS.web.label}:`));
276
+ for (const platform of AI_PLATFORMS.web.platforms) {
277
+ const status = currentIds.includes(platform.id)
278
+ ? chalk.green(' ✓ (installed)')
279
+ : chalk.gray(' (not installed)');
280
+ console.log(chalk.white(` ${index}. ${platform.name}`) + status);
281
+ if (!currentIds.includes(platform.id)) {
282
+ available.push({ index, platform });
283
+ }
284
+ index++;
285
+ }
286
+
287
+ return available;
288
+ }
289
+
290
+ /**
291
+ * Command: proagents ai add
292
+ */
293
+ export async function aiAddCommand() {
294
+ const targetDir = process.cwd();
295
+ const proagentsDir = join(targetDir, 'proagents');
296
+ const sourceDir = join(__dirname, '..', '..', 'proagents');
297
+ const configPath = join(proagentsDir, 'proagents.config.yaml');
298
+
299
+ // Check if proagents is initialized
300
+ if (!existsSync(proagentsDir)) {
301
+ console.log(chalk.red('\n✗ ProAgents not initialized. Run "proagents init" first.\n'));
302
+ return;
303
+ }
304
+
305
+ // Load current platforms
306
+ const currentIds = loadPlatformConfig(configPath);
307
+
308
+ console.log(chalk.bold.blue('\nProAgents - Add AI Platform'));
309
+ console.log(chalk.blue('===========================\n'));
310
+
311
+ // Show available platforms
312
+ const available = showAvailablePlatforms(currentIds);
313
+
314
+ if (available.length === 0) {
315
+ console.log(chalk.green('\n✓ All AI platforms are already installed!\n'));
316
+ return;
317
+ }
318
+
319
+ const rl = createInterface({
320
+ input: process.stdin,
321
+ output: process.stdout
322
+ });
323
+
324
+ const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
325
+
326
+ console.log('');
327
+ const answer = await question(chalk.yellow('Enter platform number(s) to add (e.g., 1,2,3): '));
328
+ rl.close();
329
+
330
+ const numbers = answer.split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
331
+ const toAdd = [];
332
+
333
+ for (const num of numbers) {
334
+ const found = available.find(a => a.index === num);
335
+ if (found) {
336
+ toAdd.push(found.platform.id);
337
+ }
338
+ }
339
+
340
+ if (toAdd.length === 0) {
341
+ console.log(chalk.yellow('\nNo platforms selected.\n'));
342
+ return;
343
+ }
344
+
345
+ // Copy files for new platforms
346
+ const results = copyPlatformFiles(toAdd, sourceDir, targetDir);
347
+
348
+ // Update config
349
+ const newIds = [...currentIds, ...toAdd];
350
+ savePlatformConfig(newIds, configPath);
351
+
352
+ // Show results
353
+ if (results.created.length > 0) {
354
+ console.log(chalk.green(`\n✓ Created: ${results.created.join(', ')}`));
355
+ }
356
+ if (results.updated.length > 0) {
357
+ console.log(chalk.green(`✓ Updated: ${results.updated.join(', ')}`));
358
+ }
359
+ if (results.merged.length > 0) {
360
+ console.log(chalk.green(`✓ Merged with existing: ${results.merged.join(', ')}`));
361
+ }
362
+
363
+ console.log(chalk.gray('\nAI instruction files added to project root.'));
364
+ console.log(chalk.gray('Config updated in proagents/proagents.config.yaml\n'));
365
+ }
366
+
367
+ /**
368
+ * Command: proagents ai list
369
+ */
370
+ export function aiListCommand() {
371
+ const targetDir = process.cwd();
372
+ const proagentsDir = join(targetDir, 'proagents');
373
+ const configPath = join(proagentsDir, 'proagents.config.yaml');
374
+
375
+ const currentIds = loadPlatformConfig(configPath);
376
+
377
+ console.log(chalk.bold.blue('\nProAgents - AI Platforms'));
378
+ console.log(chalk.blue('========================\n'));
379
+
380
+ showAvailablePlatforms(currentIds);
381
+ console.log('');
382
+ }
383
+
384
+ /**
385
+ * Command: proagents ai remove
386
+ */
387
+ export async function aiRemoveCommand() {
388
+ const targetDir = process.cwd();
389
+ const proagentsDir = join(targetDir, 'proagents');
390
+ const configPath = join(proagentsDir, 'proagents.config.yaml');
391
+
392
+ if (!existsSync(proagentsDir)) {
393
+ console.log(chalk.red('\n✗ ProAgents not initialized.\n'));
394
+ return;
395
+ }
396
+
397
+ const currentIds = loadPlatformConfig(configPath);
398
+
399
+ if (currentIds.length === 0) {
400
+ console.log(chalk.yellow('\nNo AI platforms configured.\n'));
401
+ return;
402
+ }
403
+
404
+ console.log(chalk.bold.blue('\nProAgents - Remove AI Platform'));
405
+ console.log(chalk.blue('==============================\n'));
406
+
407
+ console.log(chalk.cyan('Currently installed platforms:\n'));
408
+
409
+ let index = 1;
410
+ const indexMap = {};
411
+
412
+ for (const id of currentIds) {
413
+ const platform = getPlatformById(id);
414
+ if (platform) {
415
+ console.log(chalk.white(` ${index}. ${platform.name}`));
416
+ indexMap[index] = id;
417
+ index++;
418
+ }
419
+ }
420
+
421
+ const rl = createInterface({
422
+ input: process.stdin,
423
+ output: process.stdout
424
+ });
425
+
426
+ const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
427
+
428
+ console.log('');
429
+ const answer = await question(chalk.yellow('Enter platform number(s) to remove (e.g., 1,2): '));
430
+ rl.close();
431
+
432
+ const numbers = answer.split(',').map(s => parseInt(s.trim())).filter(n => !isNaN(n));
433
+ const toRemove = [];
434
+
435
+ for (const num of numbers) {
436
+ if (indexMap[num]) {
437
+ toRemove.push(indexMap[num]);
438
+ }
439
+ }
440
+
441
+ if (toRemove.length === 0) {
442
+ console.log(chalk.yellow('\nNo platforms selected.\n'));
443
+ return;
444
+ }
445
+
446
+ // Remove from config (don't delete files - user might have customized them)
447
+ const newIds = currentIds.filter(id => !toRemove.includes(id));
448
+ savePlatformConfig(newIds, configPath);
449
+
450
+ const removedNames = toRemove.map(id => getPlatformById(id)?.name).filter(Boolean);
451
+ console.log(chalk.green(`\n✓ Removed from config: ${removedNames.join(', ')}`));
452
+ console.log(chalk.gray('Note: AI instruction files in project root were not deleted.'));
453
+ console.log(chalk.gray('You can manually delete them if needed.\n'));
454
+ }