ai-devx 1.0.4 → 1.0.6

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,358 @@
1
+ const path = require("path");
2
+ const fs = require("fs-extra");
3
+ const chalk = require("chalk");
4
+ const readline = require("readline");
5
+ const config = require("../config");
6
+ const fileUtils = require("../utils/fileSystem");
7
+
8
+ class InteractiveCLI {
9
+ constructor(agentPath) {
10
+ this.agentPath = agentPath;
11
+ this.workflows = [];
12
+ this.skills = [];
13
+ this.agents = [];
14
+ this.scripts = [];
15
+ this.rules = [];
16
+ this.shared = [];
17
+ this.workflowsPath = path.join(agentPath, "workflows");
18
+ this.skillsPath = path.join(agentPath, "skills");
19
+ this.agentsPath = path.join(agentPath, "agents");
20
+ this.scriptsPath = path.join(agentPath, "scripts");
21
+ this.rulesPath = path.join(agentPath, "rules");
22
+ this.sharedPath = path.join(agentPath, ".shared");
23
+ }
24
+
25
+ async loadResources() {
26
+ // Load workflows
27
+ if (await fs.pathExists(this.workflowsPath)) {
28
+ const files = await fs.readdir(this.workflowsPath);
29
+ this.workflows = files
30
+ .filter((f) => f.endsWith(".md"))
31
+ .map((f) => ({
32
+ name: `/${path.basename(f, ".md")}`,
33
+ file: f,
34
+ type: "workflow",
35
+ }));
36
+ }
37
+
38
+ // Load skills
39
+ if (await fs.pathExists(this.skillsPath)) {
40
+ const entries = await fs.readdir(this.skillsPath, {
41
+ withFileTypes: true,
42
+ });
43
+ this.skills = entries
44
+ .filter((e) => e.isDirectory())
45
+ .map((e) => ({
46
+ name: e.name,
47
+ path: path.join(this.skillsPath, e.name),
48
+ type: "skill",
49
+ }));
50
+ }
51
+
52
+ // Load agents
53
+ if (await fs.pathExists(this.agentsPath)) {
54
+ const files = await fs.readdir(this.agentsPath);
55
+ this.agents = files
56
+ .filter((f) => f.endsWith(".md"))
57
+ .map((f) => ({
58
+ name: `@${path.basename(f, ".md")}`,
59
+ file: f,
60
+ type: "agent",
61
+ }));
62
+ }
63
+
64
+ // Load scripts
65
+ if (await fs.pathExists(this.scriptsPath)) {
66
+ const files = await fs.readdir(this.scriptsPath);
67
+ this.scripts = files
68
+ .filter((f) => f.endsWith(".py"))
69
+ .map((f) => ({
70
+ name: f,
71
+ file: f,
72
+ type: "script",
73
+ }));
74
+ }
75
+
76
+ // Load rules
77
+ if (await fs.pathExists(this.rulesPath)) {
78
+ const files = await fs.readdir(this.rulesPath);
79
+ this.rules = files
80
+ .filter((f) => f.endsWith(".md"))
81
+ .map((f) => ({
82
+ name: path.basename(f, ".md"),
83
+ file: f,
84
+ type: "rule",
85
+ }));
86
+ }
87
+
88
+ // Load shared
89
+ if (await fs.pathExists(this.sharedPath)) {
90
+ const entries = await fs.readdir(this.sharedPath, {
91
+ withFileTypes: true,
92
+ });
93
+ this.shared = entries
94
+ .filter((e) => e.isDirectory())
95
+ .map((e) => ({
96
+ name: e.name,
97
+ path: path.join(this.sharedPath, e.name),
98
+ type: "shared",
99
+ }));
100
+ }
101
+ }
102
+
103
+ showHelp() {
104
+ console.log(chalk.bold("\nšŸ“š Available Commands:\n"));
105
+
106
+ console.log(chalk.yellow.bold("Slash Commands (Workflows):"));
107
+ this.workflows.forEach((w) => {
108
+ console.log(` ${chalk.cyan(w.name)} - Workflow command`);
109
+ });
110
+
111
+ console.log(chalk.yellow.bold("\nSkills:"));
112
+ this.skills.slice(0, 15).forEach((s) => {
113
+ console.log(` ${chalk.green(s.name)} - Domain knowledge module`);
114
+ });
115
+ if (this.skills.length > 15) {
116
+ console.log(` ${chalk.gray(`... and ${this.skills.length - 15} more`)}`);
117
+ }
118
+
119
+ console.log(chalk.yellow.bold("\nAgents:"));
120
+ this.agents.slice(0, 10).forEach((a) => {
121
+ console.log(` ${chalk.magenta(a.name)} - Specialist agent`);
122
+ });
123
+ if (this.agents.length > 10) {
124
+ console.log(` ${chalk.gray(`... and ${this.agents.length - 10} more`)}`);
125
+ }
126
+
127
+ console.log(chalk.yellow.bold("\nScripts:"));
128
+ this.scripts.slice(0, 8).forEach((s) => {
129
+ console.log(` ${chalk.blue(s.name)} - Automation script`);
130
+ });
131
+ if (this.scripts.length > 8) {
132
+ console.log(` ${chalk.gray(`... and ${this.scripts.length - 8} more`)}`);
133
+ }
134
+
135
+ console.log(chalk.yellow.bold("\nRules:"));
136
+ this.rules.forEach((r) => {
137
+ console.log(` ${chalk.red(r.name)} - Assistant rules`);
138
+ });
139
+
140
+ if (this.shared.length > 0) {
141
+ console.log(chalk.yellow.bold("\nShared Resources:"));
142
+ this.shared.forEach((s) => {
143
+ console.log(` ${chalk.gray(s.name)} - Shared utilities`);
144
+ });
145
+ }
146
+
147
+ console.log(chalk.yellow.bold("\nOther Commands:"));
148
+ console.log(` ${chalk.cyan("/help")} - Show this menu`);
149
+ console.log(` ${chalk.cyan("/exit")} - Exit interactive mode`);
150
+ console.log(` ${chalk.cyan("clear")} - Clear screen`);
151
+ console.log();
152
+ }
153
+
154
+ async executeWorkflow(workflowName, args = "") {
155
+ const workflow = this.workflows.find((w) => w.name === workflowName);
156
+ if (!workflow) {
157
+ console.log(chalk.red(`āŒ Workflow '${workflowName}' not found`));
158
+ return;
159
+ }
160
+
161
+ const workflowPath = path.join(this.workflowsPath, workflow.file);
162
+ try {
163
+ const content = await fs.readFile(workflowPath, "utf-8");
164
+ console.log(chalk.bold(`\nšŸ“„ Workflow: ${workflowName}\n`));
165
+
166
+ // Show first 50 lines or until first separator
167
+ const lines = content.split("\n");
168
+ const previewLines = [];
169
+ for (const line of lines) {
170
+ if (line.startsWith("---") && previewLines.length > 0) break;
171
+ previewLines.push(line);
172
+ if (previewLines.length >= 30) break;
173
+ }
174
+
175
+ console.log(previewLines.join("\n"));
176
+ console.log(chalk.gray("\n... (truncated)"));
177
+ console.log(chalk.yellow(`\nFull file: ${workflowPath}\n`));
178
+ } catch (error) {
179
+ console.log(chalk.red(`āŒ Error reading workflow: ${error.message}`));
180
+ }
181
+ }
182
+
183
+ async executeSkill(skillName) {
184
+ const skill = this.skills.find((s) => s.name === skillName);
185
+ if (!skill) {
186
+ console.log(chalk.red(`āŒ Skill '${skillName}' not found`));
187
+ return;
188
+ }
189
+
190
+ const skillMdPath = path.join(skill.path, "SKILL.md");
191
+ try {
192
+ if (await fs.pathExists(skillMdPath)) {
193
+ const content = await fs.readFile(skillMdPath, "utf-8");
194
+ console.log(chalk.bold(`\nšŸ“š Skill: ${skillName}\n`));
195
+
196
+ const lines = content.split("\n").slice(0, 40);
197
+ console.log(lines.join("\n"));
198
+ console.log(chalk.gray("\n... (truncated)"));
199
+ console.log(chalk.yellow(`\nFull file: ${skillMdPath}\n`));
200
+ } else {
201
+ console.log(chalk.bold(`\nšŸ“š Skill: ${skillName}\n`));
202
+ console.log(`Path: ${skill.path}`);
203
+
204
+ const files = await fs.readdir(skill.path);
205
+ console.log("\nContents:");
206
+ files.forEach((f) => console.log(` - ${f}`));
207
+ console.log();
208
+ }
209
+ } catch (error) {
210
+ console.log(chalk.red(`āŒ Error reading skill: ${error.message}`));
211
+ }
212
+ }
213
+
214
+ async executeAgent(agentName) {
215
+ const agent = this.agents.find((a) => a.name === agentName);
216
+ if (!agent) {
217
+ console.log(chalk.red(`āŒ Agent '${agentName}' not found`));
218
+ return;
219
+ }
220
+
221
+ const agentPath = path.join(this.agentsPath, agent.file);
222
+ try {
223
+ const content = await fs.readFile(agentPath, "utf-8");
224
+ console.log(chalk.bold(`\nšŸ¤– Agent: ${agentName}\n`));
225
+
226
+ const lines = content.split("\n").slice(0, 40);
227
+ console.log(lines.join("\n"));
228
+ console.log(chalk.gray("\n... (truncated)"));
229
+ console.log(chalk.yellow(`\nFull file: ${agentPath}\n`));
230
+ } catch (error) {
231
+ console.log(chalk.red(`āŒ Error reading agent: ${error.message}`));
232
+ }
233
+ }
234
+
235
+ async executeRule(ruleName) {
236
+ const rule = this.rules.find((r) => r.name === ruleName);
237
+ if (!rule) {
238
+ console.log(chalk.red(`āŒ Rule '${ruleName}' not found`));
239
+ return;
240
+ }
241
+
242
+ const rulePath = path.join(this.rulesPath, rule.file);
243
+ try {
244
+ const content = await fs.readFile(rulePath, "utf-8");
245
+ console.log(chalk.bold(`\nšŸ“œ Rule: ${ruleName}\n`));
246
+
247
+ const lines = content.split("\n").slice(0, 40);
248
+ console.log(lines.join("\n"));
249
+ console.log(chalk.gray("\n... (truncated)"));
250
+ console.log(chalk.yellow(`\nFull file: ${rulePath}\n`));
251
+ } catch (error) {
252
+ console.log(chalk.red(`āŒ Error reading rule: ${error.message}`));
253
+ }
254
+ }
255
+
256
+ async start() {
257
+ await this.loadResources();
258
+
259
+ console.log(
260
+ chalk.cyan.bold(`
261
+ ╔════════════════════════════════════════════════╗
262
+ ā•‘ šŸ¤– AI-DEVX Interactive Shell ā•‘
263
+ ā•‘ Type / to see all commands and resources ā•‘
264
+ ā•‘ Type /help for help or /exit to quit ā•‘
265
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
266
+ `),
267
+ );
268
+
269
+ const rl = readline.createInterface({
270
+ input: process.stdin,
271
+ output: process.stdout,
272
+ prompt: chalk.cyan("ai-devx> "),
273
+ });
274
+
275
+ rl.prompt();
276
+
277
+ rl.on("line", async (line) => {
278
+ const input = line.trim();
279
+
280
+ if (input === "/exit" || input === "exit" || input === "quit") {
281
+ console.log(chalk.yellow("šŸ‘‹ Goodbye!"));
282
+ rl.close();
283
+ return;
284
+ }
285
+
286
+ if (input === "" || input === "/") {
287
+ this.showHelp();
288
+ } else if (input === "/help" || input === "help") {
289
+ console.log(chalk.bold("\nšŸŽÆ Quick Guide:\n"));
290
+ console.log("Type any of the following:");
291
+ console.log(
292
+ ` ${chalk.cyan("/")} - Show all available commands and resources`,
293
+ );
294
+ console.log(
295
+ ` ${chalk.cyan("/<workflow>")} - View workflow details (e.g., /plan)`,
296
+ );
297
+ console.log(
298
+ ` ${chalk.cyan("@<agent>")} - View agent details (e.g., @orchestrator)`,
299
+ );
300
+ console.log(
301
+ ` ${chalk.cyan("<skill-name>")} - View skill details (e.g., clean-code)`,
302
+ );
303
+ console.log(
304
+ ` ${chalk.cyan("<rule-name>")} - View rule details (e.g., CLAUDE)`,
305
+ );
306
+ console.log(` ${chalk.cyan("clear")} - Clear the screen`);
307
+ console.log(` ${chalk.cyan("/exit")} - Exit interactive mode\n`);
308
+ } else if (input === "clear") {
309
+ console.clear();
310
+ } else if (input.startsWith("/")) {
311
+ // Workflow command
312
+ await this.executeWorkflow(input);
313
+ } else if (input.startsWith("@")) {
314
+ // Agent command
315
+ await this.executeAgent(input);
316
+ } else if (this.skills.find((s) => s.name === input)) {
317
+ // Skill command
318
+ await this.executeSkill(input);
319
+ } else if (this.rules.find((r) => r.name === input)) {
320
+ // Rule command
321
+ await this.executeRule(input);
322
+ } else {
323
+ console.log(chalk.yellow(`āš ļø Unknown command: ${input}`));
324
+ console.log(chalk.gray("Type / to see available commands\n"));
325
+ }
326
+
327
+ rl.prompt();
328
+ });
329
+
330
+ rl.on("close", () => {
331
+ console.log(chalk.yellow("\nšŸ‘‹ Goodbye!"));
332
+ process.exit(0);
333
+ });
334
+ }
335
+ }
336
+
337
+ async function shellCommand(targetPath) {
338
+ const cwd = targetPath || process.cwd();
339
+
340
+ // Find installed folder
341
+ const installedFolder = await fileUtils.findInstalledFolder(cwd);
342
+
343
+ if (!installedFolder) {
344
+ console.log(chalk.red(`āŒ AI-DEVX not installed in ${cwd}`));
345
+ console.log(chalk.yellow("Run one of the following:"));
346
+ console.log(chalk.yellow(" npx ai-devx init"));
347
+ console.log(chalk.yellow(" npx ai-devx init --assistant claude"));
348
+ console.log(chalk.yellow(" npx ai-devx init --assistant gemini"));
349
+ process.exit(1);
350
+ }
351
+
352
+ const agentPath = path.join(path.resolve(cwd), installedFolder);
353
+
354
+ const cli = new InteractiveCLI(agentPath);
355
+ await cli.start();
356
+ }
357
+
358
+ module.exports = shellCommand;
@@ -1,59 +1,96 @@
1
- const path = require('path');
2
- const chalk = require('chalk');
3
- const logger = require('../utils/logger');
4
- const fileUtils = require('../utils/fileSystem');
5
- const config = require('../config');
1
+ const path = require("path");
2
+ const chalk = require("chalk");
3
+ const logger = require("../utils/logger");
4
+ const fileUtils = require("../utils/fileSystem");
5
+ const config = require("../config");
6
6
 
