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.
- package/README.md +110 -52
- package/bin/cli.js +27 -8
- package/package.json +1 -1
- package/src/commands/init.js +43 -18
- package/src/commands/shell.js +358 -0
- package/src/commands/status.js +76 -39
- package/src/commands/update.js +84 -56
- package/src/config.js +116 -49
- package/src/utils/fileSystem.js +23 -12
|
@@ -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;
|
package/src/commands/status.js
CHANGED
|
@@ -1,59 +1,96 @@
|
|
|
1
|
-
const path = require(
|
|
2
|
-
const chalk = require(
|
|
3
|
-
const logger = require(
|
|
4
|
-
const fileUtils = require(
|
|
5
|
-
const config = require(
|
|
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
|
-
|
|
11
|
-
|
|
10
|
+
|
|
12
11
|
logger.blank();
|
|
13
|
-
logger.info(
|
|
12
|
+
logger.info("AI-DEVX Status");
|
|
14
13
|
logger.blank();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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(
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
logger.success('Installed');
|
|
49
|
+
|
|
50
|
+
logger.success(
|
|
51
|
+
`Installed (${installations.length} installation${installations.length > 1 ? "s" : ""})`,
|
|
52
|
+
);
|
|
31
53
|
logger.blank();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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(
|
|
46
|
-
logger.step(`Agents:
|
|
47
|
-
logger.step(`Skills:
|
|
48
|
-
logger.step(`Workflows:
|
|
49
|
-
logger.step(`Scripts:
|
|
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(
|
|
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
|
|
package/src/commands/update.js
CHANGED
|
@@ -1,74 +1,102 @@
|
|
|
1
|
-
const path = require(
|
|
2
|
-
const fs = require(
|
|
3
|
-
const ora = require(
|
|
4
|
-
const logger = require(
|
|
5
|
-
const fileUtils = require(
|
|
6
|
-
const config = require(
|
|
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(
|
|
12
|
-
|
|
10
|
+
|
|
11
|
+
const spinner = quiet ? null : ora("Checking for updates...").start();
|
|
12
|
+
|
|
13
13
|
try {
|
|
14
14
|
const cwd = process.cwd();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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.
|
|
31
|
-
logger.info(
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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(
|
|
65
|
-
|
|
66
|
-
|
|
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(
|
|
99
|
+
if (spinner) spinner.fail("Update failed");
|
|
72
100
|
logger.error(error.message);
|
|
73
101
|
process.exit(1);
|
|
74
102
|
}
|