proagents 1.6.13 → 1.6.16
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/.proagents/.learning/global/common-patterns.template.json +60 -0
- package/.proagents/.learning/global/user-preferences.template.json +78 -0
- package/.proagents/AGENTS.md +55 -0
- package/.proagents/AI_INSTRUCTIONS.md +33 -0
- package/.proagents/active-features/_index.template.json +75 -0
- package/.proagents/changelog/_recent.template.md +51 -0
- package/.proagents/worklog/_context.template.md +82 -0
- package/.proagents/worklog/ai-stats.template.json +54 -0
- package/README.md +51 -498
- package/bin/proagents.js +9 -1
- package/lib/commands/ai.js +103 -11
- package/lib/commands/init.js +169 -1
- package/lib/commands/version.js +25 -4
- package/package.json +1 -1
package/lib/commands/ai.js
CHANGED
|
@@ -24,13 +24,19 @@ export const AI_PLATFORMS = {
|
|
|
24
24
|
web: {
|
|
25
25
|
label: 'Web-based AI Platforms',
|
|
26
26
|
platforms: [
|
|
27
|
-
{ id: 'chatgpt', name: 'ChatGPT
|
|
27
|
+
{ id: 'chatgpt', name: 'ChatGPT', file: 'CHATGPT.md', desc: 'OpenAI ChatGPT' },
|
|
28
28
|
{ id: 'gemini', name: 'Gemini', file: 'GEMINI.md', desc: 'Google Gemini' },
|
|
29
29
|
{ id: 'replit', name: 'Replit AI', file: 'REPLIT.md', desc: 'Replit Ghostwriter' },
|
|
30
30
|
{ id: 'bolt', name: 'Bolt.new', file: 'BOLT.md', desc: 'StackBlitz Bolt' },
|
|
31
31
|
{ id: 'lovable', name: 'Lovable', file: 'LOVABLE.md', desc: 'Lovable (GPT Engineer)' },
|
|
32
32
|
{ id: 'groq', name: 'Groq', file: 'GROQ.md', desc: 'Groq fast inference' },
|
|
33
33
|
]
|
|
34
|
+
},
|
|
35
|
+
cli: {
|
|
36
|
+
label: 'CLI-based AI Agents',
|
|
37
|
+
platforms: [
|
|
38
|
+
{ id: 'codex', name: 'Codex CLI', file: 'AGENTS.md', desc: 'OpenAI Codex CLI' },
|
|
39
|
+
]
|
|
34
40
|
}
|
|
35
41
|
};
|
|
36
42
|
|
|
@@ -39,6 +45,7 @@ export function getAllPlatforms() {
|
|
|
39
45
|
return [
|
|
40
46
|
...AI_PLATFORMS.ide.platforms,
|
|
41
47
|
...AI_PLATFORMS.web.platforms,
|
|
48
|
+
...AI_PLATFORMS.cli.platforms,
|
|
42
49
|
];
|
|
43
50
|
}
|
|
44
51
|
|
|
@@ -47,6 +54,39 @@ export function getPlatformById(id) {
|
|
|
47
54
|
return getAllPlatforms().find(p => p.id === id);
|
|
48
55
|
}
|
|
49
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Detect current IDE from environment variables
|
|
59
|
+
* Returns platform ID if detected and it's in our AI_PLATFORMS config, null otherwise
|
|
60
|
+
* Note: VS Code is NOT an AI platform, so we skip it even if detected
|
|
61
|
+
*/
|
|
62
|
+
export function detectIDE() {
|
|
63
|
+
// Cursor IDE - check for Cursor-specific env vars
|
|
64
|
+
if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL || process.env.CURSOR_SESSION_ID) {
|
|
65
|
+
return 'cursor';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Windsurf IDE - Codeium's IDE
|
|
69
|
+
if (process.env.WINDSURF_SESSION_ID || process.env.CODEIUM_API_KEY ||
|
|
70
|
+
(process.env.TERM_PROGRAM && process.env.TERM_PROGRAM.toLowerCase().includes('windsurf'))) {
|
|
71
|
+
return 'windsurf';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// AWS Kiro IDE
|
|
75
|
+
if (process.env.KIRO_SESSION || process.env.KIRO_API_KEY ||
|
|
76
|
+
(process.env.TERM_PROGRAM && process.env.TERM_PROGRAM.toLowerCase().includes('kiro'))) {
|
|
77
|
+
return 'kiro';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// VS Code - NOT an AI platform, explicitly return null
|
|
81
|
+
// This prevents accidental detection as we don't want to create files for plain VS Code
|
|
82
|
+
if (process.env.TERM_PROGRAM === 'vscode' || process.env.VSCODE_GIT_IPC_HANDLE) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// No AI-powered IDE detected
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
50
90
|
/**
|
|
51
91
|
* Interactive platform selection using readline
|
|
52
92
|
* @param {string[]} previouslySelected - Previously selected platforms (from interrupted setup)
|
|
@@ -59,6 +99,21 @@ export async function selectPlatforms(previouslySelected = []) {
|
|
|
59
99
|
|
|
60
100
|
const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
|
|
61
101
|
|
|
102
|
+
// Auto-detect IDE and pre-select it
|
|
103
|
+
const detectedIDE = detectIDE();
|
|
104
|
+
let autoSelected = [];
|
|
105
|
+
|
|
106
|
+
if (detectedIDE) {
|
|
107
|
+
const platform = getPlatformById(detectedIDE);
|
|
108
|
+
if (platform) {
|
|
109
|
+
console.log(chalk.green(`\n✓ Detected ${platform.name} IDE - auto-selected`));
|
|
110
|
+
autoSelected = [detectedIDE];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Merge auto-detected with previously selected
|
|
115
|
+
const preselected = [...new Set([...autoSelected, ...(previouslySelected || [])])];
|
|
116
|
+
|
|
62
117
|
console.log('\n' + chalk.bold('Which AI platform(s) do you use?'));
|
|
63
118
|
console.log(chalk.gray('(Enter numbers separated by commas, or "all" for all platforms)\n'));
|
|
64
119
|
|
|
@@ -69,11 +124,18 @@ export async function selectPlatforms(previouslySelected = []) {
|
|
|
69
124
|
// IDE-based platforms
|
|
70
125
|
console.log(chalk.cyan.bold(` ${AI_PLATFORMS.ide.label}:`));
|
|
71
126
|
for (const platform of AI_PLATFORMS.ide.platforms) {
|
|
72
|
-
const
|
|
73
|
-
const
|
|
127
|
+
const isAutoDetected = autoSelected.includes(platform.id);
|
|
128
|
+
const wasPreviouslySelected = previouslySelected && previouslySelected.includes(platform.id);
|
|
129
|
+
const isPreselected = preselected.includes(platform.id);
|
|
130
|
+
let marker = '';
|
|
131
|
+
if (isAutoDetected) {
|
|
132
|
+
marker = chalk.green(' ✓ (auto-detected)');
|
|
133
|
+
} else if (wasPreviouslySelected) {
|
|
134
|
+
marker = chalk.green(' ✓ (previously selected)');
|
|
135
|
+
}
|
|
74
136
|
console.log(chalk.white(` ${index}. ${platform.name}`) + chalk.gray(` - ${platform.desc}`) + marker);
|
|
75
137
|
indexMap[index] = platform.id;
|
|
76
|
-
if (
|
|
138
|
+
if (isPreselected) preSelectedIndices.push(index);
|
|
77
139
|
index++;
|
|
78
140
|
}
|
|
79
141
|
|
|
@@ -82,27 +144,42 @@ export async function selectPlatforms(previouslySelected = []) {
|
|
|
82
144
|
// Web-based platforms
|
|
83
145
|
console.log(chalk.cyan.bold(` ${AI_PLATFORMS.web.label}:`));
|
|
84
146
|
for (const platform of AI_PLATFORMS.web.platforms) {
|
|
85
|
-
const
|
|
86
|
-
const
|
|
147
|
+
const wasPreviouslySelected = previouslySelected && previouslySelected.includes(platform.id);
|
|
148
|
+
const isPreselected = preselected.includes(platform.id);
|
|
149
|
+
const marker = wasPreviouslySelected ? chalk.green(' ✓ (previously selected)') : '';
|
|
87
150
|
console.log(chalk.white(` ${index}. ${platform.name}`) + chalk.gray(` - ${platform.desc}`) + marker);
|
|
88
151
|
indexMap[index] = platform.id;
|
|
89
|
-
if (
|
|
152
|
+
if (isPreselected) preSelectedIndices.push(index);
|
|
90
153
|
index++;
|
|
91
154
|
}
|
|
92
155
|
|
|
93
156
|
console.log('');
|
|
94
157
|
|
|
95
|
-
//
|
|
158
|
+
// CLI-based platforms
|
|
159
|
+
console.log(chalk.cyan.bold(` ${AI_PLATFORMS.cli.label}:`));
|
|
160
|
+
for (const platform of AI_PLATFORMS.cli.platforms) {
|
|
161
|
+
const wasPreviouslySelected = previouslySelected && previouslySelected.includes(platform.id);
|
|
162
|
+
const isPreselected = preselected.includes(platform.id);
|
|
163
|
+
const marker = wasPreviouslySelected ? chalk.green(' ✓ (previously selected)') : '';
|
|
164
|
+
console.log(chalk.white(` ${index}. ${platform.name}`) + chalk.gray(` - ${platform.desc}`) + marker);
|
|
165
|
+
indexMap[index] = platform.id;
|
|
166
|
+
if (isPreselected) preSelectedIndices.push(index);
|
|
167
|
+
index++;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log('');
|
|
171
|
+
|
|
172
|
+
// Show default based on preselected (auto-detected + previously selected)
|
|
96
173
|
const defaultHint = preSelectedIndices.length > 0
|
|
97
|
-
? `Enter for
|
|
174
|
+
? `Enter for selected: ${preSelectedIndices.join(',')}`
|
|
98
175
|
: 'e.g., 1,2,3 or "all"';
|
|
99
176
|
|
|
100
177
|
const answer = await question(chalk.yellow(`Your selection (${defaultHint}): `));
|
|
101
178
|
rl.close();
|
|
102
179
|
|
|
103
|
-
// If user just pressed enter and we have
|
|
180
|
+
// If user just pressed enter and we have preselected platforms, use them
|
|
104
181
|
if (answer.trim() === '' && preSelectedIndices.length > 0) {
|
|
105
|
-
return
|
|
182
|
+
return preselected;
|
|
106
183
|
}
|
|
107
184
|
|
|
108
185
|
if (answer.toLowerCase() === 'all') {
|
|
@@ -387,6 +464,21 @@ export function showAvailablePlatforms(currentIds) {
|
|
|
387
464
|
index++;
|
|
388
465
|
}
|
|
389
466
|
|
|
467
|
+
console.log('');
|
|
468
|
+
|
|
469
|
+
// CLI-based platforms
|
|
470
|
+
console.log(chalk.cyan.bold(` ${AI_PLATFORMS.cli.label}:`));
|
|
471
|
+
for (const platform of AI_PLATFORMS.cli.platforms) {
|
|
472
|
+
const status = currentIds.includes(platform.id)
|
|
473
|
+
? chalk.green(' ✓ (installed)')
|
|
474
|
+
: chalk.gray(' (not installed)');
|
|
475
|
+
console.log(chalk.white(` ${index}. ${platform.name}`) + status);
|
|
476
|
+
if (!currentIds.includes(platform.id)) {
|
|
477
|
+
available.push({ index, platform });
|
|
478
|
+
}
|
|
479
|
+
index++;
|
|
480
|
+
}
|
|
481
|
+
|
|
390
482
|
return available;
|
|
391
483
|
}
|
|
392
484
|
|
package/lib/commands/init.js
CHANGED
|
@@ -16,8 +16,131 @@ const PRESERVE_PATHS = [
|
|
|
16
16
|
'cache', // Cached analysis
|
|
17
17
|
'changelog', // Change history (user data)
|
|
18
18
|
'worklog', // Work context (user data)
|
|
19
|
+
'sessions', // Session data
|
|
20
|
+
'backups', // User backups
|
|
19
21
|
];
|
|
20
22
|
|
|
23
|
+
// Files within PRESERVE_PATHS that should always be UPDATED (framework files)
|
|
24
|
+
// These patterns match files that should be overwritten with latest framework version
|
|
25
|
+
const UPDATE_WITHIN_PRESERVED = [
|
|
26
|
+
'README.md', // Documentation
|
|
27
|
+
'entry-template.md', // Changelog entry template
|
|
28
|
+
'*.template.md', // All template markdown files
|
|
29
|
+
'*.template.json', // All template JSON files
|
|
30
|
+
'schemas', // Schema folders
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Backup user data before fresh install
|
|
35
|
+
* Returns object mapping path to temp backup location
|
|
36
|
+
*/
|
|
37
|
+
function backupUserData(proagentsDir) {
|
|
38
|
+
const preserved = {};
|
|
39
|
+
for (const path of PRESERVE_PATHS) {
|
|
40
|
+
const fullPath = join(proagentsDir, path);
|
|
41
|
+
if (existsSync(fullPath)) {
|
|
42
|
+
const tempPath = join(proagentsDir, '..', `.proagents-backup-${path.replace(/\//g, '-')}`);
|
|
43
|
+
try {
|
|
44
|
+
cpSync(fullPath, tempPath, { recursive: true });
|
|
45
|
+
preserved[path] = tempPath;
|
|
46
|
+
} catch (err) {
|
|
47
|
+
// Silently continue if backup fails
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return preserved;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Restore user data after fresh install
|
|
56
|
+
*/
|
|
57
|
+
function restoreUserData(proagentsDir, preserved) {
|
|
58
|
+
for (const [path, tempPath] of Object.entries(preserved)) {
|
|
59
|
+
const fullPath = join(proagentsDir, path);
|
|
60
|
+
if (existsSync(tempPath)) {
|
|
61
|
+
try {
|
|
62
|
+
// Remove the framework's empty/default version
|
|
63
|
+
if (existsSync(fullPath)) {
|
|
64
|
+
rmSync(fullPath, { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
// Restore user's data
|
|
67
|
+
cpSync(tempPath, fullPath, { recursive: true });
|
|
68
|
+
// Clean up temp backup
|
|
69
|
+
rmSync(tempPath, { recursive: true, force: true });
|
|
70
|
+
} catch (err) {
|
|
71
|
+
// Silently continue if restore fails
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Update framework files within preserved paths
|
|
79
|
+
* After restoring user data, this copies framework files like README.md, *.template.*, schemas/
|
|
80
|
+
*/
|
|
81
|
+
function updateFrameworkInPreserved(sourceDir, proagentsDir) {
|
|
82
|
+
for (const preservedPath of PRESERVE_PATHS) {
|
|
83
|
+
const sourcePath = join(sourceDir, preservedPath);
|
|
84
|
+
const targetPath = join(proagentsDir, preservedPath);
|
|
85
|
+
|
|
86
|
+
if (!existsSync(sourcePath) || !existsSync(targetPath)) continue;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
// Update README.md if exists
|
|
90
|
+
const sourceReadme = join(sourcePath, 'README.md');
|
|
91
|
+
const targetReadme = join(targetPath, 'README.md');
|
|
92
|
+
if (existsSync(sourceReadme)) {
|
|
93
|
+
cpSync(sourceReadme, targetReadme, { force: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Update entry-template.md (for changelog)
|
|
97
|
+
const sourceEntryTemplate = join(sourcePath, 'entry-template.md');
|
|
98
|
+
const targetEntryTemplate = join(targetPath, 'entry-template.md');
|
|
99
|
+
if (existsSync(sourceEntryTemplate)) {
|
|
100
|
+
cpSync(sourceEntryTemplate, targetEntryTemplate, { force: true });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Update schemas folder if exists
|
|
104
|
+
const sourceSchemas = join(sourcePath, 'schemas');
|
|
105
|
+
const targetSchemas = join(targetPath, 'schemas');
|
|
106
|
+
if (existsSync(sourceSchemas)) {
|
|
107
|
+
if (existsSync(targetSchemas)) {
|
|
108
|
+
rmSync(targetSchemas, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
cpSync(sourceSchemas, targetSchemas, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Update all *.template.md and *.template.json files
|
|
114
|
+
if (existsSync(sourcePath)) {
|
|
115
|
+
const entries = readdirSync(sourcePath, { withFileTypes: true });
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
if (entry.isFile() && (entry.name.endsWith('.template.md') || entry.name.endsWith('.template.json'))) {
|
|
118
|
+
cpSync(join(sourcePath, entry.name), join(targetPath, entry.name), { force: true });
|
|
119
|
+
}
|
|
120
|
+
// Also check subdirectories (like .learning/global/)
|
|
121
|
+
if (entry.isDirectory()) {
|
|
122
|
+
const subPath = join(sourcePath, entry.name);
|
|
123
|
+
const subTargetPath = join(targetPath, entry.name);
|
|
124
|
+
if (existsSync(subPath)) {
|
|
125
|
+
const subEntries = readdirSync(subPath, { withFileTypes: true });
|
|
126
|
+
for (const subEntry of subEntries) {
|
|
127
|
+
if (subEntry.isFile() && (subEntry.name.endsWith('.template.md') || subEntry.name.endsWith('.template.json'))) {
|
|
128
|
+
if (!existsSync(subTargetPath)) {
|
|
129
|
+
mkdirSync(subTargetPath, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
cpSync(join(subPath, subEntry.name), join(subTargetPath, subEntry.name), { force: true });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
// Silently continue if update fails
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
21
144
|
// Config file is handled specially - merged not preserved
|
|
22
145
|
const CONFIG_FILE = 'proagents.config.yaml';
|
|
23
146
|
|
|
@@ -90,6 +213,7 @@ const FRAMEWORK_FILES = [
|
|
|
90
213
|
'GETTING-STARTED-STORY.md',
|
|
91
214
|
'slash-commands.json',
|
|
92
215
|
'AI_INSTRUCTIONS.md', // Universal instructions kept for reference
|
|
216
|
+
'AGENTS.md', // Universal AI instruction file (works with most AI platforms)
|
|
93
217
|
];
|
|
94
218
|
|
|
95
219
|
// Project type definitions for detection
|
|
@@ -611,7 +735,7 @@ function checkIncompleteSetup(targetDir) {
|
|
|
611
735
|
}
|
|
612
736
|
|
|
613
737
|
// Check 2: No AI instruction files at all = incomplete
|
|
614
|
-
const aiFiles = ['CLAUDE.md', '.cursorrules', '.windsurfrules', 'CHATGPT.md', 'GEMINI.md'];
|
|
738
|
+
const aiFiles = ['AGENTS.md', 'CLAUDE.md', '.cursorrules', '.windsurfrules', 'CHATGPT.md', 'GEMINI.md'];
|
|
615
739
|
const hasAnyAiFile = aiFiles.some(f => existsSync(join(targetDir, f)));
|
|
616
740
|
if (!hasAnyAiFile) {
|
|
617
741
|
return true;
|
|
@@ -667,6 +791,27 @@ async function promptIncompleteSetupChoice() {
|
|
|
667
791
|
return 'continue';
|
|
668
792
|
}
|
|
669
793
|
|
|
794
|
+
/**
|
|
795
|
+
* Confirm destructive "Restart fresh" action
|
|
796
|
+
*/
|
|
797
|
+
async function confirmRestartFresh() {
|
|
798
|
+
const rl = createInterface({
|
|
799
|
+
input: process.stdin,
|
|
800
|
+
output: process.stdout
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
const question = (prompt) => new Promise(resolve => rl.question(prompt, resolve));
|
|
804
|
+
|
|
805
|
+
console.log(chalk.red.bold('\n⚠️ WARNING: This will DELETE all data in .proagents/'));
|
|
806
|
+
console.log(chalk.yellow(' Including: changelog, worklog, sessions, learning data, etc.'));
|
|
807
|
+
console.log(chalk.gray(' This action cannot be undone.\n'));
|
|
808
|
+
|
|
809
|
+
const answer = await question(chalk.red('Type "DELETE" to confirm, or press Enter to cancel: '));
|
|
810
|
+
rl.close();
|
|
811
|
+
|
|
812
|
+
return answer.trim().toUpperCase() === 'DELETE';
|
|
813
|
+
}
|
|
814
|
+
|
|
670
815
|
/**
|
|
671
816
|
* Prompt user for .gitignore preference
|
|
672
817
|
*/
|
|
@@ -981,6 +1126,12 @@ export async function initCommand(options = {}) {
|
|
|
981
1126
|
console.log(chalk.cyan('\nContinuing setup...\n'));
|
|
982
1127
|
// Fall through to fresh install (but keep .proagents folder)
|
|
983
1128
|
} else if (choice === 'restart') {
|
|
1129
|
+
// Confirm destructive action
|
|
1130
|
+
const confirmed = await confirmRestartFresh();
|
|
1131
|
+
if (!confirmed) {
|
|
1132
|
+
console.log(chalk.yellow('\nCancelled. No changes made.\n'));
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
984
1135
|
console.log(chalk.cyan('\nRestarting fresh setup...\n'));
|
|
985
1136
|
rmSync(proagentsDir, { recursive: true, force: true });
|
|
986
1137
|
// Fall through to fresh install
|
|
@@ -1026,7 +1177,21 @@ export async function initCommand(options = {}) {
|
|
|
1026
1177
|
|
|
1027
1178
|
// Fresh install or force overwrite
|
|
1028
1179
|
console.log(chalk.gray('Copying framework files...'));
|
|
1180
|
+
|
|
1181
|
+
// Backup user data if .proagents exists (to preserve changelog, worklog, etc.)
|
|
1182
|
+
const userDataBackup = existsSync(proagentsDir) ? backupUserData(proagentsDir) : {};
|
|
1183
|
+
|
|
1029
1184
|
cpSync(sourceDir, proagentsDir, { recursive: true, force: true });
|
|
1185
|
+
|
|
1186
|
+
// Restore user data after framework copy
|
|
1187
|
+
if (Object.keys(userDataBackup).length > 0) {
|
|
1188
|
+
restoreUserData(proagentsDir, userDataBackup);
|
|
1189
|
+
// Update framework files within preserved paths (README.md, templates, schemas)
|
|
1190
|
+
updateFrameworkInPreserved(sourceDir, proagentsDir);
|
|
1191
|
+
console.log(chalk.green('✓ Preserved user data (changelog, worklog, sessions, etc.)'));
|
|
1192
|
+
console.log(chalk.green('✓ Updated templates and documentation in preserved folders'));
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1030
1195
|
console.log(chalk.green('✓ Framework files copied to ./.proagents/'));
|
|
1031
1196
|
|
|
1032
1197
|
// Create config if not skipped
|
|
@@ -1398,6 +1563,9 @@ async function smartUpdate(sourceDir, targetDir) {
|
|
|
1398
1563
|
}
|
|
1399
1564
|
}
|
|
1400
1565
|
|
|
1566
|
+
// Update framework files within preserved paths (templates, README, schemas)
|
|
1567
|
+
updateFrameworkInPreserved(sourceDir, targetDir);
|
|
1568
|
+
|
|
1401
1569
|
console.log(chalk.gray('\nTip: Use "proagents ai add" to add more AI platforms'));
|
|
1402
1570
|
}
|
|
1403
1571
|
|
package/lib/commands/version.js
CHANGED
|
@@ -9,7 +9,7 @@ const __dirname = dirname(__filename);
|
|
|
9
9
|
/**
|
|
10
10
|
* Get CLI version from package.json
|
|
11
11
|
*/
|
|
12
|
-
function getCliVersion() {
|
|
12
|
+
export function getCliVersion() {
|
|
13
13
|
try {
|
|
14
14
|
const packagePath = join(__dirname, '..', '..', 'package.json');
|
|
15
15
|
const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
@@ -41,7 +41,7 @@ function getProjectVersion(targetDir) {
|
|
|
41
41
|
/**
|
|
42
42
|
* Fetch latest version from npm
|
|
43
43
|
*/
|
|
44
|
-
async function getLatestVersion() {
|
|
44
|
+
export async function getLatestVersion() {
|
|
45
45
|
try {
|
|
46
46
|
const controller = new AbortController();
|
|
47
47
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
@@ -66,7 +66,7 @@ async function getLatestVersion() {
|
|
|
66
66
|
* Compare semantic versions
|
|
67
67
|
* Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
68
68
|
*/
|
|
69
|
-
function compareVersions(v1, v2) {
|
|
69
|
+
export function compareVersions(v1, v2) {
|
|
70
70
|
const parts1 = v1.split('.').map(Number);
|
|
71
71
|
const parts2 = v2.split('.').map(Number);
|
|
72
72
|
|
|
@@ -79,6 +79,27 @@ function compareVersions(v1, v2) {
|
|
|
79
79
|
return 0;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Check for updates and display notification if available
|
|
84
|
+
* Non-blocking - runs asynchronously
|
|
85
|
+
*/
|
|
86
|
+
export async function checkForUpdates() {
|
|
87
|
+
try {
|
|
88
|
+
const cliVersion = getCliVersion();
|
|
89
|
+
const latestVersion = await getLatestVersion();
|
|
90
|
+
|
|
91
|
+
if (latestVersion && compareVersions(cliVersion, latestVersion) < 0) {
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log(chalk.yellow(` ╭───────────────────────────────────────────────╮`));
|
|
94
|
+
console.log(chalk.yellow(` │ Update available: ${chalk.gray('v' + cliVersion)} → ${chalk.green('v' + latestVersion)} │`));
|
|
95
|
+
console.log(chalk.yellow(` │ Run: ${chalk.cyan('npx proagents init')} to update │`));
|
|
96
|
+
console.log(chalk.yellow(` ╰───────────────────────────────────────────────╯`));
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
// Silently ignore update check errors
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
82
103
|
/**
|
|
83
104
|
* Version command - show detailed version information
|
|
84
105
|
*/
|
|
@@ -118,7 +139,7 @@ export async function versionCommand(options = {}) {
|
|
|
118
139
|
console.log(chalk.green('v' + latestVersion + ' (up to date)'));
|
|
119
140
|
} else {
|
|
120
141
|
console.log(chalk.yellow('v' + latestVersion + ' (update available)'));
|
|
121
|
-
console.log(chalk.yellow(` ↳ Run '
|
|
142
|
+
console.log(chalk.yellow(` ↳ Run 'npx proagents init' to update`));
|
|
122
143
|
}
|
|
123
144
|
} else {
|
|
124
145
|
console.log(chalk.gray('Could not fetch (offline or npm unavailable)'));
|