@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,11 +1,16 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const yaml = require(
|
|
4
|
-
const { extractYamlFromAgent } = require(
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const yaml = require("js-yaml");
|
|
4
|
+
const { extractYamlFromAgent } = require("../../lib/yaml-utils");
|
|
5
5
|
|
|
6
6
|
class ConfigLoader {
|
|
7
7
|
constructor() {
|
|
8
|
-
this.configPath = path.join(
|
|
8
|
+
this.configPath = path.join(
|
|
9
|
+
__dirname,
|
|
10
|
+
"..",
|
|
11
|
+
"config",
|
|
12
|
+
"install.config.yaml",
|
|
13
|
+
);
|
|
9
14
|
this.config = null;
|
|
10
15
|
}
|
|
11
16
|
|
|
@@ -13,7 +18,7 @@ class ConfigLoader {
|
|
|
13
18
|
if (this.config) return this.config;
|
|
14
19
|
|
|
15
20
|
try {
|
|
16
|
-
const configContent = await fs.readFile(this.configPath,
|
|
21
|
+
const configContent = await fs.readFile(this.configPath, "utf8");
|
|
17
22
|
this.config = yaml.load(configContent);
|
|
18
23
|
return this.config;
|
|
19
24
|
} catch (error) {
|
|
@@ -23,23 +28,23 @@ class ConfigLoader {
|
|
|
23
28
|
|
|
24
29
|
async getInstallationOptions() {
|
|
25
30
|
const config = await this.load();
|
|
26
|
-
return config[
|
|
31
|
+
return config["installation-options"] || {};
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
async getAvailableAgents() {
|
|
30
|
-
const agentsDir = path.join(this.getBmadCorePath(),
|
|
35
|
+
const agentsDir = path.join(this.getBmadCorePath(), "agents");
|
|
31
36
|
|
|
32
37
|
try {
|
|
33
38
|
const entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
34
39
|
const agents = [];
|
|
35
40
|
|
|
36
41
|
for (const entry of entries) {
|
|
37
|
-
if (entry.isFile() && entry.name.endsWith(
|
|
42
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
38
43
|
const agentPath = path.join(agentsDir, entry.name);
|
|
39
|
-
const agentId = path.basename(entry.name,
|
|
44
|
+
const agentId = path.basename(entry.name, ".md");
|
|
40
45
|
|
|
41
46
|
try {
|
|
42
|
-
const agentContent = await fs.readFile(agentPath,
|
|
47
|
+
const agentContent = await fs.readFile(agentPath, "utf8");
|
|
43
48
|
|
|
44
49
|
// Extract YAML block from agent file
|
|
45
50
|
const yamlContentText = extractYamlFromAgent(agentContent);
|
|
@@ -51,11 +56,14 @@ class ConfigLoader {
|
|
|
51
56
|
id: agentId,
|
|
52
57
|
name: agentConfig.title || agentConfig.name || agentId,
|
|
53
58
|
file: `xiaoma-core/agents/${entry.name}`,
|
|
54
|
-
description:
|
|
59
|
+
description:
|
|
60
|
+
agentConfig.whenToUse || "No description available",
|
|
55
61
|
});
|
|
56
62
|
}
|
|
57
63
|
} catch (error) {
|
|
58
|
-
console.warn(
|
|
64
|
+
console.warn(
|
|
65
|
+
`Failed to read agent ${entry.name}: ${error.message}`,
|
|
66
|
+
);
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
}
|
|
@@ -71,31 +79,41 @@ class ConfigLoader {
|
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
async getAvailableExpansionPacks() {
|
|
74
|
-
const expansionPacksDir = path.join(
|
|
82
|
+
const expansionPacksDir = path.join(
|
|
83
|
+
this.getBmadCorePath(),
|
|
84
|
+
"..",
|
|
85
|
+
"expansion-packs",
|
|
86
|
+
);
|
|
75
87
|
|
|
76
88
|
try {
|
|
77
|
-
const entries = await fs.readdir(expansionPacksDir, {
|
|
89
|
+
const entries = await fs.readdir(expansionPacksDir, {
|
|
90
|
+
withFileTypes: true,
|
|
91
|
+
});
|
|
78
92
|
const expansionPacks = [];
|
|
79
93
|
|
|
80
94
|
for (const entry of entries) {
|
|
81
|
-
if (entry.isDirectory() && !entry.name.startsWith(
|
|
95
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
82
96
|
const packPath = path.join(expansionPacksDir, entry.name);
|
|
83
|
-
const configPath = path.join(packPath,
|
|
97
|
+
const configPath = path.join(packPath, "config.yaml");
|
|
84
98
|
|
|
85
99
|
try {
|
|
86
100
|
// Read config.yaml
|
|
87
|
-
const configContent = await fs.readFile(configPath,
|
|
101
|
+
const configContent = await fs.readFile(configPath, "utf8");
|
|
88
102
|
const config = yaml.load(configContent);
|
|
89
103
|
|
|
90
104
|
expansionPacks.push({
|
|
91
105
|
id: entry.name,
|
|
92
106
|
name: config.name || entry.name,
|
|
93
107
|
description:
|
|
94
|
-
config[
|
|
108
|
+
config["short-title"] ||
|
|
109
|
+
config.description ||
|
|
110
|
+
"No description available",
|
|
95
111
|
fullDescription:
|
|
96
|
-
config.description ||
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
config.description ||
|
|
113
|
+
config["short-title"] ||
|
|
114
|
+
"No description available",
|
|
115
|
+
version: config.version || "1.0.0",
|
|
116
|
+
author: config.author || "XiaoMa Team",
|
|
99
117
|
packPath: packPath,
|
|
100
118
|
dependencies: config.dependencies?.agents || [],
|
|
101
119
|
});
|
|
@@ -107,17 +125,17 @@ class ConfigLoader {
|
|
|
107
125
|
|
|
108
126
|
// Try to derive info from directory name as fallback
|
|
109
127
|
const name = entry.name
|
|
110
|
-
.split(
|
|
128
|
+
.split("-")
|
|
111
129
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
112
|
-
.join(
|
|
130
|
+
.join(" ");
|
|
113
131
|
|
|
114
132
|
expansionPacks.push({
|
|
115
133
|
id: entry.name,
|
|
116
134
|
name: name,
|
|
117
|
-
description:
|
|
118
|
-
fullDescription:
|
|
119
|
-
version:
|
|
120
|
-
author:
|
|
135
|
+
description: "No description available",
|
|
136
|
+
fullDescription: "No description available",
|
|
137
|
+
version: "1.0.0",
|
|
138
|
+
author: "XiaoMa Team",
|
|
121
139
|
packPath: packPath,
|
|
122
140
|
dependencies: [],
|
|
123
141
|
});
|
|
@@ -127,15 +145,19 @@ class ConfigLoader {
|
|
|
127
145
|
|
|
128
146
|
return expansionPacks;
|
|
129
147
|
} catch (error) {
|
|
130
|
-
console.warn(
|
|
148
|
+
console.warn(
|
|
149
|
+
`Failed to read expansion packs directory: ${error.message}`,
|
|
150
|
+
);
|
|
131
151
|
return [];
|
|
132
152
|
}
|
|
133
153
|
}
|
|
134
154
|
|
|
135
155
|
async getAgentDependencies(agentId) {
|
|
136
156
|
// Use DependencyResolver to dynamically parse agent dependencies
|
|
137
|
-
const DependencyResolver = require(
|
|
138
|
-
const resolver = new DependencyResolver(
|
|
157
|
+
const DependencyResolver = require("../../lib/dependency-resolver");
|
|
158
|
+
const resolver = new DependencyResolver(
|
|
159
|
+
path.join(__dirname, "..", "..", ".."),
|
|
160
|
+
);
|
|
139
161
|
|
|
140
162
|
const agentDeps = await resolver.resolveAgentDependencies(agentId);
|
|
141
163
|
|
|
@@ -159,49 +181,52 @@ class ConfigLoader {
|
|
|
159
181
|
|
|
160
182
|
async getIdeConfiguration(ide) {
|
|
161
183
|
const config = await this.load();
|
|
162
|
-
const ideConfigs = config[
|
|
184
|
+
const ideConfigs = config["ide-configurations"] || {};
|
|
163
185
|
return ideConfigs[ide] || null;
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
getBmadCorePath() {
|
|
167
189
|
// Get the path to xiaoma-core relative to the installer (now under tools)
|
|
168
|
-
return path.join(__dirname,
|
|
190
|
+
return path.join(__dirname, "..", "..", "..", "xiaoma-core");
|
|
169
191
|
}
|
|
170
192
|
|
|
171
193
|
getDistPath() {
|
|
172
194
|
// Get the path to dist directory relative to the installer
|
|
173
|
-
return path.join(__dirname,
|
|
195
|
+
return path.join(__dirname, "..", "..", "..", "dist");
|
|
174
196
|
}
|
|
175
197
|
|
|
176
198
|
getAgentPath(agentId) {
|
|
177
|
-
return path.join(this.getBmadCorePath(),
|
|
199
|
+
return path.join(this.getBmadCorePath(), "agents", `${agentId}.md`);
|
|
178
200
|
}
|
|
179
201
|
|
|
180
202
|
async getAvailableTeams() {
|
|
181
|
-
const teamsDir = path.join(this.getBmadCorePath(),
|
|
203
|
+
const teamsDir = path.join(this.getBmadCorePath(), "agent-teams");
|
|
182
204
|
|
|
183
205
|
try {
|
|
184
206
|
const entries = await fs.readdir(teamsDir, { withFileTypes: true });
|
|
185
207
|
const teams = [];
|
|
186
208
|
|
|
187
209
|
for (const entry of entries) {
|
|
188
|
-
if (entry.isFile() && entry.name.endsWith(
|
|
210
|
+
if (entry.isFile() && entry.name.endsWith(".yaml")) {
|
|
189
211
|
const teamPath = path.join(teamsDir, entry.name);
|
|
190
212
|
|
|
191
213
|
try {
|
|
192
|
-
const teamContent = await fs.readFile(teamPath,
|
|
214
|
+
const teamContent = await fs.readFile(teamPath, "utf8");
|
|
193
215
|
const teamConfig = yaml.load(teamContent);
|
|
194
216
|
|
|
195
217
|
if (teamConfig.bundle) {
|
|
196
218
|
teams.push({
|
|
197
|
-
id: path.basename(entry.name,
|
|
219
|
+
id: path.basename(entry.name, ".yaml"),
|
|
198
220
|
name: teamConfig.bundle.name || entry.name,
|
|
199
|
-
description:
|
|
200
|
-
|
|
221
|
+
description:
|
|
222
|
+
teamConfig.bundle.description || "Team configuration",
|
|
223
|
+
icon: teamConfig.bundle.icon || "📋",
|
|
201
224
|
});
|
|
202
225
|
}
|
|
203
226
|
} catch (error) {
|
|
204
|
-
console.warn(
|
|
227
|
+
console.warn(
|
|
228
|
+
`Warning: Could not load team config ${entry.name}: ${error.message}`,
|
|
229
|
+
);
|
|
205
230
|
}
|
|
206
231
|
}
|
|
207
232
|
}
|
|
@@ -214,13 +239,15 @@ class ConfigLoader {
|
|
|
214
239
|
}
|
|
215
240
|
|
|
216
241
|
getTeamPath(teamId) {
|
|
217
|
-
return path.join(this.getBmadCorePath(),
|
|
242
|
+
return path.join(this.getBmadCorePath(), "agent-teams", `${teamId}.yaml`);
|
|
218
243
|
}
|
|
219
244
|
|
|
220
245
|
async getTeamDependencies(teamId) {
|
|
221
246
|
// Use DependencyResolver to dynamically parse team dependencies
|
|
222
|
-
const DependencyResolver = require(
|
|
223
|
-
const resolver = new DependencyResolver(
|
|
247
|
+
const DependencyResolver = require("../../lib/dependency-resolver");
|
|
248
|
+
const resolver = new DependencyResolver(
|
|
249
|
+
path.join(__dirname, "..", "..", ".."),
|
|
250
|
+
);
|
|
224
251
|
|
|
225
252
|
try {
|
|
226
253
|
const teamDeps = await resolver.resolveTeamDependencies(teamId);
|
|
@@ -241,7 +268,7 @@ class ConfigLoader {
|
|
|
241
268
|
|
|
242
269
|
// Add all resolved resources
|
|
243
270
|
for (const resource of teamDeps.resources) {
|
|
244
|
-
const filePath = `.xiaoma-core/${resource.type}/${resource.id}.${resource.type ===
|
|
271
|
+
const filePath = `.xiaoma-core/${resource.type}/${resource.id}.${resource.type === "workflows" ? "yaml" : "md"}`;
|
|
245
272
|
if (!depPaths.includes(filePath)) {
|
|
246
273
|
depPaths.push(filePath);
|
|
247
274
|
}
|
|
@@ -249,7 +276,9 @@ class ConfigLoader {
|
|
|
249
276
|
|
|
250
277
|
return depPaths;
|
|
251
278
|
} catch (error) {
|
|
252
|
-
throw new Error(
|
|
279
|
+
throw new Error(
|
|
280
|
+
`Failed to resolve team dependencies for ${teamId}: ${error.message}`,
|
|
281
|
+
);
|
|
253
282
|
}
|
|
254
283
|
}
|
|
255
284
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const crypto = require(
|
|
4
|
-
const yaml = require(
|
|
5
|
-
const chalk = require(
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const crypto = require("node:crypto");
|
|
4
|
+
const yaml = require("js-yaml");
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const {
|
|
7
|
+
createReadStream,
|
|
8
|
+
createWriteStream,
|
|
9
|
+
promises: fsPromises,
|
|
10
|
+
} = require("node:fs");
|
|
11
|
+
const { pipeline } = require("node:stream/promises");
|
|
12
|
+
const resourceLocator = require("./resource-locator");
|
|
9
13
|
|
|
10
14
|
class FileManager {
|
|
11
15
|
constructor() {}
|
|
@@ -31,7 +35,7 @@ class FileManager {
|
|
|
31
35
|
await fs.ensureDir(destination);
|
|
32
36
|
|
|
33
37
|
// Use streaming copy for large directories
|
|
34
|
-
const files = await resourceLocator.findFiles(
|
|
38
|
+
const files = await resourceLocator.findFiles("**/*", {
|
|
35
39
|
cwd: source,
|
|
36
40
|
nodir: true,
|
|
37
41
|
});
|
|
@@ -41,12 +45,20 @@ class FileManager {
|
|
|
41
45
|
for (let index = 0; index < files.length; index += batchSize) {
|
|
42
46
|
const batch = files.slice(index, index + batchSize);
|
|
43
47
|
await Promise.all(
|
|
44
|
-
batch.map((file) =>
|
|
48
|
+
batch.map((file) =>
|
|
49
|
+
this.copyFile(
|
|
50
|
+
path.join(source, file),
|
|
51
|
+
path.join(destination, file),
|
|
52
|
+
),
|
|
53
|
+
),
|
|
45
54
|
);
|
|
46
55
|
}
|
|
47
56
|
return true;
|
|
48
57
|
} catch (error) {
|
|
49
|
-
console.error(
|
|
58
|
+
console.error(
|
|
59
|
+
chalk.red(`Failed to copy directory ${source}:`),
|
|
60
|
+
error.message,
|
|
61
|
+
);
|
|
50
62
|
return false;
|
|
51
63
|
}
|
|
52
64
|
}
|
|
@@ -61,11 +73,18 @@ class FileManager {
|
|
|
61
73
|
|
|
62
74
|
// Use root replacement if rootValue is provided and file needs it
|
|
63
75
|
const needsRootReplacement =
|
|
64
|
-
rootValue &&
|
|
76
|
+
rootValue &&
|
|
77
|
+
(file.endsWith(".md") ||
|
|
78
|
+
file.endsWith(".yaml") ||
|
|
79
|
+
file.endsWith(".yml"));
|
|
65
80
|
|
|
66
81
|
let success = false;
|
|
67
82
|
success = await (needsRootReplacement
|
|
68
|
-
? this.copyFileWithRootReplacement(
|
|
83
|
+
? this.copyFileWithRootReplacement(
|
|
84
|
+
sourcePath,
|
|
85
|
+
destinationPath,
|
|
86
|
+
rootValue,
|
|
87
|
+
)
|
|
69
88
|
: this.copyFile(sourcePath, destinationPath));
|
|
70
89
|
|
|
71
90
|
if (success) {
|
|
@@ -80,25 +99,35 @@ class FileManager {
|
|
|
80
99
|
try {
|
|
81
100
|
// Use streaming for hash calculation to reduce memory usage
|
|
82
101
|
const stream = createReadStream(filePath);
|
|
83
|
-
const hash = crypto.createHash(
|
|
102
|
+
const hash = crypto.createHash("sha256");
|
|
84
103
|
|
|
85
104
|
for await (const chunk of stream) {
|
|
86
105
|
hash.update(chunk);
|
|
87
106
|
}
|
|
88
107
|
|
|
89
|
-
return hash.digest(
|
|
108
|
+
return hash.digest("hex").slice(0, 16);
|
|
90
109
|
} catch {
|
|
91
110
|
return null;
|
|
92
111
|
}
|
|
93
112
|
}
|
|
94
113
|
|
|
95
114
|
async createManifest(installDir, config, files) {
|
|
96
|
-
const manifestPath = path.join(
|
|
115
|
+
const manifestPath = path.join(
|
|
116
|
+
installDir,
|
|
117
|
+
this.manifestDir,
|
|
118
|
+
this.manifestFile,
|
|
119
|
+
);
|
|
97
120
|
|
|
98
121
|
// Read version from package.json
|
|
99
|
-
let coreVersion =
|
|
122
|
+
let coreVersion = "unknown";
|
|
100
123
|
try {
|
|
101
|
-
const packagePath = path.join(
|
|
124
|
+
const packagePath = path.join(
|
|
125
|
+
__dirname,
|
|
126
|
+
"..",
|
|
127
|
+
"..",
|
|
128
|
+
"..",
|
|
129
|
+
"package.json",
|
|
130
|
+
);
|
|
102
131
|
const packageJson = require(packagePath);
|
|
103
132
|
coreVersion = packageJson.version;
|
|
104
133
|
} catch {
|
|
@@ -135,10 +164,14 @@ class FileManager {
|
|
|
135
164
|
}
|
|
136
165
|
|
|
137
166
|
async readManifest(installDir) {
|
|
138
|
-
const manifestPath = path.join(
|
|
167
|
+
const manifestPath = path.join(
|
|
168
|
+
installDir,
|
|
169
|
+
this.manifestDir,
|
|
170
|
+
this.manifestFile,
|
|
171
|
+
);
|
|
139
172
|
|
|
140
173
|
try {
|
|
141
|
-
const content = await fs.readFile(manifestPath,
|
|
174
|
+
const content = await fs.readFile(manifestPath, "utf8");
|
|
142
175
|
return yaml.load(content);
|
|
143
176
|
} catch {
|
|
144
177
|
return null;
|
|
@@ -149,7 +182,7 @@ class FileManager {
|
|
|
149
182
|
const manifestPath = path.join(installDir, `.${packId}`, this.manifestFile);
|
|
150
183
|
|
|
151
184
|
try {
|
|
152
|
-
const content = await fs.readFile(manifestPath,
|
|
185
|
+
const content = await fs.readFile(manifestPath, "utf8");
|
|
153
186
|
return yaml.load(content);
|
|
154
187
|
} catch {
|
|
155
188
|
return null;
|
|
@@ -181,7 +214,7 @@ class FileManager {
|
|
|
181
214
|
const filePath = path.join(installDir, file.path);
|
|
182
215
|
|
|
183
216
|
// Skip checking the manifest file itself - it will always be different due to timestamps
|
|
184
|
-
if (file.path.endsWith(
|
|
217
|
+
if (file.path.endsWith("install-manifest.yaml")) {
|
|
185
218
|
continue;
|
|
186
219
|
}
|
|
187
220
|
|
|
@@ -199,7 +232,7 @@ class FileManager {
|
|
|
199
232
|
}
|
|
200
233
|
|
|
201
234
|
async backupFile(filePath) {
|
|
202
|
-
const backupPath = filePath +
|
|
235
|
+
const backupPath = filePath + ".bak";
|
|
203
236
|
let counter = 1;
|
|
204
237
|
let finalBackupPath = backupPath;
|
|
205
238
|
|
|
@@ -227,7 +260,7 @@ class FileManager {
|
|
|
227
260
|
}
|
|
228
261
|
|
|
229
262
|
async readFile(filePath) {
|
|
230
|
-
return fs.readFile(filePath,
|
|
263
|
+
return fs.readFile(filePath, "utf8");
|
|
231
264
|
}
|
|
232
265
|
|
|
233
266
|
async writeFile(filePath, content) {
|
|
@@ -243,7 +276,8 @@ class FileManager {
|
|
|
243
276
|
const manifestPath = path.join(installDir, `.${packId}`, this.manifestFile);
|
|
244
277
|
|
|
245
278
|
const manifest = {
|
|
246
|
-
version:
|
|
279
|
+
version:
|
|
280
|
+
config.expansionPackVersion || require("../../../package.json").version,
|
|
247
281
|
installed_at: new Date().toISOString(),
|
|
248
282
|
install_type: config.installType,
|
|
249
283
|
expansion_pack_id: config.expansionPackId,
|
|
@@ -272,11 +306,15 @@ class FileManager {
|
|
|
272
306
|
}
|
|
273
307
|
|
|
274
308
|
async modifyCoreConfig(installDir, config) {
|
|
275
|
-
const coreConfigPath = path.join(
|
|
309
|
+
const coreConfigPath = path.join(
|
|
310
|
+
installDir,
|
|
311
|
+
".xiaoma-core",
|
|
312
|
+
"core-config.yaml",
|
|
313
|
+
);
|
|
276
314
|
|
|
277
315
|
try {
|
|
278
316
|
// Read the existing core-config.yaml
|
|
279
|
-
const coreConfigContent = await fs.readFile(coreConfigPath,
|
|
317
|
+
const coreConfigContent = await fs.readFile(coreConfigPath, "utf8");
|
|
280
318
|
const coreConfig = yaml.load(coreConfigContent);
|
|
281
319
|
|
|
282
320
|
// Modify sharding settings if provided
|
|
@@ -285,7 +323,8 @@ class FileManager {
|
|
|
285
323
|
}
|
|
286
324
|
|
|
287
325
|
if (config.architectureSharded !== undefined) {
|
|
288
|
-
coreConfig.architecture.architectureSharded =
|
|
326
|
+
coreConfig.architecture.architectureSharded =
|
|
327
|
+
config.architectureSharded;
|
|
289
328
|
}
|
|
290
329
|
|
|
291
330
|
// Write back the modified config
|
|
@@ -293,7 +332,10 @@ class FileManager {
|
|
|
293
332
|
|
|
294
333
|
return true;
|
|
295
334
|
} catch (error) {
|
|
296
|
-
console.error(
|
|
335
|
+
console.error(
|
|
336
|
+
chalk.red(`Failed to modify core-config.yaml:`),
|
|
337
|
+
error.message,
|
|
338
|
+
);
|
|
297
339
|
return false;
|
|
298
340
|
}
|
|
299
341
|
}
|
|
@@ -306,31 +348,34 @@ class FileManager {
|
|
|
306
348
|
if (stats.size > 5 * 1024 * 1024) {
|
|
307
349
|
// 5MB threshold
|
|
308
350
|
// Use streaming for large files
|
|
309
|
-
const { Transform } = require(
|
|
351
|
+
const { Transform } = require("node:stream");
|
|
310
352
|
const replaceStream = new Transform({
|
|
311
353
|
transform(chunk, encoding, callback) {
|
|
312
|
-
const modified = chunk.toString().replaceAll(
|
|
354
|
+
const modified = chunk.toString().replaceAll("{root}", rootValue);
|
|
313
355
|
callback(null, modified);
|
|
314
356
|
},
|
|
315
357
|
});
|
|
316
358
|
|
|
317
359
|
await this.ensureDirectory(path.dirname(destination));
|
|
318
360
|
await pipeline(
|
|
319
|
-
createReadStream(source, { encoding:
|
|
361
|
+
createReadStream(source, { encoding: "utf8" }),
|
|
320
362
|
replaceStream,
|
|
321
|
-
createWriteStream(destination, { encoding:
|
|
363
|
+
createWriteStream(destination, { encoding: "utf8" }),
|
|
322
364
|
);
|
|
323
365
|
} else {
|
|
324
366
|
// Regular approach for smaller files
|
|
325
|
-
const content = await fsPromises.readFile(source,
|
|
326
|
-
const updatedContent = content.replaceAll(
|
|
367
|
+
const content = await fsPromises.readFile(source, "utf8");
|
|
368
|
+
const updatedContent = content.replaceAll("{root}", rootValue);
|
|
327
369
|
await this.ensureDirectory(path.dirname(destination));
|
|
328
|
-
await fsPromises.writeFile(destination, updatedContent,
|
|
370
|
+
await fsPromises.writeFile(destination, updatedContent, "utf8");
|
|
329
371
|
}
|
|
330
372
|
|
|
331
373
|
return true;
|
|
332
374
|
} catch (error) {
|
|
333
|
-
console.error(
|
|
375
|
+
console.error(
|
|
376
|
+
chalk.red(`Failed to copy ${source} with root replacement:`),
|
|
377
|
+
error.message,
|
|
378
|
+
);
|
|
334
379
|
return false;
|
|
335
380
|
}
|
|
336
381
|
}
|
|
@@ -339,13 +384,13 @@ class FileManager {
|
|
|
339
384
|
source,
|
|
340
385
|
destination,
|
|
341
386
|
rootValue,
|
|
342
|
-
fileExtensions = [
|
|
387
|
+
fileExtensions = [".md", ".yaml", ".yml"],
|
|
343
388
|
) {
|
|
344
389
|
try {
|
|
345
390
|
await this.ensureDirectory(destination);
|
|
346
391
|
|
|
347
392
|
// Get all files in source directory
|
|
348
|
-
const files = await resourceLocator.findFiles(
|
|
393
|
+
const files = await resourceLocator.findFiles("**/*", {
|
|
349
394
|
cwd: source,
|
|
350
395
|
nodir: true,
|
|
351
396
|
});
|
|
@@ -357,10 +402,18 @@ class FileManager {
|
|
|
357
402
|
const destinationPath = path.join(destination, file);
|
|
358
403
|
|
|
359
404
|
// Check if this file type should have {root} replacement
|
|
360
|
-
const shouldReplace = fileExtensions.some((extension) =>
|
|
405
|
+
const shouldReplace = fileExtensions.some((extension) =>
|
|
406
|
+
file.endsWith(extension),
|
|
407
|
+
);
|
|
361
408
|
|
|
362
409
|
if (shouldReplace) {
|
|
363
|
-
if (
|
|
410
|
+
if (
|
|
411
|
+
await this.copyFileWithRootReplacement(
|
|
412
|
+
sourcePath,
|
|
413
|
+
destinationPath,
|
|
414
|
+
rootValue,
|
|
415
|
+
)
|
|
416
|
+
) {
|
|
364
417
|
replacedCount++;
|
|
365
418
|
}
|
|
366
419
|
} else {
|
|
@@ -370,7 +423,11 @@ class FileManager {
|
|
|
370
423
|
}
|
|
371
424
|
|
|
372
425
|
if (replacedCount > 0) {
|
|
373
|
-
console.log(
|
|
426
|
+
console.log(
|
|
427
|
+
chalk.dim(
|
|
428
|
+
` Processed ${replacedCount} files with {root} replacement`,
|
|
429
|
+
),
|
|
430
|
+
);
|
|
374
431
|
}
|
|
375
432
|
|
|
376
433
|
return true;
|
|
@@ -382,8 +439,8 @@ class FileManager {
|
|
|
382
439
|
return false;
|
|
383
440
|
}
|
|
384
441
|
}
|
|
385
|
-
manifestDir =
|
|
386
|
-
manifestFile =
|
|
442
|
+
manifestDir = ".xiaoma-core";
|
|
443
|
+
manifestFile = "install-manifest.yaml";
|
|
387
444
|
}
|
|
388
445
|
|
|
389
446
|
module.exports = new FileManager();
|