7
7
  async function statusCommand(options) {
8
8
  const { path: targetPath } = options;
9
9
  const absolutePath = path.resolve(targetPath);
10
- const agentPath = path.join(absolutePath, config.AGENT_FOLDER);
11
-
10
+
12
11
  logger.blank();
13
- logger.info('AI-DEVX Status');
12
+ logger.info("AI-DEVX Status");
14
13
  logger.blank();
15
-
16
- const isInstalled = await fileUtils.isInstalled(absolutePath);
17
-
18
- if (!isInstalled) {
19
- logger.error('Not installed');
14
+
15
+ // Check all possible installations
16
+ const folderNames = config.getAllFolderNames();
17
+ const installations = [];
18
+
19
+ for (const folder of folderNames) {
20
+ const isInstalled = await fileUtils.isInstalled(absolutePath, folder);
21
+ if (isInstalled) {
22
+ const folderPath = path.join(absolutePath, folder);
23
+ const version = await fileUtils.getVersion(folderPath);
24
+ const configData = await fileUtils.readJson(
25
+ path.join(folderPath, "config.json"),
26
+ );
27
+
28
+ installations.push({
29
+ folder,
30
+ path: folderPath,
31
+ version: version || "unknown",
32
+ config: configData,
33
+ assistants: configData?.assistants || [],
34
+ });
35
+ }
36
+ }
37
+
38
+ if (installations.length === 0) {
39
+ logger.error("Not installed");
20
40
  logger.info(`Directory: ${absolutePath}`);
21
41
  logger.blank();
22
- logger.info('Run "ai-devx init" to install');
42
+ logger.info("Run one of the following:");
43
+ logger.step("npx ai-devx init # Install to .agent/");
44
+ logger.step("npx ai-devx init --assistant claude # Install to .claude/");
45
+ logger.step("npx ai-devx init --assistant gemini # Install to .gemini/");
46
+ logger.blank();
23
47
  return;
24
48
  }
25
-
26
- // Get version
27
- const version = await fileUtils.getVersion(agentPath);
28
- const configData = await fileUtils.readJson(path.join(agentPath, 'config.json'));
29
-
30
- logger.success('Installed');
49
+
50
+ logger.success(
51
+ `Installed (${installations.length} installation${installations.length > 1 ? "s" : ""})`,
52
+ );
31
53
  logger.blank();
32
- logger.info(`Directory: ${agentPath}`);
33
- logger.info(`Version: ${chalk.green(version || 'unknown')}`);
34
-
35
- if (configData) {
36
- if (configData.installedAt) {
37
- logger.info(`Installed: ${new Date(configData.installedAt).toLocaleDateString()}`);
54
+
55
+ installations.forEach((inst, index) => {
56
+ if (index > 0) logger.blank();
57
+
58
+ logger.info(`Installation ${index + 1}:`);
59
+ logger.step(`Folder: ${chalk.cyan(inst.folder)}`);
60
+ logger.step(`Path: ${inst.path}`);
61
+ logger.step(`Version: ${chalk.green(inst.version)}`);
62
+
63
+ if (inst.assistants.length > 0) {
64
+ logger.step(`Assistants: ${inst.assistants.join(", ")}`);
38
65
  }
39
- if (configData.updatedAt) {
40
- logger.info(`Last updated: ${new Date(configData.updatedAt).toLocaleDateString()}`);
66
+
67
+ if (inst.config) {
68
+ if (inst.config.installedAt) {
69
+ logger.step(
70
+ `Installed: ${new Date(inst.config.installedAt).toLocaleDateString()}`,
71
+ );
72
+ }
73
+ if (inst.config.updatedAt) {
74
+ logger.step(
75
+ `Last updated: ${new Date(inst.config.updatedAt).toLocaleDateString()}`,
76
+ );
77
+ }
41
78
  }
42
- }
43
-
79
+ });
80
+
44
81
  logger.blank();
45
- logger.info('Available Components:');
46
- logger.step(`Agents: 9 specialist personas`);
47
- logger.step(`Skills: Multiple domain knowledge modules`);
48
- logger.step(`Workflows: 9 slash commands`);
49
- logger.step(`Scripts: Validation and automation tools`);
50
-
82
+ logger.info("Available Components:");
83
+ logger.step(`Agents: 22 specialist personas`);
84
+ logger.step(`Skills: 37 domain knowledge modules`);
85
+ logger.step(`Workflows: 11 slash commands`);
86
+ logger.step(`Scripts: 4 validation and automation tools`);
87
+
51
88
  logger.blank();
52
- logger.info('Quick Commands:');
53
- config.WORKFLOWS.slice(0, 5).forEach(wf => {
89
+ logger.info("Quick Commands:");
90
+ config.WORKFLOWS.slice(0, 5).forEach((wf) => {
54
91
  logger.step(`${chalk.cyan(wf.command)} - ${wf.description}`);
55
92
  });
56
-
93
+
57
94
  logger.blank();
58
95
  }
59
96
 
@@ -1,74 +1,102 @@
1
- const path = require('path');
2
- const fs = require('fs-extra');
3
- const ora = require('ora');
4
- const logger = require('../utils/logger');
5
- const fileUtils = require('../utils/fileSystem');
6
- const config = require('../config');
1
+ const path = require("path");
2
+ const fs = require("fs-extra");
3
+ const ora = require("ora");
4
+ const logger = require("../utils/logger");
5
+ const fileUtils = require("../utils/fileSystem");
6
+ const config = require("../config");
7
7
 
8
8
  async function updateCommand(options) {
9
9
  const { force, backup, quiet } = options;
10
-
11
- const spinner = quiet ? null : ora('Checking for updates...').start();
12
-
10
+
11
+ const spinner = quiet ? null : ora("Checking for updates...").start();
12
+
13
13
  try {
14
14
  const cwd = process.cwd();
15
- const isInstalled = await fileUtils.isInstalled(cwd);
16
-
17
- if (!isInstalled) {
18
- if (spinner) spinner.stop();
19
- logger.error('AI-DEVX is not installed in this directory.');
20
- logger.info('Run "ai-devx init" first.');
21
- return;
15
+
16
+ // Find all installed folders
17
+ const folderNames = config.getAllFolderNames();
18
+ const installations = [];
19
+
20
+ for (const folder of folderNames) {
21
+ const isInstalled = await fileUtils.isInstalled(cwd, folder);
22
+ if (isInstalled) {
23
+ installations.push(folder);
24
+ }
22
25
  }
23
-
24
- const agentPath = path.join(cwd, config.AGENT_FOLDER);
25
- const currentVersion = await fileUtils.getVersion(agentPath);
26
- const latestVersion = '1.0.0'; // In real implementation, fetch from GitHub
27
-
28
- if (currentVersion === latestVersion && !force) {
26
+
27
+ if (installations.length === 0) {
29
28
  if (spinner) spinner.stop();
30
- logger.success('AI-DEVX is already up to date!');
31
- logger.info(`Version: ${currentVersion}`);
29
+ logger.error("AI-DEVX is not installed in this directory.");
30
+ logger.info('Run "ai-devx init" first.');
32
31
  return;
33
32
  }
34
-
35
- if (spinner) spinner.text = 'Updating templates...';
36
-
37
- // Create backup if requested
38
- if (backup) {
39
- const backupPath = await fileUtils.backup(cwd);
40
- if (backupPath && !quiet) {
41
- logger.info(`Backup created: ${backupPath}`);
33
+
34
+ const latestVersion = "1.0.0"; // In real implementation, fetch from GitHub
35
+ const sourcePath = path.join(__dirname, "../../templates/.agent");
36
+
37
+ // Update each installation
38
+ for (const folder of installations) {
39
+ const agentPath = path.join(cwd, folder);
40
+ const currentVersion = await fileUtils.getVersion(agentPath);
41
+
42
+ if (spinner) spinner.text = `Updating ${folder}...`;
43
+
44
+ // Create backup if requested
45
+ if (backup) {
46
+ const backupPath = path.join(cwd, `${folder}.backup.${Date.now()}`);
47
+ await fs.copy(agentPath, backupPath);
48
+ if (!quiet) {
49
+ logger.info(`Backup created: ${backupPath}`);
50
+ }
51
+ }
52
+
53
+ // Read existing config to preserve assistant settings
54
+ const configFile = path.join(agentPath, "config.json");
55
+ const existingConfig = (await fileUtils.readJson(configFile)) || {};
56
+ const assistants = existingConfig.assistants || ["claude", "gemini"];
57
+
58
+ // Copy updated templates
59
+ await fileUtils.copyDir(sourcePath, agentPath, { force: true });
60
+
61
+ // Re-apply assistant filtering
62
+ const rulesPath = path.join(agentPath, "rules");
63
+ const validAssistants = ["claude", "gemini"];
64
+
65
+ for (const ast of validAssistants) {
66
+ if (!assistants.includes(ast)) {
67
+ const ruleFile = path.join(rulesPath, `${ast.toUpperCase()}.md`);
68
+ try {
69
+ await fs.remove(ruleFile);
70
+ } catch (e) {
71
+ // File might not exist, ignore
72
+ }
73
+ }
42
74
  }
75
+
76
+ // Update version file
77
+ const versionFile = path.join(agentPath, "VERSION");
78
+ await fs.writeFile(versionFile, latestVersion);
79
+
80
+ // Update config
81
+ existingConfig.version = latestVersion;
82
+ existingConfig.updatedAt = new Date().toISOString();
83
+ existingConfig.assistants = assistants;
84
+ await fileUtils.writeJson(configFile, existingConfig);
43
85
  }
44
-
45
- // Copy updated templates
46
- const sourcePath = path.join(__dirname, '../../templates/.agent');
47
- await fileUtils.copyDir(sourcePath, agentPath, { force: true });
48
-
49
- // Update version file
50
- const versionFile = path.join(agentPath, 'VERSION');
51
- await fs.writeFile(versionFile, latestVersion);
52
-
53
- // Update config
54
- const configFile = path.join(agentPath, 'config.json');
55
- const existingConfig = await fileUtils.readJson(configFile) || {};
56
- existingConfig.version = latestVersion;
57
- existingConfig.updatedAt = new Date().toISOString();
58
- await fileUtils.writeJson(configFile, existingConfig);
59
-
60
- if (spinner) spinner.succeed('AI-DEVX updated successfully!');
61
-
86
+
87
+ if (spinner) spinner.succeed("AI-DEVX updated successfully!");
88
+
62
89
  if (!quiet) {
63
90
  logger.blank();
64
- logger.success(`Updated to version: ${latestVersion}`);
65
- if (currentVersion) {
66
- logger.info(`Previous version: ${currentVersion}`);
67
- }
91
+ logger.success(
92
+ `Updated ${installations.length} installation${installations.length > 1 ? "s" : ""} to version: ${latestVersion}`,
93
+ );
94
+ installations.forEach((folder) => {
95
+ logger.info(` - ${folder}/`);
96
+ });
68
97
  }
69
-
70
98
  } catch (error) {
71
- if (spinner) spinner.fail('Update failed');
99
+ if (spinner) spinner.fail("Update failed");
72
100
  logger.error(error.message);
73
101
  process.exit(1);
74
102
  }