@zeyue0329/xiaoma-cli 1.0.37 → 1.0.38
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/.idea/workspace.xml +27 -26
- package/JAVA-BACKEND-COMMANDS-REFERENCE.md +62 -52
- package/JAVA-BACKEND-ITERATION-GUIDE.md +125 -18
- package/README.md +1 -1
- package/common/utils/bmad-doc-template.md +5 -5
- package/dist/agents/analyst.txt +35 -5
- package/dist/agents/architect.txt +217 -31
- package/dist/agents/automation-orchestrator.txt +4 -4
- package/dist/agents/dev.txt +3 -3
- package/dist/agents/full-requirement-orchestrator.txt +11 -11
- package/dist/agents/qa.txt +102 -102
- package/dist/agents/sm.txt +6 -6
- package/dist/agents/ux-expert.txt +6 -1
- package/dist/agents/workflow-executor.txt +879 -0
- package/dist/agents/xiaoma-master.txt +258 -37
- package/dist/teams/team-all.txt +1223 -445
- package/dist/teams/team-fullstack-with-database.txt +384 -446
- package/dist/teams/team-fullstack.txt +258 -37
- package/dist/teams/team-ide-minimal.txt +111 -111
- package/dist/teams/team-no-ui.txt +252 -36
- package/docs/architecture-sharding-modification.md +623 -0
- package/docs/automated-requirements-analysis-outputs.md +896 -0
- package/package.json +1 -1
- package/tools/builders/web-builder.js +292 -142
- package/tools/bump-all-versions.js +50 -32
- package/tools/cli.js +52 -47
- package/tools/flattener/aggregate.js +30 -12
- package/tools/flattener/binary.js +46 -43
- package/tools/flattener/discovery.js +23 -15
- package/tools/flattener/files.js +6 -6
- package/tools/flattener/ignoreRules.js +122 -121
- package/tools/flattener/main.js +249 -144
- package/tools/flattener/projectRoot.js +74 -69
- package/tools/flattener/prompts.js +12 -10
- package/tools/flattener/stats.helpers.js +90 -61
- package/tools/flattener/stats.js +1 -1
- package/tools/flattener/test-matrix.js +225 -170
- package/tools/flattener/xml.js +31 -23
- package/tools/installer/bin/xiaoma.js +199 -153
- package/tools/installer/lib/config-loader.js +76 -47
- package/tools/installer/lib/file-manager.js +101 -44
- package/tools/installer/lib/ide-base-setup.js +49 -39
- package/tools/installer/lib/ide-setup.js +694 -380
- package/tools/installer/lib/installer.js +802 -469
- package/tools/installer/lib/memory-profiler.js +22 -12
- package/tools/installer/lib/module-manager.js +16 -14
- package/tools/installer/lib/resource-locator.js +61 -35
- package/tools/lib/dependency-resolver.js +34 -23
- package/tools/lib/yaml-utils.js +7 -2
- package/tools/preview-release-notes.js +33 -25
- package/tools/shared/bannerArt.js +3 -3
- package/tools/sync-installer-version.js +16 -7
- package/tools/upgraders/v3-to-v4-upgrader.js +244 -163
- package/tools/version-bump.js +24 -18
- package/tools/xiaoma-npx-wrapper.js +15 -10
- package/tools/yaml-format.js +60 -36
- package/xiaoma-core/agent-teams/team-fullstack-with-database.yaml +0 -1
- package/xiaoma-core/agents/automated-fix-validator.yaml +2 -1
- package/xiaoma-core/agents/automated-quality-validator.yaml +10 -5
- package/xiaoma-core/agents/automation-orchestrator.md +4 -4
- package/xiaoma-core/agents/dev.md +4 -4
- package/xiaoma-core/agents/enhanced-workflow-orchestrator.yaml +2 -1
- package/xiaoma-core/agents/full-requirement-orchestrator.md +11 -11
- package/xiaoma-core/agents/global-requirements-auditor.yaml +11 -3
- package/xiaoma-core/agents/intelligent-template-adapter.yaml +19 -5
- package/xiaoma-core/agents/master-execution-engine.yaml +19 -5
- package/xiaoma-core/agents/workflow-executor.md +8 -4
- package/xiaoma-core/agents/xiaoma-master.md +1 -1
- package/xiaoma-core/data/test-levels-framework.md +12 -12
- package/xiaoma-core/tasks/analyze-existing-database.md +1 -1
- package/xiaoma-core/tasks/apply-qa-fixes.md +3 -3
- package/xiaoma-core/tasks/batch-story-generation.md +22 -22
- package/xiaoma-core/tasks/create-enhanced-story-with-database.md +6 -6
- package/xiaoma-core/tasks/nfr-assess.md +6 -6
- package/xiaoma-core/tasks/project-integration-testing.md +42 -42
- package/xiaoma-core/tasks/qa-gate.md +23 -23
- package/xiaoma-core/tasks/review-story.md +18 -18
- package/xiaoma-core/tasks/risk-profile.md +25 -25
- package/xiaoma-core/tasks/serial-development-orchestration.md +51 -51
- package/xiaoma-core/tasks/test-design.md +9 -9
- package/xiaoma-core/tasks/trace-requirements.md +21 -21
- package/xiaoma-core/templates/competitor-analysis-tmpl.yaml +35 -5
- package/xiaoma-core/templates/front-end-architecture-tmpl.yaml +77 -11
- package/xiaoma-core/templates/front-end-spec-tmpl.yaml +6 -1
- package/xiaoma-core/templates/fullstack-architecture-tmpl.yaml +140 -20
- package/xiaoma-core/templates/global-qa-monitoring-tmpl.yaml +2 -1
- package/xiaoma-core/templates/requirements-coverage-audit.yaml +2 -1
- package/xiaoma-core/workflows/automated-requirements-analysis.yaml +4 -4
- package/dist/agents/database-architect.txt +0 -322
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const path = require(
|
|
2
|
-
const fs = require(
|
|
3
|
-
const yaml = require(
|
|
4
|
-
const chalk = require(
|
|
5
|
-
const inquirer = require(
|
|
6
|
-
const fileManager = require(
|
|
7
|
-
const configLoader = require(
|
|
8
|
-
const { extractYamlFromAgent } = require(
|
|
9
|
-
const BaseIdeSetup = require(
|
|
10
|
-
const resourceLocator = require(
|
|
1
|
+
const path = require("node:path");
|
|
2
|
+
const fs = require("fs-extra");
|
|
3
|
+
const yaml = require("js-yaml");
|
|
4
|
+
const chalk = require("chalk");
|
|
5
|
+
const inquirer = require("inquirer");
|
|
6
|
+
const fileManager = require("./file-manager");
|
|
7
|
+
const configLoader = require("./config-loader");
|
|
8
|
+
const { extractYamlFromAgent } = require("../../lib/yaml-utils");
|
|
9
|
+
const BaseIdeSetup = require("./ide-base-setup");
|
|
10
|
+
const resourceLocator = require("./resource-locator");
|
|
11
11
|
|
|
12
12
|
class IdeSetup extends BaseIdeSetup {
|
|
13
13
|
constructor() {
|
|
@@ -19,20 +19,31 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
19
19
|
if (this.ideAgentConfig) return this.ideAgentConfig;
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
|
-
const configPath = path.join(
|
|
23
|
-
|
|
22
|
+
const configPath = path.join(
|
|
23
|
+
__dirname,
|
|
24
|
+
"..",
|
|
25
|
+
"config",
|
|
26
|
+
"ide-agent-config.yaml",
|
|
27
|
+
);
|
|
28
|
+
const configContent = await fs.readFile(configPath, "utf8");
|
|
24
29
|
this.ideAgentConfig = yaml.load(configContent);
|
|
25
30
|
return this.ideAgentConfig;
|
|
26
31
|
} catch {
|
|
27
|
-
console.warn(
|
|
32
|
+
console.warn("Failed to load IDE agent configuration, using defaults");
|
|
28
33
|
return {
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
"roo-permissions": {},
|
|
35
|
+
"cline-order": {},
|
|
31
36
|
};
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
async setup(
|
|
40
|
+
async setup(
|
|
41
|
+
ide,
|
|
42
|
+
installDir,
|
|
43
|
+
selectedAgent = null,
|
|
44
|
+
spinner = null,
|
|
45
|
+
preConfiguredSettings = null,
|
|
46
|
+
) {
|
|
36
47
|
const ideConfig = await configLoader.getIdeConfiguration(ide);
|
|
37
48
|
|
|
38
49
|
if (!ideConfig) {
|
|
@@ -41,46 +52,58 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
switch (ide) {
|
|
44
|
-
case
|
|
55
|
+
case "cursor": {
|
|
45
56
|
return this.setupCursor(installDir, selectedAgent);
|
|
46
57
|
}
|
|
47
|
-
case
|
|
58
|
+
case "claude-code": {
|
|
48
59
|
return this.setupClaudeCode(installDir, selectedAgent);
|
|
49
60
|
}
|
|
50
|
-
case
|
|
61
|
+
case "crush": {
|
|
51
62
|
return this.setupCrush(installDir, selectedAgent);
|
|
52
63
|
}
|
|
53
|
-
case
|
|
64
|
+
case "windsurf": {
|
|
54
65
|
return this.setupWindsurf(installDir, selectedAgent);
|
|
55
66
|
}
|
|
56
|
-
case
|
|
67
|
+
case "trae": {
|
|
57
68
|
return this.setupTrae(installDir, selectedAgent);
|
|
58
69
|
}
|
|
59
|
-
case
|
|
70
|
+
case "roo": {
|
|
60
71
|
return this.setupRoo(installDir, selectedAgent);
|
|
61
72
|
}
|
|
62
|
-
case
|
|
73
|
+
case "cline": {
|
|
63
74
|
return this.setupCline(installDir, selectedAgent);
|
|
64
75
|
}
|
|
65
|
-
case
|
|
76
|
+
case "kilo": {
|
|
66
77
|
return this.setupKilocode(installDir, selectedAgent);
|
|
67
78
|
}
|
|
68
|
-
case
|
|
79
|
+
case "gemini": {
|
|
69
80
|
return this.setupGeminiCli(installDir, selectedAgent);
|
|
70
81
|
}
|
|
71
|
-
case
|
|
72
|
-
return this.setupGitHubCopilot(
|
|
82
|
+
case "github-copilot": {
|
|
83
|
+
return this.setupGitHubCopilot(
|
|
84
|
+
installDir,
|
|
85
|
+
selectedAgent,
|
|
86
|
+
spinner,
|
|
87
|
+
preConfiguredSettings,
|
|
88
|
+
);
|
|
73
89
|
}
|
|
74
|
-
case
|
|
90
|
+
case "qwen-code": {
|
|
75
91
|
return this.setupQwenCode(installDir, selectedAgent);
|
|
76
92
|
}
|
|
77
|
-
case
|
|
78
|
-
return this.setupAuggieCLI(
|
|
93
|
+
case "auggie-cli": {
|
|
94
|
+
return this.setupAuggieCLI(
|
|
95
|
+
installDir,
|
|
96
|
+
selectedAgent,
|
|
97
|
+
spinner,
|
|
98
|
+
preConfiguredSettings,
|
|
99
|
+
);
|
|
79
100
|
}
|
|
80
|
-
case
|
|
81
|
-
return this.setupCodex(installDir, selectedAgent, {
|
|
101
|
+
case "codex": {
|
|
102
|
+
return this.setupCodex(installDir, selectedAgent, {
|
|
103
|
+
webEnabled: false,
|
|
104
|
+
});
|
|
82
105
|
}
|
|
83
|
-
case
|
|
106
|
+
case "codex-web": {
|
|
84
107
|
return this.setupCodex(installDir, selectedAgent, { webEnabled: true });
|
|
85
108
|
}
|
|
86
109
|
default: {
|
|
@@ -94,15 +117,17 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
94
117
|
options = options ?? { webEnabled: false };
|
|
95
118
|
// Codex reads AGENTS.md at the project root as project memory (CLI & Web).
|
|
96
119
|
// Inject/update a XIAOMAMA section with guidance, directory, and details.
|
|
97
|
-
const filePath = path.join(installDir,
|
|
98
|
-
const startMarker =
|
|
99
|
-
const endMarker =
|
|
120
|
+
const filePath = path.join(installDir, "AGENTS.md");
|
|
121
|
+
const startMarker = "<!-- BEGIN: XIAOMAMA-AGENTS -->";
|
|
122
|
+
const endMarker = "<!-- END: XIAOMAMA-AGENTS -->";
|
|
100
123
|
|
|
101
|
-
const agents = selectedAgent
|
|
124
|
+
const agents = selectedAgent
|
|
125
|
+
? [selectedAgent]
|
|
126
|
+
: await this.getAllAgentIds(installDir);
|
|
102
127
|
const tasks = await this.getAllTaskIds(installDir);
|
|
103
128
|
|
|
104
129
|
// Build XIAOMAMA section content
|
|
105
|
-
let section =
|
|
130
|
+
let section = "";
|
|
106
131
|
section += `${startMarker}\n`;
|
|
107
132
|
section += `# XIAOMA-CLI Agents and Tasks\n\n`;
|
|
108
133
|
section += `This section is auto-generated by XIAOMA-CLI for Codex. Codex merges this AGENTS.md into context.\n\n`;
|
|
@@ -129,23 +154,40 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
129
154
|
const yamlMatch = raw.match(/```ya?ml\r?\n([\s\S]*?)```/);
|
|
130
155
|
const yamlBlock = yamlMatch ? yamlMatch[1].trim() : null;
|
|
131
156
|
const title = await this.getAgentTitle(agentId, installDir);
|
|
132
|
-
const whenToUse =
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
const whenToUse =
|
|
158
|
+
yamlBlock?.match(/whenToUse:\s*"?([^\n"]+)"?/i)?.[1]?.trim() || "";
|
|
159
|
+
agentSummaries.push({
|
|
160
|
+
agentId,
|
|
161
|
+
title,
|
|
162
|
+
whenToUse,
|
|
163
|
+
yamlBlock,
|
|
164
|
+
raw,
|
|
165
|
+
path: agentPath,
|
|
166
|
+
});
|
|
167
|
+
section += `| ${title} | ${agentId} | ${whenToUse || "—"} |\n`;
|
|
135
168
|
}
|
|
136
169
|
section += `\n`;
|
|
137
170
|
|
|
138
171
|
// Detailed agent sections
|
|
139
|
-
for (const {
|
|
140
|
-
|
|
172
|
+
for (const {
|
|
173
|
+
agentId,
|
|
174
|
+
title,
|
|
175
|
+
whenToUse,
|
|
176
|
+
yamlBlock,
|
|
177
|
+
raw,
|
|
178
|
+
path: agentPath,
|
|
179
|
+
} of agentSummaries) {
|
|
180
|
+
const relativePath = path
|
|
181
|
+
.relative(installDir, agentPath)
|
|
182
|
+
.replaceAll("\\", "/");
|
|
141
183
|
section += `### ${title} (id: ${agentId})\n`;
|
|
142
184
|
section += `Source: ${relativePath}\n\n`;
|
|
143
185
|
if (whenToUse) section += `- When to use: ${whenToUse}\n`;
|
|
144
186
|
section += `- How to activate: Mention "As ${agentId}, ..." or "Use ${title} to ..."\n\n`;
|
|
145
187
|
if (yamlBlock) {
|
|
146
|
-
section +=
|
|
188
|
+
section += "```yaml\n" + yamlBlock + "\n```\n\n";
|
|
147
189
|
} else {
|
|
148
|
-
section +=
|
|
190
|
+
section += "```md\n" + raw.trim() + "\n```\n\n";
|
|
149
191
|
}
|
|
150
192
|
}
|
|
151
193
|
|
|
@@ -157,24 +199,26 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
157
199
|
const taskPath = await this.findTaskPath(taskId, installDir);
|
|
158
200
|
if (!taskPath) continue;
|
|
159
201
|
const raw = await fileManager.readFile(taskPath);
|
|
160
|
-
const relativePath = path
|
|
202
|
+
const relativePath = path
|
|
203
|
+
.relative(installDir, taskPath)
|
|
204
|
+
.replaceAll("\\", "/");
|
|
161
205
|
section += `### Task: ${taskId}\n`;
|
|
162
206
|
section += `Source: ${relativePath}\n`;
|
|
163
207
|
section += `- How to use: "Use task ${taskId} with the appropriate agent" and paste relevant parts as needed.\n\n`;
|
|
164
|
-
section +=
|
|
208
|
+
section += "```md\n" + raw.trim() + "\n```\n\n";
|
|
165
209
|
}
|
|
166
210
|
}
|
|
167
211
|
|
|
168
212
|
section += `${endMarker}\n`;
|
|
169
213
|
|
|
170
214
|
// Write or update AGENTS.md
|
|
171
|
-
let finalContent =
|
|
215
|
+
let finalContent = "";
|
|
172
216
|
if (await fileManager.pathExists(filePath)) {
|
|
173
217
|
const existing = await fileManager.readFile(filePath);
|
|
174
218
|
if (existing.includes(startMarker) && existing.includes(endMarker)) {
|
|
175
219
|
// Replace existing XIAOMAMA block
|
|
176
220
|
const pattern = String.raw`${startMarker}[\s\S]*?${endMarker}`;
|
|
177
|
-
const replaced = existing.replace(new RegExp(pattern,
|
|
221
|
+
const replaced = existing.replace(new RegExp(pattern, "m"), section);
|
|
178
222
|
finalContent = replaced;
|
|
179
223
|
} else {
|
|
180
224
|
// Append XIAOMAMA block to existing file
|
|
@@ -182,47 +226,66 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
182
226
|
}
|
|
183
227
|
} else {
|
|
184
228
|
// Create fresh AGENTS.md with a small header and XIAOMAMA block
|
|
185
|
-
finalContent +=
|
|
186
|
-
finalContent +=
|
|
229
|
+
finalContent += "# Project Agents\n\n";
|
|
230
|
+
finalContent +=
|
|
231
|
+
"This file provides guidance and memory for Codex CLI.\n\n";
|
|
187
232
|
finalContent += section;
|
|
188
233
|
}
|
|
189
234
|
|
|
190
235
|
await fileManager.writeFile(filePath, finalContent);
|
|
191
|
-
console.log(
|
|
236
|
+
console.log(
|
|
237
|
+
chalk.green("✓ Created/updated AGENTS.md for Codex CLI integration"),
|
|
238
|
+
);
|
|
192
239
|
console.log(
|
|
193
240
|
chalk.dim(
|
|
194
|
-
|
|
241
|
+
"Codex reads AGENTS.md automatically. Run `codex` in this project to use XIAOMAMA agents.",
|
|
195
242
|
),
|
|
196
243
|
);
|
|
197
244
|
|
|
198
245
|
// Optionally add helpful npm scripts if a package.json exists
|
|
199
246
|
try {
|
|
200
|
-
const pkgPath = path.join(installDir,
|
|
247
|
+
const pkgPath = path.join(installDir, "package.json");
|
|
201
248
|
if (await fileManager.pathExists(pkgPath)) {
|
|
202
249
|
const pkgRaw = await fileManager.readFile(pkgPath);
|
|
203
250
|
const pkg = JSON.parse(pkgRaw);
|
|
204
251
|
pkg.scripts = pkg.scripts || {};
|
|
205
252
|
const updated = { ...pkg.scripts };
|
|
206
|
-
if (!updated[
|
|
207
|
-
|
|
208
|
-
if (!updated[
|
|
253
|
+
if (!updated["bmad:refresh"])
|
|
254
|
+
updated["bmad:refresh"] = "xiaoma-cli install -f -i codex";
|
|
255
|
+
if (!updated["bmad:list"])
|
|
256
|
+
updated["bmad:list"] = "xiaoma-cli list:agents";
|
|
257
|
+
if (!updated["bmad:validate"])
|
|
258
|
+
updated["bmad:validate"] = "xiaoma-cli validate";
|
|
209
259
|
const changed = JSON.stringify(updated) !== JSON.stringify(pkg.scripts);
|
|
210
260
|
if (changed) {
|
|
211
261
|
const newPkg = { ...pkg, scripts: updated };
|
|
212
|
-
await fileManager.writeFile(
|
|
213
|
-
|
|
262
|
+
await fileManager.writeFile(
|
|
263
|
+
pkgPath,
|
|
264
|
+
JSON.stringify(newPkg, null, 2) + "\n",
|
|
265
|
+
);
|
|
266
|
+
console.log(
|
|
267
|
+
chalk.green(
|
|
268
|
+
"✓ Added npm scripts: bmad:refresh, bmad:list, bmad:validate",
|
|
269
|
+
),
|
|
270
|
+
);
|
|
214
271
|
}
|
|
215
272
|
}
|
|
216
273
|
} catch {
|
|
217
274
|
console.log(
|
|
218
|
-
chalk.yellow(
|
|
275
|
+
chalk.yellow(
|
|
276
|
+
"⚠︎ Skipped adding npm scripts (package.json not writable or invalid)",
|
|
277
|
+
),
|
|
219
278
|
);
|
|
220
279
|
}
|
|
221
280
|
|
|
222
281
|
// Adjust .gitignore behavior depending on Codex mode
|
|
223
282
|
try {
|
|
224
|
-
const gitignorePath = path.join(installDir,
|
|
225
|
-
const ignoreLines = [
|
|
283
|
+
const gitignorePath = path.join(installDir, ".gitignore");
|
|
284
|
+
const ignoreLines = [
|
|
285
|
+
"# XIAOMAMA (local only)",
|
|
286
|
+
".xiaoma-core/",
|
|
287
|
+
".bmad-*/",
|
|
288
|
+
];
|
|
226
289
|
const exists = await fileManager.pathExists(gitignorePath);
|
|
227
290
|
if (options.webEnabled) {
|
|
228
291
|
if (exists) {
|
|
@@ -230,40 +293,55 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
230
293
|
// Remove lines that ignore XIAOMAMA dot-folders
|
|
231
294
|
const updated = gi
|
|
232
295
|
.split(/\r?\n/)
|
|
233
|
-
.filter(
|
|
234
|
-
|
|
296
|
+
.filter(
|
|
297
|
+
(l) =>
|
|
298
|
+
!/^\s*\.xiaoma-core\/?\s*$/.test(l) &&
|
|
299
|
+
!/^\s*\.bmad-\*\/?\s*$/.test(l),
|
|
300
|
+
)
|
|
301
|
+
.join("\n");
|
|
235
302
|
if (updated !== gi) {
|
|
236
|
-
await fileManager.writeFile(
|
|
237
|
-
|
|
303
|
+
await fileManager.writeFile(
|
|
304
|
+
gitignorePath,
|
|
305
|
+
updated.trimEnd() + "\n",
|
|
306
|
+
);
|
|
307
|
+
console.log(
|
|
308
|
+
chalk.green(
|
|
309
|
+
"✓ Updated .gitignore to include .xiaoma-core in commits",
|
|
310
|
+
),
|
|
311
|
+
);
|
|
238
312
|
}
|
|
239
313
|
}
|
|
240
314
|
} else {
|
|
241
315
|
// Local-only: add ignores if missing
|
|
242
|
-
let base = exists ? await fileManager.readFile(gitignorePath) :
|
|
243
|
-
const haveCore = base.includes(
|
|
244
|
-
const haveStar = base.includes(
|
|
316
|
+
let base = exists ? await fileManager.readFile(gitignorePath) : "";
|
|
317
|
+
const haveCore = base.includes(".xiaoma-core/");
|
|
318
|
+
const haveStar = base.includes(".bmad-*/");
|
|
245
319
|
if (!haveCore || !haveStar) {
|
|
246
|
-
const sep = base.endsWith(
|
|
247
|
-
const add = [!haveCore || !haveStar ? ignoreLines.join(
|
|
320
|
+
const sep = base.endsWith("\n") || base.length === 0 ? "" : "\n";
|
|
321
|
+
const add = [!haveCore || !haveStar ? ignoreLines.join("\n") : ""]
|
|
248
322
|
.filter(Boolean)
|
|
249
|
-
.join(
|
|
250
|
-
const out = base + sep + add +
|
|
323
|
+
.join("\n");
|
|
324
|
+
const out = base + sep + add + "\n";
|
|
251
325
|
await fileManager.writeFile(gitignorePath, out);
|
|
252
326
|
console.log(
|
|
253
|
-
chalk.green(
|
|
327
|
+
chalk.green(
|
|
328
|
+
"✓ Added .xiaoma-core/* to .gitignore for local-only Codex setup",
|
|
329
|
+
),
|
|
254
330
|
);
|
|
255
331
|
}
|
|
256
332
|
}
|
|
257
333
|
} catch {
|
|
258
|
-
console.log(chalk.yellow(
|
|
334
|
+
console.log(chalk.yellow("⚠︎ Could not update .gitignore (skipping)"));
|
|
259
335
|
}
|
|
260
336
|
|
|
261
337
|
return true;
|
|
262
338
|
}
|
|
263
339
|
|
|
264
340
|
async setupCursor(installDir, selectedAgent) {
|
|
265
|
-
const cursorRulesDir = path.join(installDir,
|
|
266
|
-
const agents = selectedAgent
|
|
341
|
+
const cursorRulesDir = path.join(installDir, ".cursor", "rules", "bmad");
|
|
342
|
+
const agents = selectedAgent
|
|
343
|
+
? [selectedAgent]
|
|
344
|
+
: await this.getAllAgentIds(installDir);
|
|
267
345
|
|
|
268
346
|
await fileManager.ensureDirectory(cursorRulesDir);
|
|
269
347
|
|
|
@@ -271,7 +349,12 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
271
349
|
const agentPath = await this.findAgentPath(agentId, installDir);
|
|
272
350
|
|
|
273
351
|
if (agentPath) {
|
|
274
|
-
const mdcContent = await this.createAgentRuleContent(
|
|
352
|
+
const mdcContent = await this.createAgentRuleContent(
|
|
353
|
+
agentId,
|
|
354
|
+
agentPath,
|
|
355
|
+
installDir,
|
|
356
|
+
"mdc",
|
|
357
|
+
);
|
|
275
358
|
const mdcPath = path.join(cursorRulesDir, `${agentId}.mdc`);
|
|
276
359
|
await fileManager.writeFile(mdcPath, mdcContent);
|
|
277
360
|
console.log(chalk.green(`✓ Created rule: ${agentId}.mdc`));
|
|
@@ -285,21 +368,25 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
285
368
|
async setupCrush(installDir, selectedAgent) {
|
|
286
369
|
// Setup xiaoma-core commands
|
|
287
370
|
const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
|
|
288
|
-
const coreAgents = selectedAgent
|
|
371
|
+
const coreAgents = selectedAgent
|
|
372
|
+
? [selectedAgent]
|
|
373
|
+
: await this.getCoreAgentIds(installDir);
|
|
289
374
|
const coreTasks = await this.getCoreTaskIds(installDir);
|
|
290
375
|
await this.setupCrushForPackage(
|
|
291
376
|
installDir,
|
|
292
|
-
|
|
377
|
+
"core",
|
|
293
378
|
coreSlashPrefix,
|
|
294
379
|
coreAgents,
|
|
295
380
|
coreTasks,
|
|
296
|
-
|
|
381
|
+
".xiaoma-core",
|
|
297
382
|
);
|
|
298
383
|
|
|
299
384
|
// Setup expansion pack commands
|
|
300
385
|
const expansionPacks = await this.getInstalledExpansionPacks(installDir);
|
|
301
386
|
for (const packInfo of expansionPacks) {
|
|
302
|
-
const packSlashPrefix = await this.getExpansionPackSlashPrefix(
|
|
387
|
+
const packSlashPrefix = await this.getExpansionPackSlashPrefix(
|
|
388
|
+
packInfo.path,
|
|
389
|
+
);
|
|
303
390
|
const packAgents = await this.getExpansionPackAgents(packInfo.path);
|
|
304
391
|
const packTasks = await this.getExpansionPackTasks(packInfo.path);
|
|
305
392
|
|
|
@@ -323,21 +410,25 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
323
410
|
async setupClaudeCode(installDir, selectedAgent) {
|
|
324
411
|
// Setup xiaoma-core commands
|
|
325
412
|
const coreSlashPrefix = await this.getCoreSlashPrefix(installDir);
|
|
326
|
-
const coreAgents = selectedAgent
|
|
413
|
+
const coreAgents = selectedAgent
|
|
414
|
+
? [selectedAgent]
|
|
415
|
+
: await this.getCoreAgentIds(installDir);
|
|
327
416
|
const coreTasks = await this.getCoreTaskIds(installDir);
|
|
328
417
|
await this.setupClaudeCodeForPackage(
|
|
329
418
|
installDir,
|
|
330
|
-
|
|
419
|
+
"core",
|
|
331
420
|
coreSlashPrefix,
|
|
332
421
|
coreAgents,
|
|
333
422
|
coreTasks,
|
|
334
|
-
|
|
423
|
+
".xiaoma-core",
|
|
335
424
|
);
|
|
336
425
|
|
|
337
426
|
// Setup expansion pack commands
|
|
338
427
|
const expansionPacks = await this.getInstalledExpansionPacks(installDir);
|
|
339
428
|
for (const packInfo of expansionPacks) {
|
|
340
|
-
const packSlashPrefix = await this.getExpansionPackSlashPrefix(
|
|
429
|
+
const packSlashPrefix = await this.getExpansionPackSlashPrefix(
|
|
430
|
+
packInfo.path,
|
|
431
|
+
);
|
|
341
432
|
const packAgents = await this.getExpansionPackAgents(packInfo.path);
|
|
342
433
|
const packTasks = await this.getExpansionPackTasks(packInfo.path);
|
|
343
434
|
|
|
@@ -366,9 +457,14 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
366
457
|
taskIds,
|
|
367
458
|
rootPath,
|
|
368
459
|
) {
|
|
369
|
-
const commandsBaseDir = path.join(
|
|
370
|
-
|
|
371
|
-
|
|
460
|
+
const commandsBaseDir = path.join(
|
|
461
|
+
installDir,
|
|
462
|
+
".claude",
|
|
463
|
+
"commands",
|
|
464
|
+
slashPrefix,
|
|
465
|
+
);
|
|
466
|
+
const agentsDir = path.join(commandsBaseDir, "agents");
|
|
467
|
+
const tasksDir = path.join(commandsBaseDir, "tasks");
|
|
372
468
|
|
|
373
469
|
// Ensure directories exist
|
|
374
470
|
await fileManager.ensureDirectory(agentsDir);
|
|
@@ -378,12 +474,17 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
378
474
|
for (const agentId of agentIds) {
|
|
379
475
|
// Find the agent file - for expansion packs, prefer the expansion pack version
|
|
380
476
|
let agentPath;
|
|
381
|
-
if (packageName ===
|
|
477
|
+
if (packageName === "core") {
|
|
382
478
|
// For core, use the normal search
|
|
383
479
|
agentPath = await this.findAgentPath(agentId, installDir);
|
|
384
480
|
} else {
|
|
385
481
|
// For expansion packs, first try to find the agent in the expansion pack directory
|
|
386
|
-
const expansionPackPath = path.join(
|
|
482
|
+
const expansionPackPath = path.join(
|
|
483
|
+
installDir,
|
|
484
|
+
rootPath,
|
|
485
|
+
"agents",
|
|
486
|
+
`${agentId}.md`,
|
|
487
|
+
);
|
|
387
488
|
if (await fileManager.pathExists(expansionPackPath)) {
|
|
388
489
|
agentPath = expansionPackPath;
|
|
389
490
|
} else {
|
|
@@ -399,7 +500,7 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
399
500
|
let agentContent = await fileManager.readFile(agentPath);
|
|
400
501
|
|
|
401
502
|
// Replace {root} placeholder with the appropriate root path for this context
|
|
402
|
-
agentContent = agentContent.replaceAll(
|
|
503
|
+
agentContent = agentContent.replaceAll("{root}", rootPath);
|
|
403
504
|
|
|
404
505
|
// Add command header
|
|
405
506
|
let commandContent = `# /${agentId} Command\n\n`;
|
|
@@ -415,12 +516,17 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
415
516
|
for (const taskId of taskIds) {
|
|
416
517
|
// Find the task file - for expansion packs, prefer the expansion pack version
|
|
417
518
|
let taskPath;
|
|
418
|
-
if (packageName ===
|
|
519
|
+
if (packageName === "core") {
|
|
419
520
|
// For core, use the normal search
|
|
420
521
|
taskPath = await this.findTaskPath(taskId, installDir);
|
|
421
522
|
} else {
|
|
422
523
|
// For expansion packs, first try to find the task in the expansion pack directory
|
|
423
|
-
const expansionPackPath = path.join(
|
|
524
|
+
const expansionPackPath = path.join(
|
|
525
|
+
installDir,
|
|
526
|
+
rootPath,
|
|
527
|
+
"tasks",
|
|
528
|
+
`${taskId}.md`,
|
|
529
|
+
);
|
|
424
530
|
if (await fileManager.pathExists(expansionPackPath)) {
|
|
425
531
|
taskPath = expansionPackPath;
|
|
426
532
|
} else {
|
|
@@ -436,7 +542,7 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
436
542
|
let taskContent = await fileManager.readFile(taskPath);
|
|
437
543
|
|
|
438
544
|
// Replace {root} placeholder with the appropriate root path for this context
|
|
439
|
-
taskContent = taskContent.replaceAll(
|
|
545
|
+
taskContent = taskContent.replaceAll("{root}", rootPath);
|
|
440
546
|
|
|
441
547
|
// Add command header
|
|
442
548
|
let commandContent = `# /${taskId} Task\n\n`;
|
|
@@ -449,16 +555,30 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
449
555
|
}
|
|
450
556
|
|
|
451
557
|
console.log(
|
|
452
|
-
chalk.green(
|
|
558
|
+
chalk.green(
|
|
559
|
+
`\n✓ Created Claude Code commands for ${packageName} in ${commandsBaseDir}`,
|
|
560
|
+
),
|
|
453
561
|
);
|
|
454
562
|
console.log(chalk.dim(` - Agents in: ${agentsDir}`));
|
|
455
563
|
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
|
456
564
|
}
|
|
457
565
|
|
|
458
|
-
async setupCrushForPackage(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
566
|
+
async setupCrushForPackage(
|
|
567
|
+
installDir,
|
|
568
|
+
packageName,
|
|
569
|
+
slashPrefix,
|
|
570
|
+
agentIds,
|
|
571
|
+
taskIds,
|
|
572
|
+
rootPath,
|
|
573
|
+
) {
|
|
574
|
+
const commandsBaseDir = path.join(
|
|
575
|
+
installDir,
|
|
576
|
+
".crush",
|
|
577
|
+
"commands",
|
|
578
|
+
slashPrefix,
|
|
579
|
+
);
|
|
580
|
+
const agentsDir = path.join(commandsBaseDir, "agents");
|
|
581
|
+
const tasksDir = path.join(commandsBaseDir, "tasks");
|
|
462
582
|
|
|
463
583
|
// Ensure directories exist
|
|
464
584
|
await fileManager.ensureDirectory(agentsDir);
|
|
@@ -468,12 +588,17 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
468
588
|
for (const agentId of agentIds) {
|
|
469
589
|
// Find the agent file - for expansion packs, prefer the expansion pack version
|
|
470
590
|
let agentPath;
|
|
471
|
-
if (packageName ===
|
|
591
|
+
if (packageName === "core") {
|
|
472
592
|
// For core, use the normal search
|
|
473
593
|
agentPath = await this.findAgentPath(agentId, installDir);
|
|
474
594
|
} else {
|
|
475
595
|
// For expansion packs, first try to find the agent in the expansion pack directory
|
|
476
|
-
const expansionPackPath = path.join(
|
|
596
|
+
const expansionPackPath = path.join(
|
|
597
|
+
installDir,
|
|
598
|
+
rootPath,
|
|
599
|
+
"agents",
|
|
600
|
+
`${agentId}.md`,
|
|
601
|
+
);
|
|
477
602
|
if (await fileManager.pathExists(expansionPackPath)) {
|
|
478
603
|
agentPath = expansionPackPath;
|
|
479
604
|
} else {
|
|
@@ -489,7 +614,7 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
489
614
|
let agentContent = await fileManager.readFile(agentPath);
|
|
490
615
|
|
|
491
616
|
// Replace {root} placeholder with the appropriate root path for this context
|
|
492
|
-
agentContent = agentContent.replaceAll(
|
|
617
|
+
agentContent = agentContent.replaceAll("{root}", rootPath);
|
|
493
618
|
|
|
494
619
|
// Add command header
|
|
495
620
|
let commandContent = `# /${agentId} Command\n\n`;
|
|
@@ -505,12 +630,17 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
505
630
|
for (const taskId of taskIds) {
|
|
506
631
|
// Find the task file - for expansion packs, prefer the expansion pack version
|
|
507
632
|
let taskPath;
|
|
508
|
-
if (packageName ===
|
|
633
|
+
if (packageName === "core") {
|
|
509
634
|
// For core, use the normal search
|
|
510
635
|
taskPath = await this.findTaskPath(taskId, installDir);
|
|
511
636
|
} else {
|
|
512
637
|
// For expansion packs, first try to find the task in the expansion pack directory
|
|
513
|
-
const expansionPackPath = path.join(
|
|
638
|
+
const expansionPackPath = path.join(
|
|
639
|
+
installDir,
|
|
640
|
+
rootPath,
|
|
641
|
+
"tasks",
|
|
642
|
+
`${taskId}.md`,
|
|
643
|
+
);
|
|
514
644
|
if (await fileManager.pathExists(expansionPackPath)) {
|
|
515
645
|
taskPath = expansionPackPath;
|
|
516
646
|
} else {
|
|
@@ -526,7 +656,7 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
526
656
|
let taskContent = await fileManager.readFile(taskPath);
|
|
527
657
|
|
|
528
658
|
// Replace {root} placeholder with the appropriate root path for this context
|
|
529
|
-
taskContent = taskContent.replaceAll(
|
|
659
|
+
taskContent = taskContent.replaceAll("{root}", rootPath);
|
|
530
660
|
|
|
531
661
|
// Add command header
|
|
532
662
|
let commandContent = `# /${taskId} Task\n\n`;
|
|
@@ -538,14 +668,20 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
538
668
|
}
|
|
539
669
|
}
|
|
540
670
|
|
|
541
|
-
console.log(
|
|
671
|
+
console.log(
|
|
672
|
+
chalk.green(
|
|
673
|
+
`\n✓ Created Crush commands for ${packageName} in ${commandsBaseDir}`,
|
|
674
|
+
),
|
|
675
|
+
);
|
|
542
676
|
console.log(chalk.dim(` - Agents in: ${agentsDir}`));
|
|
543
677
|
console.log(chalk.dim(` - Tasks in: ${tasksDir}`));
|
|
544
678
|
}
|
|
545
679
|
|
|
546
680
|
async setupWindsurf(installDir, selectedAgent) {
|
|
547
|
-
const windsurfWorkflowDir = path.join(installDir,
|
|
548
|
-
const agents = selectedAgent
|
|
681
|
+
const windsurfWorkflowDir = path.join(installDir, ".windsurf", "workflows");
|
|
682
|
+
const agents = selectedAgent
|
|
683
|
+
? [selectedAgent]
|
|
684
|
+
: await this.getAllAgentIds(installDir);
|
|
549
685
|
|
|
550
686
|
await fileManager.ensureDirectory(windsurfWorkflowDir);
|
|
551
687
|
|
|
@@ -569,14 +705,18 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
569
705
|
}
|
|
570
706
|
}
|
|
571
707
|
|
|
572
|
-
console.log(
|
|
708
|
+
console.log(
|
|
709
|
+
chalk.green(`\n✓ Created Windsurf workflows in ${windsurfWorkflowDir}`),
|
|
710
|
+
);
|
|
573
711
|
|
|
574
712
|
return true;
|
|
575
713
|
}
|
|
576
714
|
|
|
577
715
|
async setupTrae(installDir, selectedAgent) {
|
|
578
|
-
const traeRulesDir = path.join(installDir,
|
|
579
|
-
const agents = selectedAgent
|
|
716
|
+
const traeRulesDir = path.join(installDir, ".trae", "rules");
|
|
717
|
+
const agents = selectedAgent
|
|
718
|
+
? [selectedAgent]
|
|
719
|
+
: await this.getAllAgentIds(installDir);
|
|
580
720
|
|
|
581
721
|
await fileManager.ensureDirectory(traeRulesDir);
|
|
582
722
|
|
|
@@ -594,23 +734,25 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
594
734
|
agentId,
|
|
595
735
|
installDir,
|
|
596
736
|
)} agent persona.\n\n`;
|
|
597
|
-
mdContent +=
|
|
737
|
+
mdContent += "## Agent Activation\n\n";
|
|
598
738
|
mdContent +=
|
|
599
|
-
|
|
600
|
-
mdContent +=
|
|
739
|
+
"CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
|
|
740
|
+
mdContent += "```yaml\n";
|
|
601
741
|
// Extract just the YAML content from the agent file
|
|
602
742
|
const yamlContent = extractYamlFromAgent(agentContent);
|
|
603
743
|
if (yamlContent) {
|
|
604
744
|
mdContent += yamlContent;
|
|
605
745
|
} else {
|
|
606
746
|
// If no YAML found, include the whole content minus the header
|
|
607
|
-
mdContent += agentContent.replace(/^#.*$/m,
|
|
747
|
+
mdContent += agentContent.replace(/^#.*$/m, "").trim();
|
|
608
748
|
}
|
|
609
|
-
mdContent +=
|
|
610
|
-
mdContent +=
|
|
611
|
-
const relativePath = path
|
|
749
|
+
mdContent += "\n```\n\n";
|
|
750
|
+
mdContent += "## File Reference\n\n";
|
|
751
|
+
const relativePath = path
|
|
752
|
+
.relative(installDir, agentPath)
|
|
753
|
+
.replaceAll("\\", "/");
|
|
612
754
|
mdContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
|
|
613
|
-
mdContent +=
|
|
755
|
+
mdContent += "## Usage\n\n";
|
|
614
756
|
mdContent += `When the user types \`@${agentId}\`, activate this ${await this.getAgentTitle(
|
|
615
757
|
agentId,
|
|
616
758
|
installDir,
|
|
@@ -625,13 +767,13 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
625
767
|
async findAgentPath(agentId, installDir) {
|
|
626
768
|
// Try to find the agent file in various locations
|
|
627
769
|
const possiblePaths = [
|
|
628
|
-
path.join(installDir,
|
|
629
|
-
path.join(installDir,
|
|
770
|
+
path.join(installDir, ".xiaoma-core", "agents", `${agentId}.md`),
|
|
771
|
+
path.join(installDir, "agents", `${agentId}.md`),
|
|
630
772
|
];
|
|
631
773
|
|
|
632
774
|
// Also check expansion pack directories
|
|
633
|
-
const glob = require(
|
|
634
|
-
const expansionDirectories = glob.sync(
|
|
775
|
+
const glob = require("glob");
|
|
776
|
+
const expansionDirectories = glob.sync(".*/agents", { cwd: installDir });
|
|
635
777
|
for (const expDir of expansionDirectories) {
|
|
636
778
|
possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
|
|
637
779
|
}
|
|
@@ -646,26 +788,28 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
646
788
|
}
|
|
647
789
|
|
|
648
790
|
async getAllAgentIds(installDir) {
|
|
649
|
-
const glob = require(
|
|
791
|
+
const glob = require("glob");
|
|
650
792
|
const allAgentIds = [];
|
|
651
793
|
|
|
652
794
|
// Check core agents in .xiaoma-core or root
|
|
653
|
-
let agentsDir = path.join(installDir,
|
|
795
|
+
let agentsDir = path.join(installDir, ".xiaoma-core", "agents");
|
|
654
796
|
if (!(await fileManager.pathExists(agentsDir))) {
|
|
655
|
-
agentsDir = path.join(installDir,
|
|
797
|
+
agentsDir = path.join(installDir, "agents");
|
|
656
798
|
}
|
|
657
799
|
|
|
658
800
|
if (await fileManager.pathExists(agentsDir)) {
|
|
659
|
-
const agentFiles = glob.sync(
|
|
660
|
-
allAgentIds.push(...agentFiles.map((file) => path.basename(file,
|
|
801
|
+
const agentFiles = glob.sync("*.md", { cwd: agentsDir });
|
|
802
|
+
allAgentIds.push(...agentFiles.map((file) => path.basename(file, ".md")));
|
|
661
803
|
}
|
|
662
804
|
|
|
663
805
|
// Also check for expansion pack agents in dot folders
|
|
664
|
-
const expansionDirectories = glob.sync(
|
|
806
|
+
const expansionDirectories = glob.sync(".*/agents", { cwd: installDir });
|
|
665
807
|
for (const expDir of expansionDirectories) {
|
|
666
808
|
const fullExpDir = path.join(installDir, expDir);
|
|
667
|
-
const expAgentFiles = glob.sync(
|
|
668
|
-
allAgentIds.push(
|
|
809
|
+
const expAgentFiles = glob.sync("*.md", { cwd: fullExpDir });
|
|
810
|
+
allAgentIds.push(
|
|
811
|
+
...expAgentFiles.map((file) => path.basename(file, ".md")),
|
|
812
|
+
);
|
|
669
813
|
}
|
|
670
814
|
|
|
671
815
|
// Remove duplicates
|
|
@@ -676,15 +820,15 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
676
820
|
const allAgentIds = [];
|
|
677
821
|
|
|
678
822
|
// Check core agents in .xiaoma-core or root only
|
|
679
|
-
let agentsDir = path.join(installDir,
|
|
823
|
+
let agentsDir = path.join(installDir, ".xiaoma-core", "agents");
|
|
680
824
|
if (!(await fileManager.pathExists(agentsDir))) {
|
|
681
|
-
agentsDir = path.join(installDir,
|
|
825
|
+
agentsDir = path.join(installDir, "xiaoma-core", "agents");
|
|
682
826
|
}
|
|
683
827
|
|
|
684
828
|
if (await fileManager.pathExists(agentsDir)) {
|
|
685
|
-
const glob = require(
|
|
686
|
-
const agentFiles = glob.sync(
|
|
687
|
-
allAgentIds.push(...agentFiles.map((file) => path.basename(file,
|
|
829
|
+
const glob = require("glob");
|
|
830
|
+
const agentFiles = glob.sync("*.md", { cwd: agentsDir });
|
|
831
|
+
allAgentIds.push(...agentFiles.map((file) => path.basename(file, ".md")));
|
|
688
832
|
}
|
|
689
833
|
|
|
690
834
|
return [...new Set(allAgentIds)];
|
|
@@ -694,22 +838,24 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
694
838
|
const allTaskIds = [];
|
|
695
839
|
|
|
696
840
|
// Check core tasks in .xiaoma-core or root only
|
|
697
|
-
let tasksDir = path.join(installDir,
|
|
841
|
+
let tasksDir = path.join(installDir, ".xiaoma-core", "tasks");
|
|
698
842
|
if (!(await fileManager.pathExists(tasksDir))) {
|
|
699
|
-
tasksDir = path.join(installDir,
|
|
843
|
+
tasksDir = path.join(installDir, "xiaoma-core", "tasks");
|
|
700
844
|
}
|
|
701
845
|
|
|
702
846
|
if (await fileManager.pathExists(tasksDir)) {
|
|
703
|
-
const glob = require(
|
|
704
|
-
const taskFiles = glob.sync(
|
|
705
|
-
allTaskIds.push(...taskFiles.map((file) => path.basename(file,
|
|
847
|
+
const glob = require("glob");
|
|
848
|
+
const taskFiles = glob.sync("*.md", { cwd: tasksDir });
|
|
849
|
+
allTaskIds.push(...taskFiles.map((file) => path.basename(file, ".md")));
|
|
706
850
|
}
|
|
707
851
|
|
|
708
852
|
// Check common tasks
|
|
709
|
-
const commonTasksDir = path.join(installDir,
|
|
853
|
+
const commonTasksDir = path.join(installDir, "common", "tasks");
|
|
710
854
|
if (await fileManager.pathExists(commonTasksDir)) {
|
|
711
|
-
const commonTaskFiles = glob.sync(
|
|
712
|
-
allTaskIds.push(
|
|
855
|
+
const commonTaskFiles = glob.sync("*.md", { cwd: commonTasksDir });
|
|
856
|
+
allTaskIds.push(
|
|
857
|
+
...commonTaskFiles.map((file) => path.basename(file, ".md")),
|
|
858
|
+
);
|
|
713
859
|
}
|
|
714
860
|
|
|
715
861
|
return [...new Set(allTaskIds)];
|
|
@@ -718,13 +864,13 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
718
864
|
async getAgentTitle(agentId, installDir) {
|
|
719
865
|
// Try to find the agent file in various locations
|
|
720
866
|
const possiblePaths = [
|
|
721
|
-
path.join(installDir,
|
|
722
|
-
path.join(installDir,
|
|
867
|
+
path.join(installDir, ".xiaoma-core", "agents", `${agentId}.md`),
|
|
868
|
+
path.join(installDir, "agents", `${agentId}.md`),
|
|
723
869
|
];
|
|
724
870
|
|
|
725
871
|
// Also check expansion pack directories
|
|
726
|
-
const glob = require(
|
|
727
|
-
const expansionDirectories = glob.sync(
|
|
872
|
+
const glob = require("glob");
|
|
873
|
+
const expansionDirectories = glob.sync(".*/agents", { cwd: installDir });
|
|
728
874
|
for (const expDir of expansionDirectories) {
|
|
729
875
|
possiblePaths.push(path.join(installDir, expDir, `${agentId}.md`));
|
|
730
876
|
}
|
|
@@ -743,56 +889,66 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
743
889
|
}
|
|
744
890
|
}
|
|
745
891
|
} catch (error) {
|
|
746
|
-
console.warn(
|
|
892
|
+
console.warn(
|
|
893
|
+
`Failed to read agent title for ${agentId}: ${error.message}`,
|
|
894
|
+
);
|
|
747
895
|
}
|
|
748
896
|
}
|
|
749
897
|
}
|
|
750
898
|
|
|
751
899
|
// Fallback to formatted agent ID
|
|
752
900
|
return agentId
|
|
753
|
-
.split(
|
|
901
|
+
.split("-")
|
|
754
902
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
755
|
-
.join(
|
|
903
|
+
.join(" ");
|
|
756
904
|
}
|
|
757
905
|
|
|
758
906
|
async getAllTaskIds(installDir) {
|
|
759
|
-
const glob = require(
|
|
907
|
+
const glob = require("glob");
|
|
760
908
|
const allTaskIds = [];
|
|
761
909
|
|
|
762
910
|
// Check core tasks in .xiaoma-core or root
|
|
763
|
-
let tasksDir = path.join(installDir,
|
|
911
|
+
let tasksDir = path.join(installDir, ".xiaoma-core", "tasks");
|
|
764
912
|
if (!(await fileManager.pathExists(tasksDir))) {
|
|
765
|
-
tasksDir = path.join(installDir,
|
|
913
|
+
tasksDir = path.join(installDir, "xiaoma-core", "tasks");
|
|
766
914
|
}
|
|
767
915
|
|
|
768
916
|
if (await fileManager.pathExists(tasksDir)) {
|
|
769
|
-
const taskFiles = glob.sync(
|
|
770
|
-
allTaskIds.push(...taskFiles.map((file) => path.basename(file,
|
|
917
|
+
const taskFiles = glob.sync("*.md", { cwd: tasksDir });
|
|
918
|
+
allTaskIds.push(...taskFiles.map((file) => path.basename(file, ".md")));
|
|
771
919
|
}
|
|
772
920
|
|
|
773
921
|
// Check common tasks
|
|
774
|
-
const commonTasksDir = path.join(installDir,
|
|
922
|
+
const commonTasksDir = path.join(installDir, "common", "tasks");
|
|
775
923
|
if (await fileManager.pathExists(commonTasksDir)) {
|
|
776
|
-
const commonTaskFiles = glob.sync(
|
|
777
|
-
allTaskIds.push(
|
|
924
|
+
const commonTaskFiles = glob.sync("*.md", { cwd: commonTasksDir });
|
|
925
|
+
allTaskIds.push(
|
|
926
|
+
...commonTaskFiles.map((file) => path.basename(file, ".md")),
|
|
927
|
+
);
|
|
778
928
|
}
|
|
779
929
|
|
|
780
930
|
// Also check for expansion pack tasks in dot folders
|
|
781
|
-
const expansionDirectories = glob.sync(
|
|
931
|
+
const expansionDirectories = glob.sync(".*/tasks", { cwd: installDir });
|
|
782
932
|
for (const expDir of expansionDirectories) {
|
|
783
933
|
const fullExpDir = path.join(installDir, expDir);
|
|
784
|
-
const expTaskFiles = glob.sync(
|
|
785
|
-
allTaskIds.push(
|
|
934
|
+
const expTaskFiles = glob.sync("*.md", { cwd: fullExpDir });
|
|
935
|
+
allTaskIds.push(
|
|
936
|
+
...expTaskFiles.map((file) => path.basename(file, ".md")),
|
|
937
|
+
);
|
|
786
938
|
}
|
|
787
939
|
|
|
788
940
|
// Check expansion-packs folder tasks
|
|
789
|
-
const expansionPacksDir = path.join(installDir,
|
|
941
|
+
const expansionPacksDir = path.join(installDir, "expansion-packs");
|
|
790
942
|
if (await fileManager.pathExists(expansionPacksDir)) {
|
|
791
|
-
const expPackDirectories = glob.sync(
|
|
943
|
+
const expPackDirectories = glob.sync("*/tasks", {
|
|
944
|
+
cwd: expansionPacksDir,
|
|
945
|
+
});
|
|
792
946
|
for (const expDir of expPackDirectories) {
|
|
793
947
|
const fullExpDir = path.join(expansionPacksDir, expDir);
|
|
794
|
-
const expTaskFiles = glob.sync(
|
|
795
|
-
allTaskIds.push(
|
|
948
|
+
const expTaskFiles = glob.sync("*.md", { cwd: fullExpDir });
|
|
949
|
+
allTaskIds.push(
|
|
950
|
+
...expTaskFiles.map((file) => path.basename(file, ".md")),
|
|
951
|
+
);
|
|
796
952
|
}
|
|
797
953
|
}
|
|
798
954
|
|
|
@@ -803,26 +959,30 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
803
959
|
async findTaskPath(taskId, installDir) {
|
|
804
960
|
// Try to find the task file in various locations
|
|
805
961
|
const possiblePaths = [
|
|
806
|
-
path.join(installDir,
|
|
807
|
-
path.join(installDir,
|
|
808
|
-
path.join(installDir,
|
|
962
|
+
path.join(installDir, ".xiaoma-core", "tasks", `${taskId}.md`),
|
|
963
|
+
path.join(installDir, "xiaoma-core", "tasks", `${taskId}.md`),
|
|
964
|
+
path.join(installDir, "common", "tasks", `${taskId}.md`),
|
|
809
965
|
];
|
|
810
966
|
|
|
811
967
|
// Also check expansion pack directories
|
|
812
|
-
const glob = require(
|
|
968
|
+
const glob = require("glob");
|
|
813
969
|
|
|
814
970
|
// Check dot folder expansion packs
|
|
815
|
-
const expansionDirectories = glob.sync(
|
|
971
|
+
const expansionDirectories = glob.sync(".*/tasks", { cwd: installDir });
|
|
816
972
|
for (const expDir of expansionDirectories) {
|
|
817
973
|
possiblePaths.push(path.join(installDir, expDir, `${taskId}.md`));
|
|
818
974
|
}
|
|
819
975
|
|
|
820
976
|
// Check expansion-packs folder
|
|
821
|
-
const expansionPacksDir = path.join(installDir,
|
|
977
|
+
const expansionPacksDir = path.join(installDir, "expansion-packs");
|
|
822
978
|
if (await fileManager.pathExists(expansionPacksDir)) {
|
|
823
|
-
const expPackDirectories = glob.sync(
|
|
979
|
+
const expPackDirectories = glob.sync("*/tasks", {
|
|
980
|
+
cwd: expansionPacksDir,
|
|
981
|
+
});
|
|
824
982
|
for (const expDir of expPackDirectories) {
|
|
825
|
-
possiblePaths.push(
|
|
983
|
+
possiblePaths.push(
|
|
984
|
+
path.join(expansionPacksDir, expDir, `${taskId}.md`),
|
|
985
|
+
);
|
|
826
986
|
}
|
|
827
987
|
}
|
|
828
988
|
|
|
@@ -837,24 +997,34 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
837
997
|
|
|
838
998
|
async getCoreSlashPrefix(installDir) {
|
|
839
999
|
try {
|
|
840
|
-
const coreConfigPath = path.join(
|
|
1000
|
+
const coreConfigPath = path.join(
|
|
1001
|
+
installDir,
|
|
1002
|
+
".xiaoma-core",
|
|
1003
|
+
"core-config.yaml",
|
|
1004
|
+
);
|
|
841
1005
|
if (!(await fileManager.pathExists(coreConfigPath))) {
|
|
842
1006
|
// Try xiaoma-core directory
|
|
843
|
-
const altConfigPath = path.join(
|
|
1007
|
+
const altConfigPath = path.join(
|
|
1008
|
+
installDir,
|
|
1009
|
+
"xiaoma-core",
|
|
1010
|
+
"core-config.yaml",
|
|
1011
|
+
);
|
|
844
1012
|
if (await fileManager.pathExists(altConfigPath)) {
|
|
845
1013
|
const configContent = await fileManager.readFile(altConfigPath);
|
|
846
1014
|
const config = yaml.load(configContent);
|
|
847
|
-
return config.slashPrefix ||
|
|
1015
|
+
return config.slashPrefix || "XiaoMa";
|
|
848
1016
|
}
|
|
849
|
-
return
|
|
1017
|
+
return "XiaoMa"; // fallback
|
|
850
1018
|
}
|
|
851
1019
|
|
|
852
1020
|
const configContent = await fileManager.readFile(coreConfigPath);
|
|
853
1021
|
const config = yaml.load(configContent);
|
|
854
|
-
return config.slashPrefix ||
|
|
1022
|
+
return config.slashPrefix || "XiaoMa";
|
|
855
1023
|
} catch (error) {
|
|
856
|
-
console.warn(
|
|
857
|
-
|
|
1024
|
+
console.warn(
|
|
1025
|
+
`Failed to read core slashPrefix, using default 'XiaoMa': ${error.message}`,
|
|
1026
|
+
);
|
|
1027
|
+
return "XiaoMa";
|
|
858
1028
|
}
|
|
859
1029
|
}
|
|
860
1030
|
|
|
@@ -862,11 +1032,11 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
862
1032
|
const expansionPacks = [];
|
|
863
1033
|
|
|
864
1034
|
// Check for dot-prefixed expansion packs in install directory
|
|
865
|
-
const glob = require(
|
|
866
|
-
const dotExpansions = glob.sync(
|
|
1035
|
+
const glob = require("glob");
|
|
1036
|
+
const dotExpansions = glob.sync(".bmad-*", { cwd: installDir });
|
|
867
1037
|
|
|
868
1038
|
for (const dotExpansion of dotExpansions) {
|
|
869
|
-
if (dotExpansion !==
|
|
1039
|
+
if (dotExpansion !== ".xiaoma-core") {
|
|
870
1040
|
const packPath = path.join(installDir, dotExpansion);
|
|
871
1041
|
const packName = dotExpansion.slice(1); // remove the dot
|
|
872
1042
|
expansionPacks.push({
|
|
@@ -877,15 +1047,15 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
877
1047
|
}
|
|
878
1048
|
|
|
879
1049
|
// Check for expansion-packs directory style
|
|
880
|
-
const expansionPacksDir = path.join(installDir,
|
|
1050
|
+
const expansionPacksDir = path.join(installDir, "expansion-packs");
|
|
881
1051
|
if (await fileManager.pathExists(expansionPacksDir)) {
|
|
882
|
-
const packDirectories = glob.sync(
|
|
1052
|
+
const packDirectories = glob.sync("*", { cwd: expansionPacksDir });
|
|
883
1053
|
|
|
884
1054
|
for (const packDir of packDirectories) {
|
|
885
1055
|
const packPath = path.join(expansionPacksDir, packDir);
|
|
886
1056
|
if (
|
|
887
1057
|
(await fileManager.pathExists(packPath)) &&
|
|
888
|
-
(await fileManager.pathExists(path.join(packPath,
|
|
1058
|
+
(await fileManager.pathExists(path.join(packPath, "config.yaml")))
|
|
889
1059
|
) {
|
|
890
1060
|
expansionPacks.push({
|
|
891
1061
|
name: packDir,
|
|
@@ -900,58 +1070,66 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
900
1070
|
|
|
901
1071
|
async getExpansionPackSlashPrefix(packPath) {
|
|
902
1072
|
try {
|
|
903
|
-
const configPath = path.join(packPath,
|
|
1073
|
+
const configPath = path.join(packPath, "config.yaml");
|
|
904
1074
|
if (await fileManager.pathExists(configPath)) {
|
|
905
1075
|
const configContent = await fileManager.readFile(configPath);
|
|
906
1076
|
const config = yaml.load(configContent);
|
|
907
1077
|
return config.slashPrefix || path.basename(packPath);
|
|
908
1078
|
}
|
|
909
1079
|
} catch (error) {
|
|
910
|
-
console.warn(
|
|
1080
|
+
console.warn(
|
|
1081
|
+
`Failed to read expansion pack slashPrefix from ${packPath}: ${error.message}`,
|
|
1082
|
+
);
|
|
911
1083
|
}
|
|
912
1084
|
|
|
913
1085
|
return path.basename(packPath); // fallback to directory name
|
|
914
1086
|
}
|
|
915
1087
|
|
|
916
1088
|
async getExpansionPackAgents(packPath) {
|
|
917
|
-
const agentsDir = path.join(packPath,
|
|
1089
|
+
const agentsDir = path.join(packPath, "agents");
|
|
918
1090
|
if (!(await fileManager.pathExists(agentsDir))) {
|
|
919
1091
|
return [];
|
|
920
1092
|
}
|
|
921
1093
|
|
|
922
1094
|
try {
|
|
923
|
-
const glob = require(
|
|
924
|
-
const agentFiles = glob.sync(
|
|
925
|
-
return agentFiles.map((file) => path.basename(file,
|
|
1095
|
+
const glob = require("glob");
|
|
1096
|
+
const agentFiles = glob.sync("*.md", { cwd: agentsDir });
|
|
1097
|
+
return agentFiles.map((file) => path.basename(file, ".md"));
|
|
926
1098
|
} catch (error) {
|
|
927
|
-
console.warn(
|
|
1099
|
+
console.warn(
|
|
1100
|
+
`Failed to read expansion pack agents from ${packPath}: ${error.message}`,
|
|
1101
|
+
);
|
|
928
1102
|
return [];
|
|
929
1103
|
}
|
|
930
1104
|
}
|
|
931
1105
|
|
|
932
1106
|
async getExpansionPackTasks(packPath) {
|
|
933
|
-
const tasksDir = path.join(packPath,
|
|
1107
|
+
const tasksDir = path.join(packPath, "tasks");
|
|
934
1108
|
if (!(await fileManager.pathExists(tasksDir))) {
|
|
935
1109
|
return [];
|
|
936
1110
|
}
|
|
937
1111
|
|
|
938
1112
|
try {
|
|
939
|
-
const glob = require(
|
|
940
|
-
const taskFiles = glob.sync(
|
|
941
|
-
return taskFiles.map((file) => path.basename(file,
|
|
1113
|
+
const glob = require("glob");
|
|
1114
|
+
const taskFiles = glob.sync("*.md", { cwd: tasksDir });
|
|
1115
|
+
return taskFiles.map((file) => path.basename(file, ".md"));
|
|
942
1116
|
} catch (error) {
|
|
943
|
-
console.warn(
|
|
1117
|
+
console.warn(
|
|
1118
|
+
`Failed to read expansion pack tasks from ${packPath}: ${error.message}`,
|
|
1119
|
+
);
|
|
944
1120
|
return [];
|
|
945
1121
|
}
|
|
946
1122
|
}
|
|
947
1123
|
|
|
948
1124
|
async setupRoo(installDir, selectedAgent) {
|
|
949
|
-
const agents = selectedAgent
|
|
1125
|
+
const agents = selectedAgent
|
|
1126
|
+
? [selectedAgent]
|
|
1127
|
+
: await this.getAllAgentIds(installDir);
|
|
950
1128
|
|
|
951
1129
|
// Check for existing .roomodes file in project root
|
|
952
|
-
const roomodesPath = path.join(installDir,
|
|
1130
|
+
const roomodesPath = path.join(installDir, ".roomodes");
|
|
953
1131
|
let existingModes = [];
|
|
954
|
-
let existingContent =
|
|
1132
|
+
let existingContent = "";
|
|
955
1133
|
|
|
956
1134
|
if (await fileManager.pathExists(roomodesPath)) {
|
|
957
1135
|
existingContent = await fileManager.readFile(roomodesPath);
|
|
@@ -960,22 +1138,30 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
960
1138
|
for (const match of modeMatches) {
|
|
961
1139
|
existingModes.push(match[1]);
|
|
962
1140
|
}
|
|
963
|
-
console.log(
|
|
1141
|
+
console.log(
|
|
1142
|
+
chalk.yellow(
|
|
1143
|
+
`Found existing .roomodes file with ${existingModes.length} modes`,
|
|
1144
|
+
),
|
|
1145
|
+
);
|
|
964
1146
|
}
|
|
965
1147
|
|
|
966
1148
|
// Create new modes content
|
|
967
|
-
let newModesContent =
|
|
1149
|
+
let newModesContent = "";
|
|
968
1150
|
|
|
969
1151
|
// Load dynamic agent permissions from configuration
|
|
970
1152
|
const config = await this.loadIdeAgentConfig();
|
|
971
|
-
const agentPermissions = config[
|
|
1153
|
+
const agentPermissions = config["roo-permissions"] || {};
|
|
972
1154
|
|
|
973
1155
|
for (const agentId of agents) {
|
|
974
1156
|
// Skip if already exists
|
|
975
1157
|
// Check both with and without bmad- prefix to handle both cases
|
|
976
|
-
const checkSlug = agentId.startsWith(
|
|
1158
|
+
const checkSlug = agentId.startsWith("bmad-")
|
|
1159
|
+
? agentId
|
|
1160
|
+
: `bmad-${agentId}`;
|
|
977
1161
|
if (existingModes.includes(checkSlug)) {
|
|
978
|
-
console.log(
|
|
1162
|
+
console.log(
|
|
1163
|
+
chalk.dim(`Skipping ${agentId} - already exists in .roomodes`),
|
|
1164
|
+
);
|
|
979
1165
|
continue;
|
|
980
1166
|
}
|
|
981
1167
|
|
|
@@ -999,8 +1185,10 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
999
1185
|
const title = titleMatch
|
|
1000
1186
|
? titleMatch[1].trim()
|
|
1001
1187
|
: await this.getAgentTitle(agentId, installDir);
|
|
1002
|
-
const icon = iconMatch ? iconMatch[1].trim() :
|
|
1003
|
-
const whenToUse = whenToUseMatch
|
|
1188
|
+
const icon = iconMatch ? iconMatch[1].trim() : "🤖";
|
|
1189
|
+
const whenToUse = whenToUseMatch
|
|
1190
|
+
? whenToUseMatch[1].trim()
|
|
1191
|
+
: `Use for ${title} tasks`;
|
|
1004
1192
|
const roleDefinition = roleDefinitionMatch
|
|
1005
1193
|
? roleDefinitionMatch[1].trim()
|
|
1006
1194
|
: `You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
|
|
@@ -1009,7 +1197,9 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1009
1197
|
const permissions = agentPermissions[agentId];
|
|
1010
1198
|
// Build mode entry with proper formatting (matching exact indentation)
|
|
1011
1199
|
// Avoid double "bmad-" prefix for agents that already have it
|
|
1012
|
-
const slug = agentId.startsWith(
|
|
1200
|
+
const slug = agentId.startsWith("bmad-")
|
|
1201
|
+
? agentId
|
|
1202
|
+
: `bmad-${agentId}`;
|
|
1013
1203
|
newModesContent += ` - slug: ${slug}\n`;
|
|
1014
1204
|
newModesContent += ` name: '${icon} ${title}'\n`;
|
|
1015
1205
|
if (permissions) {
|
|
@@ -1018,7 +1208,9 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1018
1208
|
newModesContent += ` roleDefinition: ${roleDefinition}\n`;
|
|
1019
1209
|
newModesContent += ` whenToUse: ${whenToUse}\n`;
|
|
1020
1210
|
// Get relative path from installDir to agent file
|
|
1021
|
-
const relativePath = path
|
|
1211
|
+
const relativePath = path
|
|
1212
|
+
.relative(installDir, agentPath)
|
|
1213
|
+
.replaceAll("\\", "/");
|
|
1022
1214
|
newModesContent += ` customInstructions: CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode\n`;
|
|
1023
1215
|
newModesContent += ` groups:\n`;
|
|
1024
1216
|
newModesContent += ` - read\n`;
|
|
@@ -1031,56 +1223,68 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1031
1223
|
newModesContent += ` - edit\n`;
|
|
1032
1224
|
}
|
|
1033
1225
|
|
|
1034
|
-
console.log(
|
|
1226
|
+
console.log(
|
|
1227
|
+
chalk.green(`✓ Added mode: bmad-${agentId} (${icon} ${title})`),
|
|
1228
|
+
);
|
|
1035
1229
|
}
|
|
1036
1230
|
}
|
|
1037
1231
|
}
|
|
1038
1232
|
|
|
1039
1233
|
// Build final roomodes content
|
|
1040
|
-
let roomodesContent =
|
|
1234
|
+
let roomodesContent = "";
|
|
1041
1235
|
if (existingContent) {
|
|
1042
1236
|
// If there's existing content, append new modes to it
|
|
1043
|
-
roomodesContent = existingContent.trim() +
|
|
1237
|
+
roomodesContent = existingContent.trim() + "\n" + newModesContent;
|
|
1044
1238
|
} else {
|
|
1045
1239
|
// Create new .roomodes file with proper YAML structure
|
|
1046
|
-
roomodesContent =
|
|
1240
|
+
roomodesContent = "customModes:\n" + newModesContent;
|
|
1047
1241
|
}
|
|
1048
1242
|
|
|
1049
1243
|
// Write .roomodes file
|
|
1050
1244
|
await fileManager.writeFile(roomodesPath, roomodesContent);
|
|
1051
|
-
console.log(chalk.green(
|
|
1245
|
+
console.log(chalk.green("✓ Created .roomodes file in project root"));
|
|
1052
1246
|
|
|
1053
1247
|
console.log(chalk.green(`\n✓ Roo Code setup complete!`));
|
|
1054
|
-
console.log(
|
|
1248
|
+
console.log(
|
|
1249
|
+
chalk.dim(
|
|
1250
|
+
"Custom modes will be available when you open this project in Roo Code",
|
|
1251
|
+
),
|
|
1252
|
+
);
|
|
1055
1253
|
|
|
1056
1254
|
return true;
|
|
1057
1255
|
}
|
|
1058
1256
|
|
|
1059
1257
|
async setupKilocode(installDir, selectedAgent) {
|
|
1060
|
-
const filePath = path.join(installDir,
|
|
1061
|
-
const agents = selectedAgent
|
|
1258
|
+
const filePath = path.join(installDir, ".kilocodemodes");
|
|
1259
|
+
const agents = selectedAgent
|
|
1260
|
+
? [selectedAgent]
|
|
1261
|
+
: await this.getAllAgentIds(installDir);
|
|
1062
1262
|
|
|
1063
1263
|
let existingModes = [],
|
|
1064
|
-
existingContent =
|
|
1264
|
+
existingContent = "";
|
|
1065
1265
|
if (await fileManager.pathExists(filePath)) {
|
|
1066
1266
|
existingContent = await fileManager.readFile(filePath);
|
|
1067
1267
|
for (const match of existingContent.matchAll(/- slug: ([\w-]+)/g)) {
|
|
1068
1268
|
existingModes.push(match[1]);
|
|
1069
1269
|
}
|
|
1070
1270
|
console.log(
|
|
1071
|
-
chalk.yellow(
|
|
1271
|
+
chalk.yellow(
|
|
1272
|
+
`Found existing .kilocodemodes file with ${existingModes.length} modes`,
|
|
1273
|
+
),
|
|
1072
1274
|
);
|
|
1073
1275
|
}
|
|
1074
1276
|
|
|
1075
1277
|
const config = await this.loadIdeAgentConfig();
|
|
1076
|
-
const permissions = config[
|
|
1278
|
+
const permissions = config["roo-permissions"] || {}; // reuse same roo permissions block (Kilo Code understands same mode schema)
|
|
1077
1279
|
|
|
1078
|
-
let newContent =
|
|
1280
|
+
let newContent = "";
|
|
1079
1281
|
|
|
1080
1282
|
for (const agentId of agents) {
|
|
1081
|
-
const slug = agentId.startsWith(
|
|
1283
|
+
const slug = agentId.startsWith("bmad-") ? agentId : `bmad-${agentId}`;
|
|
1082
1284
|
if (existingModes.includes(slug)) {
|
|
1083
|
-
console.log(
|
|
1285
|
+
console.log(
|
|
1286
|
+
chalk.dim(`Skipping ${agentId} - already exists in .kilocodemodes`),
|
|
1287
|
+
);
|
|
1084
1288
|
continue;
|
|
1085
1289
|
}
|
|
1086
1290
|
|
|
@@ -1101,14 +1305,19 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1101
1305
|
|
|
1102
1306
|
// Robust fallback for title and icon
|
|
1103
1307
|
const title =
|
|
1104
|
-
yaml.match(/title:\s*(.+)/)?.[1]?.trim() ||
|
|
1105
|
-
|
|
1106
|
-
const
|
|
1308
|
+
yaml.match(/title:\s*(.+)/)?.[1]?.trim() ||
|
|
1309
|
+
(await this.getAgentTitle(agentId, installDir));
|
|
1310
|
+
const icon = yaml.match(/icon:\s*(.+)/)?.[1]?.trim() || "🤖";
|
|
1311
|
+
const whenToUse =
|
|
1312
|
+
yaml.match(/whenToUse:\s*"(.+)"/)?.[1]?.trim() ||
|
|
1313
|
+
`Use for ${title} tasks`;
|
|
1107
1314
|
const roleDefinition =
|
|
1108
1315
|
yaml.match(/roleDefinition:\s*"(.+)"/)?.[1]?.trim() ||
|
|
1109
1316
|
`You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
|
|
1110
1317
|
|
|
1111
|
-
const relativePath = path
|
|
1318
|
+
const relativePath = path
|
|
1319
|
+
.relative(installDir, agentPath)
|
|
1320
|
+
.replaceAll("\\", "/");
|
|
1112
1321
|
const customInstructions = `CRITICAL Read the full YAML from ${relativePath} start activation to alter your state of being follow startup section instructions stay in this being until told to exit this mode`;
|
|
1113
1322
|
|
|
1114
1323
|
// Add permissions from config if they exist
|
|
@@ -1140,26 +1349,32 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1140
1349
|
}
|
|
1141
1350
|
|
|
1142
1351
|
const finalContent = existingContent
|
|
1143
|
-
? existingContent.trim() +
|
|
1144
|
-
:
|
|
1352
|
+
? existingContent.trim() + "\n" + newContent
|
|
1353
|
+
: "customModes:\n" + newContent;
|
|
1145
1354
|
|
|
1146
1355
|
await fileManager.writeFile(filePath, finalContent);
|
|
1147
|
-
console.log(chalk.green(
|
|
1356
|
+
console.log(chalk.green("✓ Created .kilocodemodes file in project root"));
|
|
1148
1357
|
console.log(chalk.green(`✓ KiloCode setup complete!`));
|
|
1149
|
-
console.log(
|
|
1358
|
+
console.log(
|
|
1359
|
+
chalk.dim(
|
|
1360
|
+
"Custom modes will be available when you open this project in KiloCode",
|
|
1361
|
+
),
|
|
1362
|
+
);
|
|
1150
1363
|
|
|
1151
1364
|
return true;
|
|
1152
1365
|
}
|
|
1153
1366
|
|
|
1154
1367
|
async setupCline(installDir, selectedAgent) {
|
|
1155
|
-
const clineRulesDir = path.join(installDir,
|
|
1156
|
-
const agents = selectedAgent
|
|
1368
|
+
const clineRulesDir = path.join(installDir, ".clinerules");
|
|
1369
|
+
const agents = selectedAgent
|
|
1370
|
+
? [selectedAgent]
|
|
1371
|
+
: await this.getAllAgentIds(installDir);
|
|
1157
1372
|
|
|
1158
1373
|
await fileManager.ensureDirectory(clineRulesDir);
|
|
1159
1374
|
|
|
1160
1375
|
// Load dynamic agent ordering from configuration
|
|
1161
1376
|
const config = await this.loadIdeAgentConfig();
|
|
1162
|
-
const agentOrder = config[
|
|
1377
|
+
const agentOrder = config["cline-order"] || {};
|
|
1163
1378
|
|
|
1164
1379
|
for (const agentId of agents) {
|
|
1165
1380
|
// Find the agent file
|
|
@@ -1170,34 +1385,36 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1170
1385
|
|
|
1171
1386
|
// Get numeric prefix for ordering
|
|
1172
1387
|
const order = agentOrder[agentId] || 99;
|
|
1173
|
-
const prefix = order.toString().padStart(2,
|
|
1388
|
+
const prefix = order.toString().padStart(2, "0");
|
|
1174
1389
|
const mdPath = path.join(clineRulesDir, `${prefix}-${agentId}.md`);
|
|
1175
1390
|
|
|
1176
1391
|
// Create MD content for Cline (focused on project standards and role)
|
|
1177
1392
|
let mdContent = `# ${await this.getAgentTitle(agentId, installDir)} Agent\n\n`;
|
|
1178
1393
|
mdContent += `This rule defines the ${await this.getAgentTitle(agentId, installDir)} persona and project standards.\n\n`;
|
|
1179
|
-
mdContent +=
|
|
1394
|
+
mdContent += "## Role Definition\n\n";
|
|
1180
1395
|
mdContent +=
|
|
1181
|
-
|
|
1396
|
+
"When the user types `@" +
|
|
1182
1397
|
agentId +
|
|
1183
|
-
|
|
1184
|
-
mdContent +=
|
|
1398
|
+
"`, adopt this persona and follow these guidelines:\n\n";
|
|
1399
|
+
mdContent += "```yaml\n";
|
|
1185
1400
|
// Extract just the YAML content from the agent file
|
|
1186
1401
|
const yamlContent = extractYamlFromAgent(agentContent);
|
|
1187
1402
|
if (yamlContent) {
|
|
1188
1403
|
mdContent += yamlContent;
|
|
1189
1404
|
} else {
|
|
1190
1405
|
// If no YAML found, include the whole content minus the header
|
|
1191
|
-
mdContent += agentContent.replace(/^#.*$/m,
|
|
1406
|
+
mdContent += agentContent.replace(/^#.*$/m, "").trim();
|
|
1192
1407
|
}
|
|
1193
|
-
mdContent +=
|
|
1194
|
-
mdContent +=
|
|
1408
|
+
mdContent += "\n```\n\n";
|
|
1409
|
+
mdContent += "## Project Standards\n\n";
|
|
1195
1410
|
mdContent += `- Always maintain consistency with project documentation in .xiaoma-core/\n`;
|
|
1196
1411
|
mdContent += `- Follow the agent's specific guidelines and constraints\n`;
|
|
1197
1412
|
mdContent += `- Update relevant project files when making changes\n`;
|
|
1198
|
-
const relativePath = path
|
|
1413
|
+
const relativePath = path
|
|
1414
|
+
.relative(installDir, agentPath)
|
|
1415
|
+
.replaceAll("\\", "/");
|
|
1199
1416
|
mdContent += `- Reference the complete agent definition in [${relativePath}](${relativePath})\n\n`;
|
|
1200
|
-
mdContent +=
|
|
1417
|
+
mdContent += "## Usage\n\n";
|
|
1201
1418
|
mdContent += `Type \`@${agentId}\` to activate this ${await this.getAgentTitle(agentId, installDir)} persona.\n`;
|
|
1202
1419
|
|
|
1203
1420
|
await fileManager.writeFile(mdPath, mdContent);
|
|
@@ -1211,12 +1428,12 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1211
1428
|
}
|
|
1212
1429
|
|
|
1213
1430
|
async setupGeminiCli(installDir) {
|
|
1214
|
-
const geminiDir = path.join(installDir,
|
|
1215
|
-
const bmadMethodDir = path.join(geminiDir,
|
|
1431
|
+
const geminiDir = path.join(installDir, ".gemini");
|
|
1432
|
+
const bmadMethodDir = path.join(geminiDir, "xiaoma-cli");
|
|
1216
1433
|
await fileManager.ensureDirectory(bmadMethodDir);
|
|
1217
1434
|
|
|
1218
1435
|
// Update logic for existing settings.json
|
|
1219
|
-
const settingsPath = path.join(geminiDir,
|
|
1436
|
+
const settingsPath = path.join(geminiDir, "settings.json");
|
|
1220
1437
|
if (await fileManager.pathExists(settingsPath)) {
|
|
1221
1438
|
try {
|
|
1222
1439
|
const settingsContent = await fileManager.readFile(settingsPath);
|
|
@@ -1224,10 +1441,13 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1224
1441
|
let updated = false;
|
|
1225
1442
|
|
|
1226
1443
|
// Handle contextFileName property
|
|
1227
|
-
if (
|
|
1444
|
+
if (
|
|
1445
|
+
settings.contextFileName &&
|
|
1446
|
+
Array.isArray(settings.contextFileName)
|
|
1447
|
+
) {
|
|
1228
1448
|
const originalLength = settings.contextFileName.length;
|
|
1229
1449
|
settings.contextFileName = settings.contextFileName.filter(
|
|
1230
|
-
(fileName) => !fileName.startsWith(
|
|
1450
|
+
(fileName) => !fileName.startsWith("agents/"),
|
|
1231
1451
|
);
|
|
1232
1452
|
if (settings.contextFileName.length !== originalLength) {
|
|
1233
1453
|
updated = true;
|
|
@@ -1235,26 +1455,34 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1235
1455
|
}
|
|
1236
1456
|
|
|
1237
1457
|
if (updated) {
|
|
1238
|
-
await fileManager.writeFile(
|
|
1458
|
+
await fileManager.writeFile(
|
|
1459
|
+
settingsPath,
|
|
1460
|
+
JSON.stringify(settings, null, 2),
|
|
1461
|
+
);
|
|
1239
1462
|
console.log(
|
|
1240
|
-
chalk.green(
|
|
1463
|
+
chalk.green(
|
|
1464
|
+
"✓ Updated .gemini/settings.json - removed agent file references",
|
|
1465
|
+
),
|
|
1241
1466
|
);
|
|
1242
1467
|
}
|
|
1243
1468
|
} catch (error) {
|
|
1244
|
-
console.warn(
|
|
1469
|
+
console.warn(
|
|
1470
|
+
chalk.yellow("Could not update .gemini/settings.json"),
|
|
1471
|
+
error,
|
|
1472
|
+
);
|
|
1245
1473
|
}
|
|
1246
1474
|
}
|
|
1247
1475
|
|
|
1248
1476
|
// Remove old agents directory
|
|
1249
|
-
const agentsDir = path.join(geminiDir,
|
|
1477
|
+
const agentsDir = path.join(geminiDir, "agents");
|
|
1250
1478
|
if (await fileManager.pathExists(agentsDir)) {
|
|
1251
1479
|
await fileManager.removeDirectory(agentsDir);
|
|
1252
|
-
console.log(chalk.green(
|
|
1480
|
+
console.log(chalk.green("✓ Removed old .gemini/agents directory"));
|
|
1253
1481
|
}
|
|
1254
1482
|
|
|
1255
1483
|
// Get all available agents
|
|
1256
1484
|
const agents = await this.getAllAgentIds(installDir);
|
|
1257
|
-
let concatenatedContent =
|
|
1485
|
+
let concatenatedContent = "";
|
|
1258
1486
|
|
|
1259
1487
|
for (const agentId of agents) {
|
|
1260
1488
|
// Find the source agent file
|
|
@@ -1269,36 +1497,38 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1269
1497
|
agentId,
|
|
1270
1498
|
installDir,
|
|
1271
1499
|
)} agent persona.\n\n`;
|
|
1272
|
-
agentRuleContent +=
|
|
1500
|
+
agentRuleContent += "## Agent Activation\n\n";
|
|
1273
1501
|
agentRuleContent +=
|
|
1274
|
-
|
|
1275
|
-
agentRuleContent +=
|
|
1502
|
+
"CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
|
|
1503
|
+
agentRuleContent += "```yaml\n";
|
|
1276
1504
|
// Extract just the YAML content from the agent file
|
|
1277
1505
|
const yamlContent = extractYamlFromAgent(agentContent);
|
|
1278
1506
|
if (yamlContent) {
|
|
1279
1507
|
agentRuleContent += yamlContent;
|
|
1280
1508
|
} else {
|
|
1281
1509
|
// If no YAML found, include the whole content minus the header
|
|
1282
|
-
agentRuleContent += agentContent.replace(/^#.*$/m,
|
|
1510
|
+
agentRuleContent += agentContent.replace(/^#.*$/m, "").trim();
|
|
1283
1511
|
}
|
|
1284
|
-
agentRuleContent +=
|
|
1285
|
-
agentRuleContent +=
|
|
1286
|
-
const relativePath = path
|
|
1512
|
+
agentRuleContent += "\n```\n\n";
|
|
1513
|
+
agentRuleContent += "## File Reference\n\n";
|
|
1514
|
+
const relativePath = path
|
|
1515
|
+
.relative(installDir, agentPath)
|
|
1516
|
+
.replaceAll("\\", "/");
|
|
1287
1517
|
agentRuleContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
|
|
1288
|
-
agentRuleContent +=
|
|
1518
|
+
agentRuleContent += "## Usage\n\n";
|
|
1289
1519
|
agentRuleContent += `When the user types \`*${agentId}\`, activate this ${await this.getAgentTitle(
|
|
1290
1520
|
agentId,
|
|
1291
1521
|
installDir,
|
|
1292
1522
|
)} persona and follow all instructions defined in the YAML configuration above.\n`;
|
|
1293
1523
|
|
|
1294
1524
|
// Add to concatenated content with separator
|
|
1295
|
-
concatenatedContent += agentRuleContent +
|
|
1525
|
+
concatenatedContent += agentRuleContent + "\n\n---\n\n";
|
|
1296
1526
|
console.log(chalk.green(`✓ Added context for @${agentId}`));
|
|
1297
1527
|
}
|
|
1298
1528
|
}
|
|
1299
1529
|
|
|
1300
1530
|
// Write the concatenated content to GEMINI.md
|
|
1301
|
-
const geminiMdPath = path.join(bmadMethodDir,
|
|
1531
|
+
const geminiMdPath = path.join(bmadMethodDir, "GEMINI.md");
|
|
1302
1532
|
await fileManager.writeFile(geminiMdPath, concatenatedContent);
|
|
1303
1533
|
console.log(chalk.green(`\n✓ Created GEMINI.md in ${bmadMethodDir}`));
|
|
1304
1534
|
|
|
@@ -1306,12 +1536,12 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1306
1536
|
}
|
|
1307
1537
|
|
|
1308
1538
|
async setupQwenCode(installDir, selectedAgent) {
|
|
1309
|
-
const qwenDir = path.join(installDir,
|
|
1310
|
-
const bmadMethodDir = path.join(qwenDir,
|
|
1539
|
+
const qwenDir = path.join(installDir, ".qwen");
|
|
1540
|
+
const bmadMethodDir = path.join(qwenDir, "xiaoma-cli");
|
|
1311
1541
|
await fileManager.ensureDirectory(bmadMethodDir);
|
|
1312
1542
|
|
|
1313
1543
|
// Update logic for existing settings.json
|
|
1314
|
-
const settingsPath = path.join(qwenDir,
|
|
1544
|
+
const settingsPath = path.join(qwenDir, "settings.json");
|
|
1315
1545
|
if (await fileManager.pathExists(settingsPath)) {
|
|
1316
1546
|
try {
|
|
1317
1547
|
const settingsContent = await fileManager.readFile(settingsPath);
|
|
@@ -1319,10 +1549,13 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1319
1549
|
let updated = false;
|
|
1320
1550
|
|
|
1321
1551
|
// Handle contextFileName property
|
|
1322
|
-
if (
|
|
1552
|
+
if (
|
|
1553
|
+
settings.contextFileName &&
|
|
1554
|
+
Array.isArray(settings.contextFileName)
|
|
1555
|
+
) {
|
|
1323
1556
|
const originalLength = settings.contextFileName.length;
|
|
1324
1557
|
settings.contextFileName = settings.contextFileName.filter(
|
|
1325
|
-
(fileName) => !fileName.startsWith(
|
|
1558
|
+
(fileName) => !fileName.startsWith("agents/"),
|
|
1326
1559
|
);
|
|
1327
1560
|
if (settings.contextFileName.length !== originalLength) {
|
|
1328
1561
|
updated = true;
|
|
@@ -1330,24 +1563,36 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1330
1563
|
}
|
|
1331
1564
|
|
|
1332
1565
|
if (updated) {
|
|
1333
|
-
await fileManager.writeFile(
|
|
1334
|
-
|
|
1566
|
+
await fileManager.writeFile(
|
|
1567
|
+
settingsPath,
|
|
1568
|
+
JSON.stringify(settings, null, 2),
|
|
1569
|
+
);
|
|
1570
|
+
console.log(
|
|
1571
|
+
chalk.green(
|
|
1572
|
+
"✓ Updated .qwen/settings.json - removed agent file references",
|
|
1573
|
+
),
|
|
1574
|
+
);
|
|
1335
1575
|
}
|
|
1336
1576
|
} catch (error) {
|
|
1337
|
-
console.warn(
|
|
1577
|
+
console.warn(
|
|
1578
|
+
chalk.yellow("Could not update .qwen/settings.json"),
|
|
1579
|
+
error,
|
|
1580
|
+
);
|
|
1338
1581
|
}
|
|
1339
1582
|
}
|
|
1340
1583
|
|
|
1341
1584
|
// Remove old agents directory
|
|
1342
|
-
const agentsDir = path.join(qwenDir,
|
|
1585
|
+
const agentsDir = path.join(qwenDir, "agents");
|
|
1343
1586
|
if (await fileManager.pathExists(agentsDir)) {
|
|
1344
1587
|
await fileManager.removeDirectory(agentsDir);
|
|
1345
|
-
console.log(chalk.green(
|
|
1588
|
+
console.log(chalk.green("✓ Removed old .qwen/agents directory"));
|
|
1346
1589
|
}
|
|
1347
1590
|
|
|
1348
1591
|
// Get all available agents
|
|
1349
|
-
const agents = selectedAgent
|
|
1350
|
-
|
|
1592
|
+
const agents = selectedAgent
|
|
1593
|
+
? [selectedAgent]
|
|
1594
|
+
: await this.getAllAgentIds(installDir);
|
|
1595
|
+
let concatenatedContent = "";
|
|
1351
1596
|
|
|
1352
1597
|
for (const agentId of agents) {
|
|
1353
1598
|
// Find the source agent file
|
|
@@ -1362,36 +1607,38 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1362
1607
|
agentId,
|
|
1363
1608
|
installDir,
|
|
1364
1609
|
)} agent persona.\n\n`;
|
|
1365
|
-
agentRuleContent +=
|
|
1610
|
+
agentRuleContent += "## Agent Activation\n\n";
|
|
1366
1611
|
agentRuleContent +=
|
|
1367
|
-
|
|
1368
|
-
agentRuleContent +=
|
|
1612
|
+
"CRITICAL: Read the full YAML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n";
|
|
1613
|
+
agentRuleContent += "```yaml\n";
|
|
1369
1614
|
// Extract just the YAML content from the agent file
|
|
1370
1615
|
const yamlContent = extractYamlFromAgent(agentContent);
|
|
1371
1616
|
if (yamlContent) {
|
|
1372
1617
|
agentRuleContent += yamlContent;
|
|
1373
1618
|
} else {
|
|
1374
1619
|
// If no YAML found, include the whole content minus the header
|
|
1375
|
-
agentRuleContent += agentContent.replace(/^#.*$/m,
|
|
1620
|
+
agentRuleContent += agentContent.replace(/^#.*$/m, "").trim();
|
|
1376
1621
|
}
|
|
1377
|
-
agentRuleContent +=
|
|
1378
|
-
agentRuleContent +=
|
|
1379
|
-
const relativePath = path
|
|
1622
|
+
agentRuleContent += "\n```\n\n";
|
|
1623
|
+
agentRuleContent += "## File Reference\n\n";
|
|
1624
|
+
const relativePath = path
|
|
1625
|
+
.relative(installDir, agentPath)
|
|
1626
|
+
.replaceAll("\\", "/");
|
|
1380
1627
|
agentRuleContent += `The complete agent definition is available in [${relativePath}](${relativePath}).\n\n`;
|
|
1381
|
-
agentRuleContent +=
|
|
1628
|
+
agentRuleContent += "## Usage\n\n";
|
|
1382
1629
|
agentRuleContent += `When the user types \`*${agentId}\`, activate this ${await this.getAgentTitle(
|
|
1383
1630
|
agentId,
|
|
1384
1631
|
installDir,
|
|
1385
1632
|
)} persona and follow all instructions defined in the YAML configuration above.\n`;
|
|
1386
1633
|
|
|
1387
1634
|
// Add to concatenated content with separator
|
|
1388
|
-
concatenatedContent += agentRuleContent +
|
|
1635
|
+
concatenatedContent += agentRuleContent + "\n\n---\n\n";
|
|
1389
1636
|
console.log(chalk.green(`✓ Added context for *${agentId}`));
|
|
1390
1637
|
}
|
|
1391
1638
|
}
|
|
1392
1639
|
|
|
1393
1640
|
// Write the concatenated content to QWEN.md
|
|
1394
|
-
const qwenMdPath = path.join(bmadMethodDir,
|
|
1641
|
+
const qwenMdPath = path.join(bmadMethodDir, "QWEN.md");
|
|
1395
1642
|
await fileManager.writeFile(qwenMdPath, concatenatedContent);
|
|
1396
1643
|
console.log(chalk.green(`\n✓ Created QWEN.md in ${bmadMethodDir}`));
|
|
1397
1644
|
|
|
@@ -1405,10 +1652,16 @@ class IdeSetup extends BaseIdeSetup {
|
|
|
1405
1652
|
preConfiguredSettings = null,
|
|
1406
1653
|
) {
|
|
1407
1654
|
// Configure VS Code workspace settings first to avoid UI conflicts with loading spinners
|
|
1408
|
-
await this.configureVsCodeSettings(
|
|
1655
|
+
await this.configureVsCodeSettings(
|
|
1656
|
+
installDir,
|
|
1657
|
+
spinner,
|
|
1658
|
+
preConfiguredSettings,
|
|
1659
|
+
);
|
|
1409
1660
|
|
|
1410
|
-
const chatmodesDir = path.join(installDir,
|
|
1411
|
-
const agents = selectedAgent
|
|
1661
|
+
const chatmodesDir = path.join(installDir, ".github", "chatmodes");
|
|
1662
|
+
const agents = selectedAgent
|
|
1663
|
+
? [selectedAgent]
|
|
1664
|
+
: await this.getAllAgentIds(installDir);
|
|
1412
1665
|
|
|
1413
1666
|
await fileManager.ensureDirectory(chatmodesDir);
|
|
1414
1667
|
|
|
@@ -1446,14 +1699,22 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1446
1699
|
}
|
|
1447
1700
|
|
|
1448
1701
|
console.log(chalk.green(`\n✓ Github Copilot setup complete!`));
|
|
1449
|
-
console.log(
|
|
1702
|
+
console.log(
|
|
1703
|
+
chalk.dim(
|
|
1704
|
+
`You can now find the XiaoMa agents in the Chat view's mode selector.`,
|
|
1705
|
+
),
|
|
1706
|
+
);
|
|
1450
1707
|
|
|
1451
1708
|
return true;
|
|
1452
1709
|
}
|
|
1453
1710
|
|
|
1454
|
-
async configureVsCodeSettings(
|
|
1455
|
-
|
|
1456
|
-
|
|
1711
|
+
async configureVsCodeSettings(
|
|
1712
|
+
installDir,
|
|
1713
|
+
spinner,
|
|
1714
|
+
preConfiguredSettings = null,
|
|
1715
|
+
) {
|
|
1716
|
+
const vscodeDir = path.join(installDir, ".vscode");
|
|
1717
|
+
const settingsPath = path.join(vscodeDir, "settings.json");
|
|
1457
1718
|
|
|
1458
1719
|
await fileManager.ensureDirectory(vscodeDir);
|
|
1459
1720
|
|
|
@@ -1464,10 +1725,16 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1464
1725
|
const existingContent = await fileManager.readFile(settingsPath);
|
|
1465
1726
|
existingSettings = JSON.parse(existingContent);
|
|
1466
1727
|
console.log(
|
|
1467
|
-
chalk.yellow(
|
|
1728
|
+
chalk.yellow(
|
|
1729
|
+
"Found existing .vscode/settings.json. Merging XiaoMa settings...",
|
|
1730
|
+
),
|
|
1468
1731
|
);
|
|
1469
1732
|
} catch {
|
|
1470
|
-
console.warn(
|
|
1733
|
+
console.warn(
|
|
1734
|
+
chalk.yellow(
|
|
1735
|
+
"Could not parse existing settings.json. Creating new one.",
|
|
1736
|
+
),
|
|
1737
|
+
);
|
|
1471
1738
|
existingSettings = {};
|
|
1472
1739
|
}
|
|
1473
1740
|
}
|
|
@@ -1476,36 +1743,44 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1476
1743
|
let configChoice;
|
|
1477
1744
|
if (preConfiguredSettings && preConfiguredSettings.configChoice) {
|
|
1478
1745
|
configChoice = preConfiguredSettings.configChoice;
|
|
1479
|
-
console.log(
|
|
1746
|
+
console.log(
|
|
1747
|
+
chalk.dim(
|
|
1748
|
+
`Using pre-configured GitHub Copilot settings: ${configChoice}`,
|
|
1749
|
+
),
|
|
1750
|
+
);
|
|
1480
1751
|
} else {
|
|
1481
1752
|
// Clear any previous output and add spacing to avoid conflicts with loaders
|
|
1482
|
-
console.log(
|
|
1483
|
-
console.log(chalk.blue(
|
|
1753
|
+
console.log("\n".repeat(2));
|
|
1754
|
+
console.log(chalk.blue("🔧 Github Copilot Agent Settings Configuration"));
|
|
1484
1755
|
console.log(
|
|
1485
|
-
chalk.dim(
|
|
1756
|
+
chalk.dim(
|
|
1757
|
+
"XiaoMa works best with specific VS Code settings for optimal agent experience.",
|
|
1758
|
+
),
|
|
1486
1759
|
);
|
|
1487
|
-
console.log(
|
|
1760
|
+
console.log(""); // Add extra spacing
|
|
1488
1761
|
|
|
1489
1762
|
const response = await inquirer.prompt([
|
|
1490
1763
|
{
|
|
1491
|
-
type:
|
|
1492
|
-
name:
|
|
1493
|
-
message: chalk.yellow(
|
|
1764
|
+
type: "list",
|
|
1765
|
+
name: "configChoice",
|
|
1766
|
+
message: chalk.yellow(
|
|
1767
|
+
"How would you like to configure GitHub Copilot settings?",
|
|
1768
|
+
),
|
|
1494
1769
|
choices: [
|
|
1495
1770
|
{
|
|
1496
|
-
name:
|
|
1497
|
-
value:
|
|
1771
|
+
name: "Use recommended defaults (fastest setup)",
|
|
1772
|
+
value: "defaults",
|
|
1498
1773
|
},
|
|
1499
1774
|
{
|
|
1500
|
-
name:
|
|
1501
|
-
value:
|
|
1775
|
+
name: "Configure each setting manually (customize to your preferences)",
|
|
1776
|
+
value: "manual",
|
|
1502
1777
|
},
|
|
1503
1778
|
{
|
|
1504
1779
|
name: "Skip settings configuration (I'll configure manually later)",
|
|
1505
|
-
value:
|
|
1780
|
+
value: "skip",
|
|
1506
1781
|
},
|
|
1507
1782
|
],
|
|
1508
|
-
default:
|
|
1783
|
+
default: "defaults",
|
|
1509
1784
|
},
|
|
1510
1785
|
]);
|
|
1511
1786
|
configChoice = response.configChoice;
|
|
@@ -1513,32 +1788,42 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1513
1788
|
|
|
1514
1789
|
let bmadSettings = {};
|
|
1515
1790
|
|
|
1516
|
-
if (configChoice ===
|
|
1517
|
-
console.log(chalk.yellow(
|
|
1518
|
-
console.log(
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
console.log(chalk.dim(
|
|
1524
|
-
console.log(chalk.dim(
|
|
1791
|
+
if (configChoice === "skip") {
|
|
1792
|
+
console.log(chalk.yellow("⚠️ Skipping VS Code settings configuration."));
|
|
1793
|
+
console.log(
|
|
1794
|
+
chalk.dim(
|
|
1795
|
+
"You can manually configure these settings in .vscode/settings.json:",
|
|
1796
|
+
),
|
|
1797
|
+
);
|
|
1798
|
+
console.log(chalk.dim(" • chat.agent.enabled: true"));
|
|
1799
|
+
console.log(chalk.dim(" • chat.agent.maxRequests: 15"));
|
|
1800
|
+
console.log(chalk.dim(" • github.copilot.chat.agent.runTasks: true"));
|
|
1801
|
+
console.log(chalk.dim(" • chat.mcp.discovery.enabled: true"));
|
|
1802
|
+
console.log(chalk.dim(" • github.copilot.chat.agent.autoFix: true"));
|
|
1803
|
+
console.log(chalk.dim(" • chat.tools.autoApprove: false"));
|
|
1525
1804
|
return true;
|
|
1526
1805
|
}
|
|
1527
1806
|
|
|
1528
|
-
if (configChoice ===
|
|
1807
|
+
if (configChoice === "defaults") {
|
|
1529
1808
|
// Use recommended defaults
|
|
1530
1809
|
bmadSettings = {
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1810
|
+
"chat.agent.enabled": true,
|
|
1811
|
+
"chat.agent.maxRequests": 15,
|
|
1812
|
+
"github.copilot.chat.agent.runTasks": true,
|
|
1813
|
+
"chat.mcp.discovery.enabled": true,
|
|
1814
|
+
"github.copilot.chat.agent.autoFix": true,
|
|
1815
|
+
"chat.tools.autoApprove": false,
|
|
1537
1816
|
};
|
|
1538
|
-
console.log(
|
|
1817
|
+
console.log(
|
|
1818
|
+
chalk.green(
|
|
1819
|
+
"✓ Using recommended XiaoMa defaults for Github Copilot settings",
|
|
1820
|
+
),
|
|
1821
|
+
);
|
|
1539
1822
|
} else {
|
|
1540
1823
|
// Manual configuration
|
|
1541
|
-
console.log(
|
|
1824
|
+
console.log(
|
|
1825
|
+
chalk.blue("\n📋 Let's configure each setting for your preferences:"),
|
|
1826
|
+
);
|
|
1542
1827
|
|
|
1543
1828
|
// Pause spinner during manual configuration prompts
|
|
1544
1829
|
let spinnerWasActive = false;
|
|
@@ -1549,40 +1834,43 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1549
1834
|
|
|
1550
1835
|
const manualSettings = await inquirer.prompt([
|
|
1551
1836
|
{
|
|
1552
|
-
type:
|
|
1553
|
-
name:
|
|
1554
|
-
message:
|
|
1555
|
-
default:
|
|
1837
|
+
type: "input",
|
|
1838
|
+
name: "maxRequests",
|
|
1839
|
+
message: "Maximum requests per agent session (recommended: 15)?",
|
|
1840
|
+
default: "15",
|
|
1556
1841
|
validate: (input) => {
|
|
1557
1842
|
const number_ = Number.parseInt(input);
|
|
1558
1843
|
if (isNaN(number_) || number_ < 1 || number_ > 50) {
|
|
1559
|
-
return
|
|
1844
|
+
return "Please enter a number between 1 and 50";
|
|
1560
1845
|
}
|
|
1561
1846
|
return true;
|
|
1562
1847
|
},
|
|
1563
1848
|
},
|
|
1564
1849
|
{
|
|
1565
|
-
type:
|
|
1566
|
-
name:
|
|
1567
|
-
message:
|
|
1850
|
+
type: "confirm",
|
|
1851
|
+
name: "runTasks",
|
|
1852
|
+
message:
|
|
1853
|
+
"Allow agents to run workspace tasks (package.json scripts, etc.)?",
|
|
1568
1854
|
default: true,
|
|
1569
1855
|
},
|
|
1570
1856
|
{
|
|
1571
|
-
type:
|
|
1572
|
-
name:
|
|
1573
|
-
message:
|
|
1857
|
+
type: "confirm",
|
|
1858
|
+
name: "mcpDiscovery",
|
|
1859
|
+
message: "Enable MCP (Model Context Protocol) server discovery?",
|
|
1574
1860
|
default: true,
|
|
1575
1861
|
},
|
|
1576
1862
|
{
|
|
1577
|
-
type:
|
|
1578
|
-
name:
|
|
1579
|
-
message:
|
|
1863
|
+
type: "confirm",
|
|
1864
|
+
name: "autoFix",
|
|
1865
|
+
message:
|
|
1866
|
+
"Enable automatic error detection and fixing in generated code?",
|
|
1580
1867
|
default: true,
|
|
1581
1868
|
},
|
|
1582
1869
|
{
|
|
1583
|
-
type:
|
|
1584
|
-
name:
|
|
1585
|
-
message:
|
|
1870
|
+
type: "confirm",
|
|
1871
|
+
name: "autoApprove",
|
|
1872
|
+
message:
|
|
1873
|
+
"Auto-approve ALL tools without confirmation? (⚠️ EXPERIMENTAL - less secure)",
|
|
1586
1874
|
default: false,
|
|
1587
1875
|
},
|
|
1588
1876
|
]);
|
|
@@ -1593,39 +1881,55 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1593
1881
|
}
|
|
1594
1882
|
|
|
1595
1883
|
bmadSettings = {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1884
|
+
"chat.agent.enabled": true, // Always enabled - required for XiaoMa agents
|
|
1885
|
+
"chat.agent.maxRequests": Number.parseInt(manualSettings.maxRequests),
|
|
1886
|
+
"github.copilot.chat.agent.runTasks": manualSettings.runTasks,
|
|
1887
|
+
"chat.mcp.discovery.enabled": manualSettings.mcpDiscovery,
|
|
1888
|
+
"github.copilot.chat.agent.autoFix": manualSettings.autoFix,
|
|
1889
|
+
"chat.tools.autoApprove": manualSettings.autoApprove,
|
|
1602
1890
|
};
|
|
1603
1891
|
|
|
1604
|
-
console.log(chalk.green(
|
|
1892
|
+
console.log(chalk.green("✓ Custom settings configured"));
|
|
1605
1893
|
}
|
|
1606
1894
|
|
|
1607
1895
|
// Merge settings (existing settings take precedence to avoid overriding user preferences)
|
|
1608
1896
|
const mergedSettings = { ...bmadSettings, ...existingSettings };
|
|
1609
1897
|
|
|
1610
1898
|
// Write the updated settings
|
|
1611
|
-
await fileManager.writeFile(
|
|
1899
|
+
await fileManager.writeFile(
|
|
1900
|
+
settingsPath,
|
|
1901
|
+
JSON.stringify(mergedSettings, null, 2),
|
|
1902
|
+
);
|
|
1612
1903
|
|
|
1613
|
-
console.log(
|
|
1614
|
-
|
|
1904
|
+
console.log(
|
|
1905
|
+
chalk.green("✓ VS Code workspace settings configured successfully"),
|
|
1906
|
+
);
|
|
1907
|
+
console.log(chalk.dim(" Settings written to .vscode/settings.json:"));
|
|
1615
1908
|
for (const [key, value] of Object.entries(bmadSettings)) {
|
|
1616
1909
|
console.log(chalk.dim(` • ${key}: ${value}`));
|
|
1617
1910
|
}
|
|
1618
|
-
console.log(chalk.dim(
|
|
1619
|
-
console.log(
|
|
1911
|
+
console.log(chalk.dim(""));
|
|
1912
|
+
console.log(
|
|
1913
|
+
chalk.dim(
|
|
1914
|
+
"You can modify these settings anytime in .vscode/settings.json",
|
|
1915
|
+
),
|
|
1916
|
+
);
|
|
1620
1917
|
}
|
|
1621
1918
|
|
|
1622
|
-
async setupAuggieCLI(
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1919
|
+
async setupAuggieCLI(
|
|
1920
|
+
installDir,
|
|
1921
|
+
selectedAgent,
|
|
1922
|
+
spinner = null,
|
|
1923
|
+
preConfiguredSettings = null,
|
|
1924
|
+
) {
|
|
1925
|
+
const os = require("node:os");
|
|
1926
|
+
const inquirer = require("inquirer");
|
|
1927
|
+
const agents = selectedAgent
|
|
1928
|
+
? [selectedAgent]
|
|
1929
|
+
: await this.getAllAgentIds(installDir);
|
|
1626
1930
|
|
|
1627
1931
|
// Get the IDE configuration to access location options
|
|
1628
|
-
const ideConfig = await configLoader.getIdeConfiguration(
|
|
1932
|
+
const ideConfig = await configLoader.getIdeConfiguration("auggie-cli");
|
|
1629
1933
|
const locations = ideConfig.locations;
|
|
1630
1934
|
|
|
1631
1935
|
// Use pre-configured settings if provided, otherwise prompt
|
|
@@ -1634,7 +1938,7 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1634
1938
|
selectedLocations = preConfiguredSettings.selectedLocations;
|
|
1635
1939
|
console.log(
|
|
1636
1940
|
chalk.dim(
|
|
1637
|
-
`Using pre-configured Auggie CLI (Augment Code) locations: ${selectedLocations.join(
|
|
1941
|
+
`Using pre-configured Auggie CLI (Augment Code) locations: ${selectedLocations.join(", ")}`,
|
|
1638
1942
|
),
|
|
1639
1943
|
);
|
|
1640
1944
|
} else {
|
|
@@ -1646,23 +1950,27 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1646
1950
|
}
|
|
1647
1951
|
|
|
1648
1952
|
// Clear any previous output and add spacing to avoid conflicts with loaders
|
|
1649
|
-
console.log(
|
|
1650
|
-
console.log(chalk.blue(
|
|
1651
|
-
console.log(
|
|
1652
|
-
|
|
1953
|
+
console.log("\n".repeat(2));
|
|
1954
|
+
console.log(chalk.blue("📍 Auggie CLI Location Configuration"));
|
|
1955
|
+
console.log(
|
|
1956
|
+
chalk.dim(
|
|
1957
|
+
"Choose where to install XiaoMa agents for Auggie CLI access.",
|
|
1958
|
+
),
|
|
1959
|
+
);
|
|
1960
|
+
console.log(""); // Add extra spacing
|
|
1653
1961
|
|
|
1654
1962
|
const response = await inquirer.prompt([
|
|
1655
1963
|
{
|
|
1656
|
-
type:
|
|
1657
|
-
name:
|
|
1658
|
-
message:
|
|
1964
|
+
type: "checkbox",
|
|
1965
|
+
name: "selectedLocations",
|
|
1966
|
+
message: "Select Auggie CLI command locations:",
|
|
1659
1967
|
choices: Object.entries(locations).map(([key, location]) => ({
|
|
1660
1968
|
name: `${location.name}: ${location.description}`,
|
|
1661
1969
|
value: key,
|
|
1662
1970
|
})),
|
|
1663
1971
|
validate: (selected) => {
|
|
1664
1972
|
if (selected.length === 0) {
|
|
1665
|
-
return
|
|
1973
|
+
return "Please select at least one location";
|
|
1666
1974
|
}
|
|
1667
1975
|
return true;
|
|
1668
1976
|
},
|
|
@@ -1679,12 +1987,12 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1679
1987
|
// Install to each selected location
|
|
1680
1988
|
for (const locationKey of selectedLocations) {
|
|
1681
1989
|
const location = locations[locationKey];
|
|
1682
|
-
let commandsDir = location[
|
|
1990
|
+
let commandsDir = location["rule-dir"];
|
|
1683
1991
|
|
|
1684
1992
|
// Handle tilde expansion for user directory
|
|
1685
|
-
if (commandsDir.startsWith(
|
|
1993
|
+
if (commandsDir.startsWith("~/")) {
|
|
1686
1994
|
commandsDir = path.join(os.homedir(), commandsDir.slice(2));
|
|
1687
|
-
} else if (commandsDir.startsWith(
|
|
1995
|
+
} else if (commandsDir.startsWith("./")) {
|
|
1688
1996
|
commandsDir = path.join(installDir, commandsDir.slice(2));
|
|
1689
1997
|
}
|
|
1690
1998
|
|
|
@@ -1698,12 +2006,18 @@ tools: ['changes', 'codebase', 'fetch', 'findTestFiles', 'githubRepo', 'problems
|
|
|
1698
2006
|
const agentContent = await fileManager.readFile(agentPath);
|
|
1699
2007
|
const mdPath = path.join(commandsDir, `${agentId}.md`);
|
|
1700
2008
|
await fileManager.writeFile(mdPath, agentContent);
|
|
1701
|
-
console.log(
|
|
2009
|
+
console.log(
|
|
2010
|
+
chalk.green(`✓ Created command: ${agentId}.md in ${location.name}`),
|
|
2011
|
+
);
|
|
1702
2012
|
}
|
|
1703
2013
|
}
|
|
1704
2014
|
|
|
1705
|
-
console.log(
|
|
1706
|
-
|
|
2015
|
+
console.log(
|
|
2016
|
+
chalk.green(`\n✓ Created Auggie CLI commands in ${commandsDir}`),
|
|
2017
|
+
);
|
|
2018
|
+
console.log(
|
|
2019
|
+
chalk.dim(` Location: ${location.name} - ${location.description}`),
|
|
2020
|
+
);
|
|
1707
2021
|
}
|
|
1708
2022
|
|
|
1709
2023
|
return true;
|