@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
|
@@ -5,64 +5,67 @@
|
|
|
5
5
|
No external options or flags required. Safe to run multiple times.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const os = require(
|
|
9
|
-
const path = require(
|
|
10
|
-
const fs = require(
|
|
11
|
-
const { promisify } = require(
|
|
12
|
-
const { execFile } = require(
|
|
13
|
-
const process = require(
|
|
8
|
+
const os = require("node:os");
|
|
9
|
+
const path = require("node:path");
|
|
10
|
+
const fs = require("fs-extra");
|
|
11
|
+
const { promisify } = require("node:util");
|
|
12
|
+
const { execFile } = require("node:child_process");
|
|
13
|
+
const process = require("node:process");
|
|
14
14
|
const execFileAsync = promisify(execFile);
|
|
15
15
|
|
|
16
|
-
const { findProjectRoot } = require(
|
|
16
|
+
const { findProjectRoot } = require("./projectRoot.js");
|
|
17
17
|
|
|
18
18
|
async function cmdAvailable(cmd) {
|
|
19
19
|
try {
|
|
20
|
-
await execFileAsync(cmd, [
|
|
20
|
+
await execFileAsync(cmd, ["--version"], {
|
|
21
|
+
timeout: 500,
|
|
22
|
+
windowsHide: true,
|
|
23
|
+
});
|
|
21
24
|
return true;
|
|
22
25
|
} catch {
|
|
23
26
|
return false;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
async function testSvnMarker() {
|
|
27
|
-
const root = await mkTmpDir(
|
|
28
|
-
const nested = path.join(root,
|
|
30
|
+
const root = await mkTmpDir("svn");
|
|
31
|
+
const nested = path.join(root, "proj", "code");
|
|
29
32
|
await fs.ensureDir(nested);
|
|
30
|
-
await fs.ensureDir(path.join(root,
|
|
33
|
+
await fs.ensureDir(path.join(root, ".svn"));
|
|
31
34
|
const found = await findProjectRoot(nested);
|
|
32
|
-
assertEqual(found, root,
|
|
33
|
-
return { name:
|
|
35
|
+
assertEqual(found, root, ".svn marker should be detected");
|
|
36
|
+
return { name: "svn-marker", ok: true };
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
async function testSymlinkStart() {
|
|
37
|
-
const root = await mkTmpDir(
|
|
38
|
-
const nested = path.join(root,
|
|
40
|
+
const root = await mkTmpDir("symlink-start");
|
|
41
|
+
const nested = path.join(root, "a", "b");
|
|
39
42
|
await fs.ensureDir(nested);
|
|
40
|
-
await fs.writeFile(path.join(root,
|
|
41
|
-
const tmp = await mkTmpDir(
|
|
42
|
-
const link = path.join(tmp,
|
|
43
|
+
await fs.writeFile(path.join(root, ".project-root"), "\n");
|
|
44
|
+
const tmp = await mkTmpDir("symlink-tmp");
|
|
45
|
+
const link = path.join(tmp, "link-to-b");
|
|
43
46
|
try {
|
|
44
47
|
await fs.symlink(nested, link);
|
|
45
48
|
} catch {
|
|
46
49
|
// symlink may not be permitted on some systems; skip
|
|
47
|
-
return { name:
|
|
50
|
+
return { name: "symlink-start", ok: true, skipped: true };
|
|
48
51
|
}
|
|
49
52
|
const found = await findProjectRoot(link);
|
|
50
|
-
assertEqual(found, root,
|
|
51
|
-
return { name:
|
|
53
|
+
assertEqual(found, root, "should resolve symlinked start to real root");
|
|
54
|
+
return { name: "symlink-start", ok: true };
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
async function testSubmoduleLikeInnerGitFile() {
|
|
55
|
-
const root = await mkTmpDir(
|
|
56
|
-
const mid = path.join(root,
|
|
57
|
-
const leaf = path.join(mid,
|
|
58
|
+
const root = await mkTmpDir("submodule-like");
|
|
59
|
+
const mid = path.join(root, "mid");
|
|
60
|
+
const leaf = path.join(mid, "leaf");
|
|
58
61
|
await fs.ensureDir(leaf);
|
|
59
62
|
// outer repo
|
|
60
|
-
await fs.ensureDir(path.join(root,
|
|
63
|
+
await fs.ensureDir(path.join(root, ".git"));
|
|
61
64
|
// inner submodule-like .git file
|
|
62
|
-
await fs.writeFile(path.join(mid,
|
|
65
|
+
await fs.writeFile(path.join(mid, ".git"), "gitdir: ../.git/modules/mid\n");
|
|
63
66
|
const found = await findProjectRoot(leaf);
|
|
64
|
-
assertEqual(found, root,
|
|
65
|
-
return { name:
|
|
67
|
+
assertEqual(found, root, "outermost .git should win on tie weight");
|
|
68
|
+
return { name: "submodule-like-gitfile", ok: true };
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
|
|
@@ -79,278 +82,325 @@ function assertEqual(actual, expected, msg) {
|
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
async function testSentinel() {
|
|
82
|
-
const root = await mkTmpDir(
|
|
83
|
-
const nested = path.join(root,
|
|
85
|
+
const root = await mkTmpDir("sentinel");
|
|
86
|
+
const nested = path.join(root, "a", "b", "c");
|
|
84
87
|
await fs.ensureDir(nested);
|
|
85
|
-
await fs.writeFile(path.join(root,
|
|
88
|
+
await fs.writeFile(path.join(root, ".project-root"), "\n");
|
|
86
89
|
const found = await findProjectRoot(nested);
|
|
87
|
-
await assertEqual(found, root,
|
|
88
|
-
return { name:
|
|
90
|
+
await assertEqual(found, root, "sentinel .project-root should win");
|
|
91
|
+
return { name: "sentinel", ok: true };
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
async function testOtherSentinels() {
|
|
92
|
-
const root = await mkTmpDir(
|
|
93
|
-
const nested = path.join(root,
|
|
95
|
+
const root = await mkTmpDir("other-sentinels");
|
|
96
|
+
const nested = path.join(root, "x", "y");
|
|
94
97
|
await fs.ensureDir(nested);
|
|
95
|
-
await fs.writeFile(path.join(root,
|
|
98
|
+
await fs.writeFile(path.join(root, ".workspace-root"), "\n");
|
|
96
99
|
const found1 = await findProjectRoot(nested);
|
|
97
|
-
assertEqual(found1, root,
|
|
100
|
+
assertEqual(found1, root, "sentinel .workspace-root should win");
|
|
98
101
|
|
|
99
|
-
await fs.remove(path.join(root,
|
|
100
|
-
await fs.writeFile(path.join(root,
|
|
102
|
+
await fs.remove(path.join(root, ".workspace-root"));
|
|
103
|
+
await fs.writeFile(path.join(root, ".repo-root"), "\n");
|
|
101
104
|
const found2 = await findProjectRoot(nested);
|
|
102
|
-
assertEqual(found2, root,
|
|
103
|
-
return { name:
|
|
105
|
+
assertEqual(found2, root, "sentinel .repo-root should win");
|
|
106
|
+
return { name: "other-sentinels", ok: true };
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
async function testGitCliAndMarker() {
|
|
107
|
-
const hasGit = await cmdAvailable(
|
|
108
|
-
if (!hasGit) return { name:
|
|
110
|
+
const hasGit = await cmdAvailable("git");
|
|
111
|
+
if (!hasGit) return { name: "git-cli", ok: true, skipped: true };
|
|
109
112
|
|
|
110
|
-
const root = await mkTmpDir(
|
|
111
|
-
const nested = path.join(root,
|
|
113
|
+
const root = await mkTmpDir("git");
|
|
114
|
+
const nested = path.join(root, "pkg", "src");
|
|
112
115
|
await fs.ensureDir(nested);
|
|
113
|
-
await execFileAsync(
|
|
116
|
+
await execFileAsync("git", ["init"], { cwd: root, timeout: 2000 });
|
|
114
117
|
const found = await findProjectRoot(nested);
|
|
115
|
-
await assertEqual(found, root,
|
|
116
|
-
return { name:
|
|
118
|
+
await assertEqual(found, root, "git toplevel should be detected");
|
|
119
|
+
return { name: "git-cli", ok: true };
|
|
117
120
|
}
|
|
118
121
|
|
|
119
122
|
async function testHgMarkerOrCli() {
|
|
120
123
|
// Prefer simple marker test to avoid requiring Mercurial install
|
|
121
|
-
const root = await mkTmpDir(
|
|
122
|
-
const nested = path.join(root,
|
|
124
|
+
const root = await mkTmpDir("hg");
|
|
125
|
+
const nested = path.join(root, "lib");
|
|
123
126
|
await fs.ensureDir(nested);
|
|
124
|
-
await fs.ensureDir(path.join(root,
|
|
127
|
+
await fs.ensureDir(path.join(root, ".hg"));
|
|
125
128
|
const found = await findProjectRoot(nested);
|
|
126
|
-
await assertEqual(found, root,
|
|
127
|
-
return { name:
|
|
129
|
+
await assertEqual(found, root, ".hg marker should be detected");
|
|
130
|
+
return { name: "hg-marker", ok: true };
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
async function testWorkspacePnpm() {
|
|
131
|
-
const root = await mkTmpDir(
|
|
132
|
-
const pkgA = path.join(root,
|
|
134
|
+
const root = await mkTmpDir("pnpm-workspace");
|
|
135
|
+
const pkgA = path.join(root, "packages", "a");
|
|
133
136
|
await fs.ensureDir(pkgA);
|
|
134
|
-
await fs.writeFile(
|
|
137
|
+
await fs.writeFile(
|
|
138
|
+
path.join(root, "pnpm-workspace.yaml"),
|
|
139
|
+
"packages:\n - packages/*\n",
|
|
140
|
+
);
|
|
135
141
|
const found = await findProjectRoot(pkgA);
|
|
136
|
-
await assertEqual(found, root,
|
|
137
|
-
return { name:
|
|
142
|
+
await assertEqual(found, root, "pnpm-workspace.yaml should be detected");
|
|
143
|
+
return { name: "pnpm-workspace", ok: true };
|
|
138
144
|
}
|
|
139
145
|
|
|
140
146
|
async function testPackageJsonWorkspaces() {
|
|
141
|
-
const root = await mkTmpDir(
|
|
142
|
-
const pkgA = path.join(root,
|
|
147
|
+
const root = await mkTmpDir("package-workspaces");
|
|
148
|
+
const pkgA = path.join(root, "packages", "a");
|
|
143
149
|
await fs.ensureDir(pkgA);
|
|
144
150
|
await fs.writeJson(
|
|
145
|
-
path.join(root,
|
|
146
|
-
{ private: true, workspaces: [
|
|
151
|
+
path.join(root, "package.json"),
|
|
152
|
+
{ private: true, workspaces: ["packages/*"] },
|
|
147
153
|
{ spaces: 2 },
|
|
148
154
|
);
|
|
149
155
|
const found = await findProjectRoot(pkgA);
|
|
150
|
-
await assertEqual(found, root,
|
|
151
|
-
return { name:
|
|
156
|
+
await assertEqual(found, root, "package.json workspaces should be detected");
|
|
157
|
+
return { name: "package.json-workspaces", ok: true };
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
async function testLockfiles() {
|
|
155
|
-
const root = await mkTmpDir(
|
|
156
|
-
const nested = path.join(root,
|
|
161
|
+
const root = await mkTmpDir("lockfiles");
|
|
162
|
+
const nested = path.join(root, "src");
|
|
157
163
|
await fs.ensureDir(nested);
|
|
158
|
-
await fs.writeFile(path.join(root,
|
|
164
|
+
await fs.writeFile(path.join(root, "yarn.lock"), "\n");
|
|
159
165
|
const found = await findProjectRoot(nested);
|
|
160
|
-
await assertEqual(found, root,
|
|
161
|
-
return { name:
|
|
166
|
+
await assertEqual(found, root, "yarn.lock should be detected");
|
|
167
|
+
return { name: "lockfiles", ok: true };
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
async function testLanguageConfigs() {
|
|
165
|
-
const root = await mkTmpDir(
|
|
166
|
-
const nested = path.join(root,
|
|
171
|
+
const root = await mkTmpDir("lang-configs");
|
|
172
|
+
const nested = path.join(root, "x", "y");
|
|
167
173
|
await fs.ensureDir(nested);
|
|
168
|
-
await fs.writeFile(
|
|
174
|
+
await fs.writeFile(
|
|
175
|
+
path.join(root, "pyproject.toml"),
|
|
176
|
+
"[tool.poetry]\nname='tmp'\n",
|
|
177
|
+
);
|
|
169
178
|
const found = await findProjectRoot(nested);
|
|
170
|
-
await assertEqual(found, root,
|
|
171
|
-
return { name:
|
|
179
|
+
await assertEqual(found, root, "pyproject.toml should be detected");
|
|
180
|
+
return { name: "language-configs", ok: true };
|
|
172
181
|
}
|
|
173
182
|
|
|
174
183
|
async function testPreferOuterOnTie() {
|
|
175
|
-
const root = await mkTmpDir(
|
|
176
|
-
const mid = path.join(root,
|
|
177
|
-
const leaf = path.join(mid,
|
|
184
|
+
const root = await mkTmpDir("tie");
|
|
185
|
+
const mid = path.join(root, "mid");
|
|
186
|
+
const leaf = path.join(mid, "leaf");
|
|
178
187
|
await fs.ensureDir(leaf);
|
|
179
188
|
// same weight marker at two levels
|
|
180
|
-
await fs.writeFile(path.join(root,
|
|
181
|
-
await fs.writeFile(path.join(mid,
|
|
189
|
+
await fs.writeFile(path.join(root, "requirements.txt"), "\n");
|
|
190
|
+
await fs.writeFile(path.join(mid, "requirements.txt"), "\n");
|
|
182
191
|
const found = await findProjectRoot(leaf);
|
|
183
|
-
await assertEqual(
|
|
184
|
-
|
|
192
|
+
await assertEqual(
|
|
193
|
+
found,
|
|
194
|
+
root,
|
|
195
|
+
"outermost directory should win on equal weight",
|
|
196
|
+
);
|
|
197
|
+
return { name: "prefer-outermost-tie", ok: true };
|
|
185
198
|
}
|
|
186
199
|
|
|
187
200
|
// Additional coverage: Bazel, Nx/Turbo/Rush, Go workspaces, Deno, Java/Scala, PHP, Rust, Nix, Changesets, env markers,
|
|
188
201
|
// and priority interaction between package.json and lockfiles.
|
|
189
202
|
|
|
190
203
|
async function testBazelWorkspace() {
|
|
191
|
-
const root = await mkTmpDir(
|
|
192
|
-
const nested = path.join(root,
|
|
204
|
+
const root = await mkTmpDir("bazel");
|
|
205
|
+
const nested = path.join(root, "apps", "svc");
|
|
193
206
|
await fs.ensureDir(nested);
|
|
194
|
-
await fs.writeFile(path.join(root,
|
|
207
|
+
await fs.writeFile(path.join(root, "WORKSPACE"), 'workspace(name="tmp")\n');
|
|
195
208
|
const found = await findProjectRoot(nested);
|
|
196
|
-
await assertEqual(found, root,
|
|
197
|
-
return { name:
|
|
209
|
+
await assertEqual(found, root, "Bazel WORKSPACE should be detected");
|
|
210
|
+
return { name: "bazel-workspace", ok: true };
|
|
198
211
|
}
|
|
199
212
|
|
|
200
213
|
async function testNx() {
|
|
201
|
-
const root = await mkTmpDir(
|
|
202
|
-
const nested = path.join(root,
|
|
214
|
+
const root = await mkTmpDir("nx");
|
|
215
|
+
const nested = path.join(root, "apps", "web");
|
|
203
216
|
await fs.ensureDir(nested);
|
|
204
|
-
await fs.writeJson(
|
|
217
|
+
await fs.writeJson(
|
|
218
|
+
path.join(root, "nx.json"),
|
|
219
|
+
{ npmScope: "tmp" },
|
|
220
|
+
{ spaces: 2 },
|
|
221
|
+
);
|
|
205
222
|
const found = await findProjectRoot(nested);
|
|
206
|
-
await assertEqual(found, root,
|
|
207
|
-
return { name:
|
|
223
|
+
await assertEqual(found, root, "nx.json should be detected");
|
|
224
|
+
return { name: "nx", ok: true };
|
|
208
225
|
}
|
|
209
226
|
|
|
210
227
|
async function testTurbo() {
|
|
211
|
-
const root = await mkTmpDir(
|
|
212
|
-
const nested = path.join(root,
|
|
228
|
+
const root = await mkTmpDir("turbo");
|
|
229
|
+
const nested = path.join(root, "packages", "x");
|
|
213
230
|
await fs.ensureDir(nested);
|
|
214
|
-
await fs.writeJson(
|
|
231
|
+
await fs.writeJson(
|
|
232
|
+
path.join(root, "turbo.json"),
|
|
233
|
+
{ pipeline: {} },
|
|
234
|
+
{ spaces: 2 },
|
|
235
|
+
);
|
|
215
236
|
const found = await findProjectRoot(nested);
|
|
216
|
-
await assertEqual(found, root,
|
|
217
|
-
return { name:
|
|
237
|
+
await assertEqual(found, root, "turbo.json should be detected");
|
|
238
|
+
return { name: "turbo", ok: true };
|
|
218
239
|
}
|
|
219
240
|
|
|
220
241
|
async function testRush() {
|
|
221
|
-
const root = await mkTmpDir(
|
|
222
|
-
const nested = path.join(root,
|
|
242
|
+
const root = await mkTmpDir("rush");
|
|
243
|
+
const nested = path.join(root, "apps", "a");
|
|
223
244
|
await fs.ensureDir(nested);
|
|
224
|
-
await fs.writeJson(
|
|
245
|
+
await fs.writeJson(
|
|
246
|
+
path.join(root, "rush.json"),
|
|
247
|
+
{ projectFolderMinDepth: 1 },
|
|
248
|
+
{ spaces: 2 },
|
|
249
|
+
);
|
|
225
250
|
const found = await findProjectRoot(nested);
|
|
226
|
-
await assertEqual(found, root,
|
|
227
|
-
return { name:
|
|
251
|
+
await assertEqual(found, root, "rush.json should be detected");
|
|
252
|
+
return { name: "rush", ok: true };
|
|
228
253
|
}
|
|
229
254
|
|
|
230
255
|
async function testGoWorkAndMod() {
|
|
231
|
-
const root = await mkTmpDir(
|
|
232
|
-
const mod = path.join(root,
|
|
233
|
-
const nested = path.join(mod,
|
|
256
|
+
const root = await mkTmpDir("gowork");
|
|
257
|
+
const mod = path.join(root, "modA");
|
|
258
|
+
const nested = path.join(mod, "pkg");
|
|
234
259
|
await fs.ensureDir(nested);
|
|
235
|
-
await fs.writeFile(path.join(root,
|
|
236
|
-
await fs.writeFile(
|
|
260
|
+
await fs.writeFile(path.join(root, "go.work"), "go 1.22\nuse ./modA\n");
|
|
261
|
+
await fs.writeFile(
|
|
262
|
+
path.join(mod, "go.mod"),
|
|
263
|
+
"module example.com/a\ngo 1.22\n",
|
|
264
|
+
);
|
|
237
265
|
const found = await findProjectRoot(nested);
|
|
238
|
-
await assertEqual(found, root,
|
|
239
|
-
return { name:
|
|
266
|
+
await assertEqual(found, root, "go.work should define the workspace root");
|
|
267
|
+
return { name: "go-work", ok: true };
|
|
240
268
|
}
|
|
241
269
|
|
|
242
270
|
async function testDenoJson() {
|
|
243
|
-
const root = await mkTmpDir(
|
|
244
|
-
const nested = path.join(root,
|
|
271
|
+
const root = await mkTmpDir("deno");
|
|
272
|
+
const nested = path.join(root, "src");
|
|
245
273
|
await fs.ensureDir(nested);
|
|
246
|
-
await fs.writeJson(
|
|
274
|
+
await fs.writeJson(
|
|
275
|
+
path.join(root, "deno.json"),
|
|
276
|
+
{ tasks: {} },
|
|
277
|
+
{ spaces: 2 },
|
|
278
|
+
);
|
|
247
279
|
const found = await findProjectRoot(nested);
|
|
248
|
-
await assertEqual(found, root,
|
|
249
|
-
return { name:
|
|
280
|
+
await assertEqual(found, root, "deno.json should be detected");
|
|
281
|
+
return { name: "deno-json", ok: true };
|
|
250
282
|
}
|
|
251
283
|
|
|
252
284
|
async function testGradleSettings() {
|
|
253
|
-
const root = await mkTmpDir(
|
|
254
|
-
const nested = path.join(root,
|
|
285
|
+
const root = await mkTmpDir("gradle");
|
|
286
|
+
const nested = path.join(root, "app");
|
|
255
287
|
await fs.ensureDir(nested);
|
|
256
|
-
await fs.writeFile(
|
|
288
|
+
await fs.writeFile(
|
|
289
|
+
path.join(root, "settings.gradle"),
|
|
290
|
+
"rootProject.name='tmp'\n",
|
|
291
|
+
);
|
|
257
292
|
const found = await findProjectRoot(nested);
|
|
258
|
-
await assertEqual(found, root,
|
|
259
|
-
return { name:
|
|
293
|
+
await assertEqual(found, root, "settings.gradle should be detected");
|
|
294
|
+
return { name: "gradle-settings", ok: true };
|
|
260
295
|
}
|
|
261
296
|
|
|
262
297
|
async function testMavenPom() {
|
|
263
|
-
const root = await mkTmpDir(
|
|
264
|
-
const nested = path.join(root,
|
|
298
|
+
const root = await mkTmpDir("maven");
|
|
299
|
+
const nested = path.join(root, "module");
|
|
265
300
|
await fs.ensureDir(nested);
|
|
266
|
-
await fs.writeFile(path.join(root,
|
|
301
|
+
await fs.writeFile(path.join(root, "pom.xml"), "<project></project>\n");
|
|
267
302
|
const found = await findProjectRoot(nested);
|
|
268
|
-
await assertEqual(found, root,
|
|
269
|
-
return { name:
|
|
303
|
+
await assertEqual(found, root, "pom.xml should be detected");
|
|
304
|
+
return { name: "maven-pom", ok: true };
|
|
270
305
|
}
|
|
271
306
|
|
|
272
307
|
async function testSbtBuild() {
|
|
273
|
-
const root = await mkTmpDir(
|
|
274
|
-
const nested = path.join(root,
|
|
308
|
+
const root = await mkTmpDir("sbt");
|
|
309
|
+
const nested = path.join(root, "sub");
|
|
275
310
|
await fs.ensureDir(nested);
|
|
276
|
-
await fs.writeFile(path.join(root,
|
|
311
|
+
await fs.writeFile(path.join(root, "build.sbt"), 'name := "tmp"\n');
|
|
277
312
|
const found = await findProjectRoot(nested);
|
|
278
|
-
await assertEqual(found, root,
|
|
279
|
-
return { name:
|
|
313
|
+
await assertEqual(found, root, "build.sbt should be detected");
|
|
314
|
+
return { name: "sbt-build", ok: true };
|
|
280
315
|
}
|
|
281
316
|
|
|
282
317
|
async function testComposer() {
|
|
283
|
-
const root = await mkTmpDir(
|
|
284
|
-
const nested = path.join(root,
|
|
318
|
+
const root = await mkTmpDir("composer");
|
|
319
|
+
const nested = path.join(root, "src");
|
|
285
320
|
await fs.ensureDir(nested);
|
|
286
|
-
await fs.writeJson(
|
|
287
|
-
|
|
321
|
+
await fs.writeJson(
|
|
322
|
+
path.join(root, "composer.json"),
|
|
323
|
+
{ name: "tmp/pkg" },
|
|
324
|
+
{ spaces: 2 },
|
|
325
|
+
);
|
|
326
|
+
await fs.writeFile(path.join(root, "composer.lock"), "{}\n");
|
|
288
327
|
const found = await findProjectRoot(nested);
|
|
289
|
-
await assertEqual(found, root,
|
|
290
|
-
return { name:
|
|
328
|
+
await assertEqual(found, root, "composer.{json,lock} should be detected");
|
|
329
|
+
return { name: "composer", ok: true };
|
|
291
330
|
}
|
|
292
331
|
|
|
293
332
|
async function testCargo() {
|
|
294
|
-
const root = await mkTmpDir(
|
|
295
|
-
const nested = path.join(root,
|
|
333
|
+
const root = await mkTmpDir("cargo");
|
|
334
|
+
const nested = path.join(root, "src");
|
|
296
335
|
await fs.ensureDir(nested);
|
|
297
|
-
await fs.writeFile(
|
|
336
|
+
await fs.writeFile(
|
|
337
|
+
path.join(root, "Cargo.toml"),
|
|
338
|
+
"[package]\nname='tmp'\nversion='0.0.0'\n",
|
|
339
|
+
);
|
|
298
340
|
const found = await findProjectRoot(nested);
|
|
299
|
-
await assertEqual(found, root,
|
|
300
|
-
return { name:
|
|
341
|
+
await assertEqual(found, root, "Cargo.toml should be detected");
|
|
342
|
+
return { name: "cargo", ok: true };
|
|
301
343
|
}
|
|
302
344
|
|
|
303
345
|
async function testNixFlake() {
|
|
304
|
-
const root = await mkTmpDir(
|
|
305
|
-
const nested = path.join(root,
|
|
346
|
+
const root = await mkTmpDir("nix");
|
|
347
|
+
const nested = path.join(root, "work");
|
|
306
348
|
await fs.ensureDir(nested);
|
|
307
|
-
await fs.writeFile(path.join(root,
|
|
349
|
+
await fs.writeFile(path.join(root, "flake.nix"), "{ }\n");
|
|
308
350
|
const found = await findProjectRoot(nested);
|
|
309
|
-
await assertEqual(found, root,
|
|
310
|
-
return { name:
|
|
351
|
+
await assertEqual(found, root, "flake.nix should be detected");
|
|
352
|
+
return { name: "nix-flake", ok: true };
|
|
311
353
|
}
|
|
312
354
|
|
|
313
355
|
async function testChangesetConfig() {
|
|
314
|
-
const root = await mkTmpDir(
|
|
315
|
-
const nested = path.join(root,
|
|
356
|
+
const root = await mkTmpDir("changeset");
|
|
357
|
+
const nested = path.join(root, "pkg");
|
|
316
358
|
await fs.ensureDir(nested);
|
|
317
|
-
await fs.ensureDir(path.join(root,
|
|
359
|
+
await fs.ensureDir(path.join(root, ".changeset"));
|
|
318
360
|
await fs.writeJson(
|
|
319
|
-
path.join(root,
|
|
320
|
-
{ $schema:
|
|
361
|
+
path.join(root, ".changeset", "config.json"),
|
|
362
|
+
{ $schema: "https://unpkg.com/@changesets/config@2.3.1/schema.json" },
|
|
321
363
|
{ spaces: 2 },
|
|
322
364
|
);
|
|
323
365
|
const found = await findProjectRoot(nested);
|
|
324
|
-
await assertEqual(found, root,
|
|
325
|
-
return { name:
|
|
366
|
+
await assertEqual(found, root, ".changeset/config.json should be detected");
|
|
367
|
+
return { name: "changesets", ok: true };
|
|
326
368
|
}
|
|
327
369
|
|
|
328
370
|
async function testEnvCustomMarker() {
|
|
329
|
-
const root = await mkTmpDir(
|
|
330
|
-
const nested = path.join(root,
|
|
371
|
+
const root = await mkTmpDir("env-marker");
|
|
372
|
+
const nested = path.join(root, "dir");
|
|
331
373
|
await fs.ensureDir(nested);
|
|
332
|
-
await fs.writeFile(path.join(root,
|
|
374
|
+
await fs.writeFile(path.join(root, "MY_ROOT"), "\n");
|
|
333
375
|
const prev = process.env.PROJECT_ROOT_MARKERS;
|
|
334
|
-
process.env.PROJECT_ROOT_MARKERS =
|
|
376
|
+
process.env.PROJECT_ROOT_MARKERS = "MY_ROOT";
|
|
335
377
|
try {
|
|
336
378
|
const found = await findProjectRoot(nested);
|
|
337
|
-
await assertEqual(found, root,
|
|
379
|
+
await assertEqual(found, root, "custom env marker should be honored");
|
|
338
380
|
} finally {
|
|
339
381
|
if (prev === undefined) delete process.env.PROJECT_ROOT_MARKERS;
|
|
340
382
|
else process.env.PROJECT_ROOT_MARKERS = prev;
|
|
341
383
|
}
|
|
342
|
-
return { name:
|
|
384
|
+
return { name: "env-custom-marker", ok: true };
|
|
343
385
|
}
|
|
344
386
|
|
|
345
387
|
async function testPackageLowPriorityVsLock() {
|
|
346
|
-
const root = await mkTmpDir(
|
|
347
|
-
const nested = path.join(root,
|
|
348
|
-
await fs.ensureDir(path.join(nested,
|
|
349
|
-
await fs.writeJson(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
388
|
+
const root = await mkTmpDir("pkg-vs-lock");
|
|
389
|
+
const nested = path.join(root, "nested");
|
|
390
|
+
await fs.ensureDir(path.join(nested, "deep"));
|
|
391
|
+
await fs.writeJson(
|
|
392
|
+
path.join(nested, "package.json"),
|
|
393
|
+
{ name: "nested" },
|
|
394
|
+
{ spaces: 2 },
|
|
395
|
+
);
|
|
396
|
+
await fs.writeFile(path.join(root, "yarn.lock"), "\n");
|
|
397
|
+
const found = await findProjectRoot(path.join(nested, "deep"));
|
|
398
|
+
await assertEqual(
|
|
399
|
+
found,
|
|
400
|
+
root,
|
|
401
|
+
"lockfile at root should outrank nested package.json",
|
|
402
|
+
);
|
|
403
|
+
return { name: "package-vs-lock-priority", ok: true };
|
|
354
404
|
}
|
|
355
405
|
|
|
356
406
|
async function run() {
|
|
@@ -389,17 +439,22 @@ async function run() {
|
|
|
389
439
|
try {
|
|
390
440
|
const r = await t();
|
|
391
441
|
results.push({ ...r, ok: true });
|
|
392
|
-
console.log(`✔ ${r.name}${r.skipped ?
|
|
442
|
+
console.log(`✔ ${r.name}${r.skipped ? " (skipped)" : ""}`);
|
|
393
443
|
} catch (error) {
|
|
394
|
-
console.error(
|
|
444
|
+
console.error(
|
|
445
|
+
`✖ ${t.name}:`,
|
|
446
|
+
error && error.message ? error.message : error,
|
|
447
|
+
);
|
|
395
448
|
results.push({ name: t.name, ok: false, error: String(error) });
|
|
396
449
|
}
|
|
397
450
|
}
|
|
398
451
|
|
|
399
452
|
const failed = results.filter((r) => !r.ok);
|
|
400
|
-
console.log(
|
|
453
|
+
console.log("\nSummary:");
|
|
401
454
|
for (const r of results) {
|
|
402
|
-
console.log(
|
|
455
|
+
console.log(
|
|
456
|
+
`- ${r.name}: ${r.ok ? "ok" : "FAIL"}${r.skipped ? " (skipped)" : ""}`,
|
|
457
|
+
);
|
|
403
458
|
}
|
|
404
459
|
|
|
405
460
|
if (failed.length > 0) {
|
|
@@ -408,6 +463,6 @@ async function run() {
|
|
|
408
463
|
}
|
|
409
464
|
|
|
410
465
|
run().catch((error) => {
|
|
411
|
-
console.error(
|
|
466
|
+
console.error("Fatal error:", error);
|
|
412
467
|
process.exit(1);
|
|
413
468
|
});
|