@mozole/cli 1.4.2 ā 2.0.0-b
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/LICENSE +16 -4
- package/README.md +85 -89
- package/dist/cli.d.ts +2 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +204 -157
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.d.ts +5 -8
- package/dist/commands/adopt.d.ts.map +1 -1
- package/dist/commands/adopt.js +49 -812
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -7
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +69 -377
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/new.d.ts +8 -10
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +32 -149
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/phase.d.ts +11 -0
- package/dist/commands/phase.d.ts.map +1 -0
- package/dist/commands/phase.js +106 -0
- package/dist/commands/phase.js.map +1 -0
- package/dist/commands/prototype.d.ts +6 -0
- package/dist/commands/prototype.d.ts.map +1 -0
- package/dist/commands/prototype.js +28 -0
- package/dist/commands/prototype.js.map +1 -0
- package/dist/commands/repomap.d.ts +6 -0
- package/dist/commands/repomap.d.ts.map +1 -0
- package/dist/commands/repomap.js +71 -0
- package/dist/commands/repomap.js.map +1 -0
- package/dist/commands/test.d.ts +6 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +115 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/ui.d.ts +1 -37
- package/dist/commands/ui.d.ts.map +1 -1
- package/dist/commands/ui.js +75 -317
- package/dist/commands/ui.js.map +1 -1
- package/dist/commands/verify.d.ts +12 -7
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +148 -65
- package/dist/commands/verify.js.map +1 -1
- package/dist/qa/a11y.d.ts +24 -0
- package/dist/qa/a11y.d.ts.map +1 -0
- package/dist/qa/a11y.js +124 -0
- package/dist/qa/a11y.js.map +1 -0
- package/dist/qa/breakpoints.d.ts +16 -0
- package/dist/qa/breakpoints.d.ts.map +1 -0
- package/dist/qa/breakpoints.js +45 -0
- package/dist/qa/breakpoints.js.map +1 -0
- package/dist/qa/contract.d.ts +21 -0
- package/dist/qa/contract.d.ts.map +1 -0
- package/dist/qa/contract.js +116 -0
- package/dist/qa/contract.js.map +1 -0
- package/dist/qa/index.d.ts +6 -0
- package/dist/qa/index.d.ts.map +1 -0
- package/dist/qa/index.js +6 -0
- package/dist/qa/index.js.map +1 -0
- package/dist/qa/probe.d.ts +48 -0
- package/dist/qa/probe.d.ts.map +1 -0
- package/dist/qa/probe.js +206 -0
- package/dist/qa/probe.js.map +1 -0
- package/dist/qa/runner.d.ts +27 -32
- package/dist/qa/runner.d.ts.map +1 -1
- package/dist/qa/runner.js +286 -336
- package/dist/qa/runner.js.map +1 -1
- package/dist/scaffold/agents.d.ts +5 -0
- package/dist/scaffold/agents.d.ts.map +1 -0
- package/dist/scaffold/agents.js +84 -0
- package/dist/scaffold/agents.js.map +1 -0
- package/dist/scaffold/backend.d.ts +6 -8
- package/dist/scaffold/backend.d.ts.map +1 -1
- package/dist/scaffold/backend.js +318 -199
- package/dist/scaffold/backend.js.map +1 -1
- package/dist/scaffold/design.d.ts +4 -3
- package/dist/scaffold/design.d.ts.map +1 -1
- package/dist/scaffold/design.js +38 -47
- package/dist/scaffold/design.js.map +1 -1
- package/dist/scaffold/flagship.d.ts +8 -0
- package/dist/scaffold/flagship.d.ts.map +1 -0
- package/dist/scaffold/flagship.js +337 -0
- package/dist/scaffold/flagship.js.map +1 -0
- package/dist/scaffold/index.d.ts +9 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +48 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/phases.d.ts +10 -0
- package/dist/scaffold/phases.d.ts.map +1 -0
- package/dist/scaffold/phases.js +165 -0
- package/dist/scaffold/phases.js.map +1 -0
- package/dist/scaffold/repomap.d.ts +6 -0
- package/dist/scaffold/repomap.d.ts.map +1 -0
- package/dist/scaffold/repomap.js +89 -0
- package/dist/scaffold/repomap.js.map +1 -0
- package/dist/scaffold/standard.d.ts +13 -0
- package/dist/scaffold/standard.d.ts.map +1 -0
- package/dist/scaffold/standard.js +371 -0
- package/dist/scaffold/standard.js.map +1 -0
- package/dist/scaffold/tokens.d.ts +3 -0
- package/dist/scaffold/tokens.d.ts.map +1 -0
- package/dist/scaffold/tokens.js +126 -0
- package/dist/scaffold/tokens.js.map +1 -0
- package/dist/server/manager.d.ts +23 -0
- package/dist/server/manager.d.ts.map +1 -0
- package/dist/server/manager.js +96 -0
- package/dist/server/manager.js.map +1 -0
- package/dist/utils/ai-trace.d.ts +8 -0
- package/dist/utils/ai-trace.d.ts.map +1 -0
- package/dist/utils/ai-trace.js +69 -0
- package/dist/utils/ai-trace.js.map +1 -0
- package/dist/utils/browser.d.ts +10 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +127 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/fs.d.ts +11 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +166 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +69 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/process.d.ts +21 -0
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +120 -0
- package/dist/utils/process.js.map +1 -0
- package/dist/version.d.ts +21 -36
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +21 -44
- package/dist/version.js.map +1 -1
- package/package.json +19 -38
- package/CHANGELOG.md +0 -30
- package/dist/commands/cce.d.ts +0 -7
- package/dist/commands/cce.d.ts.map +0 -1
- package/dist/commands/cce.js +0 -37
- package/dist/commands/cce.js.map +0 -1
- package/dist/commands/logs.d.ts +0 -3
- package/dist/commands/logs.d.ts.map +0 -1
- package/dist/commands/logs.js +0 -39
- package/dist/commands/logs.js.map +0 -1
- package/dist/commands/qa.d.ts +0 -12
- package/dist/commands/qa.d.ts.map +0 -1
- package/dist/commands/qa.js +0 -143
- package/dist/commands/qa.js.map +0 -1
- package/dist/commands/stats.d.ts +0 -37
- package/dist/commands/stats.d.ts.map +0 -1
- package/dist/commands/stats.js +0 -169
- package/dist/commands/stats.js.map +0 -1
- package/dist/config/project.d.ts +0 -51
- package/dist/config/project.d.ts.map +0 -1
- package/dist/config/project.js +0 -81
- package/dist/config/project.js.map +0 -1
- package/dist/context/cce.d.ts +0 -39
- package/dist/context/cce.d.ts.map +0 -1
- package/dist/context/cce.js +0 -455
- package/dist/context/cce.js.map +0 -1
- package/dist/governor/fingerprint.d.ts +0 -7
- package/dist/governor/fingerprint.d.ts.map +0 -1
- package/dist/governor/fingerprint.js +0 -129
- package/dist/governor/fingerprint.js.map +0 -1
- package/dist/governor/hook.d.ts +0 -13
- package/dist/governor/hook.d.ts.map +0 -1
- package/dist/governor/hook.js +0 -389
- package/dist/governor/hook.js.map +0 -1
- package/dist/governor/state.d.ts +0 -45
- package/dist/governor/state.d.ts.map +0 -1
- package/dist/governor/state.js +0 -60
- package/dist/governor/state.js.map +0 -1
- package/dist/integrations/antigravity.d.ts +0 -20
- package/dist/integrations/antigravity.d.ts.map +0 -1
- package/dist/integrations/antigravity.js +0 -153
- package/dist/integrations/antigravity.js.map +0 -1
- package/dist/output.d.ts +0 -5
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -2
- package/dist/output.js.map +0 -1
- package/dist/platform/executable.d.ts +0 -11
- package/dist/platform/executable.d.ts.map +0 -1
- package/dist/platform/executable.js +0 -69
- package/dist/platform/executable.js.map +0 -1
- package/dist/platform/process.d.ts +0 -25
- package/dist/platform/process.d.ts.map +0 -1
- package/dist/platform/process.js +0 -179
- package/dist/platform/process.js.map +0 -1
- package/dist/progress.d.ts +0 -44
- package/dist/progress.d.ts.map +0 -1
- package/dist/progress.js +0 -123
- package/dist/progress.js.map +0 -1
- package/dist/qa/diff.d.ts +0 -10
- package/dist/qa/diff.d.ts.map +0 -1
- package/dist/qa/diff.js +0 -91
- package/dist/qa/diff.js.map +0 -1
- package/dist/qa/server.d.ts +0 -10
- package/dist/qa/server.d.ts.map +0 -1
- package/dist/qa/server.js +0 -127
- package/dist/qa/server.js.map +0 -1
- package/dist/scaffold/adoption.d.ts +0 -17
- package/dist/scaffold/adoption.d.ts.map +0 -1
- package/dist/scaffold/adoption.js +0 -279
- package/dist/scaffold/adoption.js.map +0 -1
- package/dist/scaffold/legacy.d.ts +0 -4
- package/dist/scaffold/legacy.d.ts.map +0 -1
- package/dist/scaffold/legacy.js +0 -139
- package/dist/scaffold/legacy.js.map +0 -1
- package/dist/scaffold/routes.d.ts +0 -3
- package/dist/scaffold/routes.d.ts.map +0 -1
- package/dist/scaffold/routes.js +0 -37
- package/dist/scaffold/routes.js.map +0 -1
- package/dist/scaffold/standalone.d.ts +0 -7
- package/dist/scaffold/standalone.d.ts.map +0 -1
- package/dist/scaffold/standalone.js +0 -294
- package/dist/scaffold/standalone.js.map +0 -1
- package/dist/scaffold/templates.d.ts +0 -8
- package/dist/scaffold/templates.d.ts.map +0 -1
- package/dist/scaffold/templates.js +0 -312
- package/dist/scaffold/templates.js.map +0 -1
- package/dist/scaffold/write.d.ts +0 -2
- package/dist/scaffold/write.d.ts.map +0 -1
- package/dist/scaffold/write.js +0 -38
- package/dist/scaffold/write.js.map +0 -1
- package/dist/telemetry/store.d.ts +0 -33
- package/dist/telemetry/store.d.ts.map +0 -1
- package/dist/telemetry/store.js +0 -75
- package/dist/telemetry/store.js.map +0 -1
- package/dist/verify/runner.d.ts +0 -15
- package/dist/verify/runner.d.ts.map +0 -1
- package/dist/verify/runner.js +0 -112
- package/dist/verify/runner.js.map +0 -1
- package/docs/agent-audit.md +0 -24
package/dist/commands/adopt.js
CHANGED
|
@@ -1,815 +1,52 @@
|
|
|
1
|
-
import { access, copyFile, lstat, mkdir, mkdtemp, readdir, readFile, rename, rm, rmdir, writeFile, } from "node:fs/promises";
|
|
2
1
|
import path from "node:path";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
await
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"tests/setup.ts",
|
|
53
|
-
".mozole/project.json",
|
|
54
|
-
".mozole/toolchain.json",
|
|
55
|
-
".mozole/backups/package.json.before-adopt",
|
|
56
|
-
".mozole/backups/antigravity-mcp-config.json.before-adopt",
|
|
57
|
-
".mozole/backups/codex-hooks.json.before-adopt",
|
|
58
|
-
".mozole/backups/claude-settings.json.before-adopt",
|
|
59
|
-
".codex/hooks.json",
|
|
60
|
-
".claude/settings.json",
|
|
61
|
-
".agents/mcp_config.json",
|
|
62
|
-
".agents/rules/mozole.md",
|
|
63
|
-
".agents/workflows/mozole-verify.md",
|
|
64
|
-
".agents/workflows/mozole-qa.md",
|
|
65
|
-
".agents/workflows/mozole-qa-interactive.md",
|
|
66
|
-
".agents/workflows/mozole-qa-visual.md",
|
|
67
|
-
].map((relative) => path.join(root, ...relative.split("/")));
|
|
68
|
-
}
|
|
69
|
-
async function exists(file) {
|
|
70
|
-
try {
|
|
71
|
-
await access(file);
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async function atomicJson(file, value) {
|
|
79
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
80
|
-
const temporary = `${file}.${process.pid}.tmp`;
|
|
81
|
-
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
82
|
-
await rename(temporary, file);
|
|
83
|
-
}
|
|
84
|
-
export { detectRoutes } from "../scaffold/routes.js";
|
|
85
|
-
async function ensureGitignore(root, optimized = true) {
|
|
86
|
-
const file = path.join(root, ".gitignore");
|
|
87
|
-
const original = (await exists(file)) ? await readFile(file, "utf8") : "";
|
|
88
|
-
const current = optimized
|
|
89
|
-
? original
|
|
90
|
-
: original
|
|
91
|
-
.split(/\r?\n/)
|
|
92
|
-
.filter((line) => ![
|
|
93
|
-
".cce/",
|
|
94
|
-
"/.cce/",
|
|
95
|
-
".mozole/state/",
|
|
96
|
-
".mozole/telemetry.json",
|
|
97
|
-
].includes(line.trim()))
|
|
98
|
-
.join("\n");
|
|
99
|
-
const required = [
|
|
100
|
-
"dist/",
|
|
101
|
-
"build/",
|
|
102
|
-
"deploy/",
|
|
103
|
-
".mozole/logs/",
|
|
104
|
-
".mozole/qa/",
|
|
105
|
-
...(optimized ? [".mozole/state/", ".mozole/telemetry.json", ".cce/"] : []),
|
|
106
|
-
];
|
|
107
|
-
const existing = new Set(current.split(/\r?\n/).map((line) => line.trim()));
|
|
108
|
-
const missing = required.filter((line) => !existing.has(line));
|
|
109
|
-
if (!missing.length && current === original)
|
|
110
|
-
return;
|
|
111
|
-
await writeFiles(root, {
|
|
112
|
-
".gitignore": `${current.trimEnd()}${missing.length ? `${current.trim() ? "\n\n" : ""}# Mozole local files\n${missing.join("\n")}` : ""}\n`,
|
|
113
|
-
}, true);
|
|
114
|
-
}
|
|
115
|
-
async function ensureBiomeConfig(root, defaultTemplate, optimized = true) {
|
|
116
|
-
const file = path.join(root, "biome.json");
|
|
117
|
-
const requiredIgnores = [
|
|
118
|
-
"!build",
|
|
119
|
-
"!dist",
|
|
120
|
-
"!deploy",
|
|
121
|
-
"!.react-router",
|
|
122
|
-
"!node_modules",
|
|
123
|
-
"!coverage",
|
|
124
|
-
"!.mozole",
|
|
125
|
-
...(optimized ? ["!.cce"] : []),
|
|
126
|
-
"!public",
|
|
127
|
-
"!assets",
|
|
128
|
-
"!qa-screenshots",
|
|
129
|
-
];
|
|
130
|
-
if (!(await exists(file))) {
|
|
131
|
-
await atomicJson(file, JSON.parse(defaultTemplate));
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
try {
|
|
135
|
-
const content = await readFile(file, "utf8");
|
|
136
|
-
const config = JSON.parse(content);
|
|
137
|
-
if (!config || typeof config !== "object")
|
|
138
|
-
throw new Error("biome.json must contain an object");
|
|
139
|
-
config.$schema = JSON.parse(defaultTemplate).$schema;
|
|
140
|
-
if (Array.isArray(config.files?.ignore)) {
|
|
141
|
-
config.files.includes = [
|
|
142
|
-
...(config.files.includes ?? ["**"]),
|
|
143
|
-
...config.files.ignore.map((pattern) => pattern.startsWith("!") ? pattern : `!${pattern}`),
|
|
144
|
-
];
|
|
145
|
-
delete config.files.ignore;
|
|
146
|
-
}
|
|
147
|
-
if (!optimized && Array.isArray(config.files?.includes))
|
|
148
|
-
config.files.includes = config.files.includes.filter((pattern) => ![
|
|
149
|
-
"!.cce",
|
|
150
|
-
"!.cce/**",
|
|
151
|
-
"!.mozole/state",
|
|
152
|
-
"!.mozole/telemetry.json",
|
|
153
|
-
].includes(pattern));
|
|
154
|
-
if (!config.files || typeof config.files !== "object") {
|
|
155
|
-
config.files = {
|
|
156
|
-
includes: ["**", ...requiredIgnores],
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
else if (Array.isArray(config.files.includes)) {
|
|
160
|
-
const existing = new Set(config.files.includes);
|
|
161
|
-
for (const pattern of requiredIgnores) {
|
|
162
|
-
if (!existing.has(pattern)) {
|
|
163
|
-
config.files.includes.push(pattern);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
config.files.includes = ["**", ...requiredIgnores];
|
|
169
|
-
}
|
|
170
|
-
await atomicJson(file, config);
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
throw new Error(`Cannot migrate biome.json: ${String(error)}`);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
async function validateJsonObject(file) {
|
|
177
|
-
if (!(await exists(file)))
|
|
178
|
-
return false;
|
|
179
|
-
const value = JSON.parse(await readFile(file, "utf8"));
|
|
180
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
181
|
-
throw new Error(`Expected a JSON object in ${file}`);
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
async function mergeHookConfig(file, template, backupFile) {
|
|
185
|
-
const currentText = await readFile(file, "utf8");
|
|
186
|
-
const current = JSON.parse(currentText);
|
|
187
|
-
const desired = JSON.parse(template);
|
|
188
|
-
const hooks = current.hooks && typeof current.hooks === "object"
|
|
189
|
-
? current.hooks
|
|
190
|
-
: {};
|
|
191
|
-
current.hooks = hooks;
|
|
192
|
-
for (const [event, groups] of Object.entries(desired.hooks)) {
|
|
193
|
-
const existing = Array.isArray(hooks[event])
|
|
194
|
-
? hooks[event]
|
|
195
|
-
: [];
|
|
196
|
-
const preserved = existing.flatMap((group) => {
|
|
197
|
-
if (!Array.isArray(group.hooks))
|
|
198
|
-
return [group];
|
|
199
|
-
const remaining = group.hooks.filter((handler) => !JSON.stringify(handler).includes("mozole governor hook"));
|
|
200
|
-
return remaining.length || !group.hooks.length
|
|
201
|
-
? [{ ...group, hooks: remaining }]
|
|
202
|
-
: [];
|
|
203
|
-
});
|
|
204
|
-
hooks[event] = [...preserved, ...groups];
|
|
205
|
-
}
|
|
206
|
-
if (JSON.stringify(current) === JSON.stringify(JSON.parse(currentText)))
|
|
207
|
-
return;
|
|
208
|
-
await mkdir(path.dirname(backupFile), { recursive: true });
|
|
209
|
-
await copyFile(file, backupFile);
|
|
210
|
-
await atomicJson(file, current);
|
|
211
|
-
}
|
|
212
|
-
export async function adoptCommand(input, cwd, output = console, options = {}) {
|
|
213
|
-
if (!input) {
|
|
214
|
-
output.error("Usage: mozole adopt <path>");
|
|
215
|
-
return 2;
|
|
216
|
-
}
|
|
217
|
-
const root = path.resolve(cwd, input);
|
|
218
|
-
if (!options.cceInstall)
|
|
219
|
-
return adoptPlain(root, output, options);
|
|
220
|
-
const packagePath = path.join(root, "package.json");
|
|
221
|
-
const progress = new TaskProgress({
|
|
222
|
-
total: 5,
|
|
223
|
-
title: "Adopt",
|
|
224
|
-
verbose: options.verbose,
|
|
225
|
-
output,
|
|
226
|
-
onUpdate: options.onProgress,
|
|
227
|
-
});
|
|
228
|
-
const controller = new AbortController();
|
|
229
|
-
const abort = () => controller.abort();
|
|
230
|
-
process.once("SIGINT", abort);
|
|
231
|
-
process.once("SIGTERM", abort);
|
|
232
|
-
let snapshots;
|
|
233
|
-
let movedDirectories = [];
|
|
234
|
-
try {
|
|
235
|
-
progress.step("Analyzing project structure and package metadata");
|
|
236
|
-
progress.logVerbose(`Reading package.json from ${packagePath}`);
|
|
237
|
-
await validateManagedPaths(root);
|
|
238
|
-
const original = await readFile(packagePath, "utf8");
|
|
239
|
-
const packageJson = JSON.parse(original);
|
|
240
|
-
await assertAntigravityMcpMergeable(root);
|
|
241
|
-
const allDependencies = {
|
|
242
|
-
...packageJson.dependencies,
|
|
243
|
-
...packageJson.devDependencies,
|
|
244
|
-
};
|
|
245
|
-
if (!allDependencies.react ||
|
|
246
|
-
(!allDependencies.vite && !allDependencies["@react-router/dev"]))
|
|
247
|
-
throw new Error("Expected a React project using Vite or React Router Framework Mode.");
|
|
248
|
-
const name = packageJson.name ?? path.basename(root);
|
|
249
|
-
let templates = projectTemplates(name, { scaffold: false });
|
|
250
|
-
const codexHooksPath = path.join(root, ".codex", "hooks.json");
|
|
251
|
-
const claudeSettingsPath = path.join(root, ".claude", "settings.json");
|
|
252
|
-
const hadCodexHooks = await validateJsonObject(codexHooksPath);
|
|
253
|
-
const hadClaudeSettings = await validateJsonObject(claudeSettingsPath);
|
|
254
|
-
const configPath = path.join(root, ".mozole", "project.json");
|
|
255
|
-
const config = (await exists(configPath))
|
|
256
|
-
? await (async () => {
|
|
257
|
-
try {
|
|
258
|
-
return await readConfig(root);
|
|
259
|
-
}
|
|
260
|
-
catch (error) {
|
|
261
|
-
throw new Error(`Existing .mozole/project.json is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
262
|
-
}
|
|
263
|
-
})()
|
|
264
|
-
: projectConfig(name, { scaffold: false });
|
|
265
|
-
const wasManaged = await exists(configPath);
|
|
266
|
-
if (!config.backend && (await exists(path.join(root, "api/index.php"))))
|
|
267
|
-
config.backend = projectConfig(name, { backend: true }).backend;
|
|
268
|
-
templates = projectTemplates(name, {
|
|
269
|
-
scaffold: false,
|
|
270
|
-
backend: Boolean(config.backend),
|
|
271
|
-
});
|
|
272
|
-
adaptFrameworkGuide(templates, Boolean(allDependencies["@react-router/dev"]));
|
|
273
|
-
const migration = await canonicalPlan(root, templates, true, wasManaged, name, knownGuides(name));
|
|
274
|
-
config.name = name;
|
|
275
|
-
for (const key of Object.keys(config))
|
|
276
|
-
if (![
|
|
277
|
-
"schemaVersion",
|
|
278
|
-
"name",
|
|
279
|
-
"mode",
|
|
280
|
-
"framework",
|
|
281
|
-
"routes",
|
|
282
|
-
"backend",
|
|
283
|
-
"qa",
|
|
284
|
-
"cce",
|
|
285
|
-
"governor",
|
|
286
|
-
"telemetry",
|
|
287
|
-
"verifiers",
|
|
288
|
-
].includes(key))
|
|
289
|
-
delete config[key];
|
|
290
|
-
const optimized = projectConfig(name, { scaffold: false });
|
|
291
|
-
config.mode = "optimized";
|
|
292
|
-
if (!optimized.cce || !optimized.governor || !optimized.telemetry)
|
|
293
|
-
throw new Error("Missing optimized defaults");
|
|
294
|
-
config.cce = {
|
|
295
|
-
...optimized.cce,
|
|
296
|
-
initialized: config.cce?.initialized ?? false,
|
|
297
|
-
};
|
|
298
|
-
config.governor = optimized.governor;
|
|
299
|
-
config.telemetry = optimized.telemetry;
|
|
300
|
-
progress.step("Verifying CCE runtime availability");
|
|
301
|
-
const cceRequired = process.env.MOZOLE_SKIP_CCE !== "1" && config.cce?.enabled;
|
|
302
|
-
const cce = cceRequired
|
|
303
|
-
? await requireCceRuntime(root, controller.signal)
|
|
304
|
-
: await cceCommand();
|
|
305
|
-
const cceLauncher = cce?.prefix.length ? "uvx" : "cce";
|
|
306
|
-
progress.logVerbose(`CCE runtime: launcher=${cceLauncher}`);
|
|
307
|
-
snapshots = await snapshotFiles([
|
|
308
|
-
...new Set([
|
|
309
|
-
...adoptionMutationFiles(root),
|
|
310
|
-
...Object.keys(migration.writes).map((f) => path.join(root, f)),
|
|
311
|
-
...migration.removes.map((f) => path.join(root, f)),
|
|
312
|
-
]),
|
|
313
|
-
]);
|
|
314
|
-
const routeSourcePath = path.join(root, "app", "routes.ts");
|
|
315
|
-
if (await exists(routeSourcePath))
|
|
316
|
-
config.routes = detectRoutes(await readFile(routeSourcePath, "utf8"));
|
|
317
|
-
const desiredScripts = {
|
|
318
|
-
lint: "biome check .",
|
|
319
|
-
format: "biome format --write .",
|
|
320
|
-
typecheck: !packageJson.scripts?.typecheck ||
|
|
321
|
-
packageJson.scripts.typecheck === "tsc --noEmit"
|
|
322
|
-
? allDependencies["@react-router/dev"]
|
|
323
|
-
? "react-router typegen && tsc --noEmit"
|
|
324
|
-
: "tsc --noEmit"
|
|
325
|
-
: packageJson.scripts.typecheck,
|
|
326
|
-
"test:run": packageJson.scripts?.["test:run"] ?? "vitest run",
|
|
327
|
-
verify: "mozole verify",
|
|
328
|
-
"verify:force": "mozole verify --force",
|
|
329
|
-
qa: "mozole qa",
|
|
330
|
-
"qa:force": "mozole qa --force",
|
|
331
|
-
"qa:interactive": "mozole qa --interactive",
|
|
332
|
-
"qa:visual": "mozole qa --visual",
|
|
333
|
-
};
|
|
334
|
-
const next = structuredClone(packageJson);
|
|
335
|
-
next.scripts = {
|
|
336
|
-
...desiredScripts,
|
|
337
|
-
...packageJson.scripts,
|
|
338
|
-
verify: "mozole verify",
|
|
339
|
-
"verify:force": "mozole verify --force",
|
|
340
|
-
qa: "mozole qa",
|
|
341
|
-
"qa:force": "mozole qa --force",
|
|
342
|
-
"qa:interactive": "mozole qa --interactive",
|
|
343
|
-
"qa:visual": "mozole qa --visual",
|
|
344
|
-
};
|
|
345
|
-
next.scripts.typecheck = desiredScripts.typecheck;
|
|
346
|
-
if (config.backend)
|
|
347
|
-
for (const key of ["check:php", "build:deploy", "dev:api"])
|
|
348
|
-
next.scripts[key] = JSON.parse(required(templates, "package.json")).scripts[key];
|
|
349
|
-
next.devDependencies = {
|
|
350
|
-
"@mozole/cli": process.env.MOZOLE_CLI_SPEC ?? TOOLCHAIN.mozoleCli,
|
|
351
|
-
"@playwright/test": packageJson.devDependencies?.["@playwright/test"] ??
|
|
352
|
-
TOOLCHAIN.playwright,
|
|
353
|
-
"@biomejs/biome": packageJson.devDependencies?.["@biomejs/biome"] ?? TOOLCHAIN.biome,
|
|
354
|
-
"@testing-library/jest-dom": packageJson.devDependencies?.["@testing-library/jest-dom"] ??
|
|
355
|
-
TOOLCHAIN.testingLibraryJestDom,
|
|
356
|
-
"@testing-library/react": packageJson.devDependencies?.["@testing-library/react"] ??
|
|
357
|
-
TOOLCHAIN.testingLibraryReact,
|
|
358
|
-
"@testing-library/user-event": packageJson.devDependencies?.["@testing-library/user-event"] ??
|
|
359
|
-
TOOLCHAIN.testingLibraryUserEvent,
|
|
360
|
-
"@vitejs/plugin-react": packageJson.devDependencies?.["@vitejs/plugin-react"] ??
|
|
361
|
-
TOOLCHAIN.viteReact,
|
|
362
|
-
jsdom: packageJson.devDependencies?.jsdom ?? TOOLCHAIN.jsdom,
|
|
363
|
-
typescript: packageJson.devDependencies?.typescript ?? TOOLCHAIN.typescript,
|
|
364
|
-
vitest: packageJson.devDependencies?.vitest ?? TOOLCHAIN.vitest,
|
|
365
|
-
...packageJson.devDependencies,
|
|
366
|
-
};
|
|
367
|
-
const generatedPackage = JSON.parse(required(templates, "package.json"));
|
|
368
|
-
for (const key of [
|
|
369
|
-
"@mozole/cli",
|
|
370
|
-
"@biomejs/biome",
|
|
371
|
-
"@testing-library/jest-dom",
|
|
372
|
-
"@testing-library/react",
|
|
373
|
-
"@testing-library/user-event",
|
|
374
|
-
"@vitejs/plugin-react",
|
|
375
|
-
"jsdom",
|
|
376
|
-
"typescript",
|
|
377
|
-
"vitest",
|
|
378
|
-
])
|
|
379
|
-
next.devDependencies[key] = required(generatedPackage.devDependencies, key);
|
|
380
|
-
if (next.dependencies)
|
|
381
|
-
delete next.dependencies["@mozole/cli"];
|
|
382
|
-
const nextText = `${JSON.stringify(next, null, 2)}\n`;
|
|
383
|
-
const packageChanged = nextText !== original;
|
|
384
|
-
if (packageChanged) {
|
|
385
|
-
const backupDir = path.join(root, ".mozole", "backups");
|
|
386
|
-
await mkdir(backupDir, { recursive: true });
|
|
387
|
-
await copyFile(packagePath, path.join(backupDir, "package.json.before-adopt"));
|
|
388
|
-
await atomicJson(packagePath, next);
|
|
389
|
-
}
|
|
390
|
-
progress.step("Configuring gitignore, hooks & Antigravity MCP");
|
|
391
|
-
const template = (key) => {
|
|
392
|
-
const value = templates[key];
|
|
393
|
-
if (value === undefined)
|
|
394
|
-
throw new Error(`Missing internal template: ${key}`);
|
|
395
|
-
return value;
|
|
396
|
-
};
|
|
397
|
-
await ensureBiomeConfig(root, template("biome.json"));
|
|
398
|
-
await atomicJson(configPath, config);
|
|
399
|
-
await writeFiles(root, {
|
|
400
|
-
".mozole/toolchain.json": template(".mozole/toolchain.json"),
|
|
401
|
-
"docs/design/README.md": template("docs/design/README.md"),
|
|
402
|
-
".context-engine.yaml": template(".context-engine.yaml"),
|
|
403
|
-
"vitest.config.ts": template("vitest.config.ts"),
|
|
404
|
-
"tests/setup.ts": template("tests/setup.ts"),
|
|
405
|
-
".codex/hooks.json": template(".codex/hooks.json"),
|
|
406
|
-
".claude/settings.json": template(".claude/settings.json"),
|
|
407
|
-
});
|
|
408
|
-
await writeFiles(root, {
|
|
409
|
-
".agents/rules/mozole.md": template(".agents/rules/mozole.md"),
|
|
410
|
-
".agents/workflows/mozole-verify.md": template(".agents/workflows/mozole-verify.md"),
|
|
411
|
-
".agents/workflows/mozole-qa.md": template(".agents/workflows/mozole-qa.md"),
|
|
412
|
-
".agents/workflows/mozole-qa-interactive.md": template(".agents/workflows/mozole-qa-interactive.md"),
|
|
413
|
-
".agents/workflows/mozole-qa-visual.md": template(".agents/workflows/mozole-qa-visual.md"),
|
|
414
|
-
}, true);
|
|
415
|
-
const antigravityMcpPath = path.join(root, ".agents", "mcp_config.json");
|
|
416
|
-
const antigravityMcpBefore = (await exists(antigravityMcpPath))
|
|
417
|
-
? await readFile(antigravityMcpPath, "utf8")
|
|
418
|
-
: undefined;
|
|
419
|
-
const antigravityMcpWillChange = antigravityMcpBefore !== undefined &&
|
|
420
|
-
`${JSON.stringify(mergeAntigravityMcp(JSON.parse(antigravityMcpBefore), cceMcpServer(cceLauncher, cce?.command)), null, 2)}\n` !== antigravityMcpBefore;
|
|
421
|
-
const backupDir = path.join(root, ".mozole", "backups");
|
|
422
|
-
if (antigravityMcpWillChange && antigravityMcpBefore !== undefined) {
|
|
423
|
-
await mkdir(backupDir, { recursive: true });
|
|
424
|
-
await writeFile(path.join(backupDir, "antigravity-mcp-config.json.before-adopt"), antigravityMcpBefore, "utf8");
|
|
425
|
-
}
|
|
426
|
-
if (hadCodexHooks)
|
|
427
|
-
await mergeHookConfig(codexHooksPath, template(".codex/hooks.json"), path.join(backupDir, "codex-hooks.json.before-adopt"));
|
|
428
|
-
if (hadClaudeSettings)
|
|
429
|
-
await mergeHookConfig(claudeSettingsPath, template(".claude/settings.json"), path.join(backupDir, "claude-settings.json.before-adopt"));
|
|
430
|
-
await ensureGitignore(root);
|
|
431
|
-
movedDirectories = await applyCanonicalPlan(root, migration, snapshots);
|
|
432
|
-
progress.step("Installing missing dependencies & toolchain packages");
|
|
433
|
-
if (process.env.MOZOLE_SKIP_INSTALL !== "1") {
|
|
434
|
-
progress.logVerbose("Running npm install for adopted project");
|
|
435
|
-
const installed = await run(npmExecutable(), ["install", "--no-fund", "--no-audit"], {
|
|
436
|
-
cwd: root,
|
|
437
|
-
timeoutMs: 600_000,
|
|
438
|
-
signal: controller.signal,
|
|
439
|
-
logFile: path.join(root, ".mozole", "logs", "npm-install.log"),
|
|
440
|
-
});
|
|
441
|
-
if (installed.exitCode !== 0)
|
|
442
|
-
throw new Error("npm install failed; package.json backup is in .mozole/backups/.");
|
|
443
|
-
if (process.env.MOZOLE_SKIP_BROWSER !== "1") {
|
|
444
|
-
progress.logVerbose("Installing Playwright browser");
|
|
445
|
-
const browser = await run(npmExecutable(), ["exec", "--", "playwright", "install", "chromium"], {
|
|
446
|
-
cwd: root,
|
|
447
|
-
timeoutMs: 600_000,
|
|
448
|
-
signal: controller.signal,
|
|
449
|
-
logFile: path.join(root, ".mozole", "logs", "playwright-install.log"),
|
|
450
|
-
});
|
|
451
|
-
if (browser.exitCode !== 0)
|
|
452
|
-
throw new Error("Chromium installation failed; package.json backup is in .mozole/backups/.");
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
progress.step("Initializing CCE index and project configuration");
|
|
456
|
-
if (cceRequired && !config.cce?.initialized) {
|
|
457
|
-
progress.logVerbose("Initializing CCE for adopted project");
|
|
458
|
-
const initialized = await initializeCce(root, controller.signal, cce);
|
|
459
|
-
if (initialized?.exitCode !== 0) {
|
|
460
|
-
const detail = initialized
|
|
461
|
-
? (initialized.stderr || initialized.stdout)
|
|
462
|
-
.trim()
|
|
463
|
-
.split(/\r?\n/)[0]
|
|
464
|
-
?.slice(0, 240)
|
|
465
|
-
: undefined;
|
|
466
|
-
throw new Error(`CCE indexing failed.${detail ? ` ${detail}` : " See .mozole/logs/cce-init.log."}`);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
// CCE may create or update workspace agent files; restore Mozole's
|
|
470
|
-
// managed MCP entry after it finishes so Antigravity cannot lose CCE.
|
|
471
|
-
await configureAntigravityMcp(root, cceLauncher, cce?.command);
|
|
472
|
-
if (controller.signal.aborted)
|
|
473
|
-
throw new Error("Adoption cancelled.");
|
|
474
|
-
progress.done(`Adopted ${name}`);
|
|
475
|
-
output.log(`ADOPT PASS\n\n${root}\nApplication code preserved.${migrationSummary(migration)}`);
|
|
476
|
-
return 0;
|
|
477
|
-
}
|
|
478
|
-
catch (error) {
|
|
479
|
-
progress.done();
|
|
480
|
-
let rollbackFailure;
|
|
481
|
-
if (snapshots)
|
|
482
|
-
try {
|
|
483
|
-
await restoreDirectories(movedDirectories);
|
|
484
|
-
await restoreFiles(snapshots);
|
|
485
|
-
}
|
|
486
|
-
catch (rollbackError) {
|
|
487
|
-
rollbackFailure =
|
|
488
|
-
rollbackError instanceof Error
|
|
489
|
-
? rollbackError.message
|
|
490
|
-
: String(rollbackError);
|
|
491
|
-
}
|
|
492
|
-
output.error(`ADOPT FAIL\n\n${error instanceof Error ? error.message : String(error)}${rollbackFailure ? `\nRollback failed: ${rollbackFailure}` : "\nManaged project files restored."}`);
|
|
493
|
-
return controller.signal.aborted ? 130 : 1;
|
|
494
|
-
}
|
|
495
|
-
finally {
|
|
496
|
-
process.removeListener("SIGINT", abort);
|
|
497
|
-
process.removeListener("SIGTERM", abort);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
/** Plain adoption never probes or installs agent runtimes. Existing user integrations are preserved. */
|
|
501
|
-
async function adoptPlain(root, output, options) {
|
|
502
|
-
let snapshots;
|
|
503
|
-
let movedDirectories = [];
|
|
504
|
-
const controller = new AbortController();
|
|
505
|
-
const abort = () => controller.abort();
|
|
506
|
-
process.once("SIGINT", abort);
|
|
507
|
-
process.once("SIGTERM", abort);
|
|
508
|
-
const progress = new TaskProgress({
|
|
509
|
-
total: 3,
|
|
510
|
-
title: "Adopt",
|
|
511
|
-
output,
|
|
512
|
-
verbose: options.verbose,
|
|
513
|
-
onUpdate: options.onProgress,
|
|
514
|
-
});
|
|
515
|
-
try {
|
|
516
|
-
progress.step("Inspecting project and planning canonical migration");
|
|
517
|
-
await validateManagedPaths(root);
|
|
518
|
-
const packagePath = path.join(root, "package.json");
|
|
519
|
-
const pkg = JSON.parse(await readFile(packagePath, "utf8"));
|
|
520
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
521
|
-
if (!deps.react || (!deps.vite && !deps["@react-router/dev"]))
|
|
522
|
-
throw new Error("Expected a React project using Vite or React Router Framework Mode.");
|
|
523
|
-
const name = pkg.name ?? path.basename(root);
|
|
524
|
-
const configPath = path.join(root, ".mozole", "project.json");
|
|
525
|
-
const config = (await exists(configPath))
|
|
526
|
-
? await readConfig(root)
|
|
527
|
-
: projectConfig(name);
|
|
528
|
-
const wasManaged = await exists(configPath);
|
|
529
|
-
if (!config.backend && (await exists(path.join(root, "api/index.php"))))
|
|
530
|
-
config.backend = projectConfig(name, { backend: true }).backend;
|
|
531
|
-
delete config.cce;
|
|
532
|
-
delete config.governor;
|
|
533
|
-
delete config.telemetry;
|
|
534
|
-
config.mode = "scaffold";
|
|
535
|
-
config.name = name;
|
|
536
|
-
for (const key of Object.keys(config))
|
|
537
|
-
if (![
|
|
538
|
-
"schemaVersion",
|
|
539
|
-
"name",
|
|
540
|
-
"mode",
|
|
541
|
-
"framework",
|
|
542
|
-
"routes",
|
|
543
|
-
"backend",
|
|
544
|
-
"qa",
|
|
545
|
-
"cce",
|
|
546
|
-
"governor",
|
|
547
|
-
"telemetry",
|
|
548
|
-
"verifiers",
|
|
549
|
-
].includes(key))
|
|
550
|
-
delete config[key];
|
|
551
|
-
const templates = projectTemplates(name, {
|
|
552
|
-
backend: Boolean(config.backend?.enabled),
|
|
553
|
-
});
|
|
554
|
-
adaptFrameworkGuide(templates, Boolean(deps["@react-router/dev"]));
|
|
555
|
-
const migration = await canonicalPlan(root, templates, false, wasManaged, name, knownGuides(name));
|
|
556
|
-
const generated = JSON.parse(templates["package.json"] ?? "{}");
|
|
557
|
-
const routeFile = path.join(root, "app", "routes.ts");
|
|
558
|
-
if (await exists(routeFile))
|
|
559
|
-
config.routes = detectRoutes(await readFile(routeFile, "utf8"));
|
|
560
|
-
else if (!wasManaged)
|
|
561
|
-
config.routes = ["/"];
|
|
562
|
-
const files = {
|
|
563
|
-
".mozole/project.json": `${JSON.stringify(config, null, 2)}\n`,
|
|
564
|
-
".mozole/toolchain.json": required(templates, ".mozole/toolchain.json"),
|
|
565
|
-
"scripts/qa.mjs": required(templates, "scripts/qa.mjs"),
|
|
566
|
-
"docs/design/README.md": required(templates, "docs/design/README.md"),
|
|
567
|
-
"vitest.config.ts": required(templates, "vitest.config.ts"),
|
|
568
|
-
"tests/setup.ts": required(templates, "tests/setup.ts"),
|
|
569
|
-
...(config.backend?.enabled
|
|
570
|
-
? {
|
|
571
|
-
"scripts/check-php.mjs": required(templates, "scripts/check-php.mjs"),
|
|
572
|
-
"scripts/deploy.mjs": required(templates, "scripts/deploy.mjs"),
|
|
573
|
-
}
|
|
574
|
-
: {}),
|
|
575
|
-
};
|
|
576
|
-
snapshots = await snapshotFiles([
|
|
577
|
-
...new Set([
|
|
578
|
-
...adoptionMutationFiles(root),
|
|
579
|
-
...Object.keys(migration.writes).map((file) => path.join(root, file)),
|
|
580
|
-
...migration.removes.map((file) => path.join(root, file)),
|
|
581
|
-
...Object.keys(files).map((file) => path.join(root, file)),
|
|
582
|
-
]),
|
|
583
|
-
]);
|
|
584
|
-
progress.step("Updating project scripts and managed files");
|
|
585
|
-
const next = structuredClone(pkg);
|
|
586
|
-
next.scripts = {
|
|
587
|
-
lint: required(generated.scripts, "lint"),
|
|
588
|
-
format: required(generated.scripts, "format"),
|
|
589
|
-
typecheck: "tsc --noEmit",
|
|
590
|
-
"test:run": "vitest run",
|
|
591
|
-
...pkg.scripts,
|
|
592
|
-
};
|
|
593
|
-
if (!pkg.scripts?.typecheck || pkg.scripts.typecheck === "tsc --noEmit")
|
|
594
|
-
next.scripts.typecheck = deps["@react-router/dev"]
|
|
595
|
-
? "react-router typegen && tsc --noEmit"
|
|
596
|
-
: "tsc --noEmit";
|
|
597
|
-
for (const key of [
|
|
598
|
-
"verify",
|
|
599
|
-
"verify:force",
|
|
600
|
-
"qa",
|
|
601
|
-
"qa:force",
|
|
602
|
-
"qa:interactive",
|
|
603
|
-
"qa:visual",
|
|
604
|
-
])
|
|
605
|
-
next.scripts[key] = required(generated.scripts, key);
|
|
606
|
-
if (config.backend?.enabled)
|
|
607
|
-
for (const key of ["check:php", "build:deploy", "dev:api"])
|
|
608
|
-
next.scripts[key] = required(generated.scripts, key);
|
|
609
|
-
for (const [key, command] of Object.entries(next.scripts)) {
|
|
610
|
-
if (/^(?:npx(?: --no-install)? )?mozole\s/.test(command))
|
|
611
|
-
delete next.scripts[key];
|
|
612
|
-
}
|
|
613
|
-
next.devDependencies = { ...pkg.devDependencies };
|
|
614
|
-
delete next.devDependencies["@mozole/cli"];
|
|
615
|
-
if (next.dependencies)
|
|
616
|
-
delete next.dependencies["@mozole/cli"];
|
|
617
|
-
for (const key of [
|
|
618
|
-
"@biomejs/biome",
|
|
619
|
-
"@testing-library/jest-dom",
|
|
620
|
-
"@testing-library/react",
|
|
621
|
-
"@testing-library/user-event",
|
|
622
|
-
"@vitejs/plugin-react",
|
|
623
|
-
"jsdom",
|
|
624
|
-
"typescript",
|
|
625
|
-
"vitest",
|
|
626
|
-
"playwright",
|
|
627
|
-
])
|
|
628
|
-
next.devDependencies[key] = required(generated.devDependencies, key);
|
|
629
|
-
const changed = JSON.stringify(next) !== JSON.stringify(pkg);
|
|
630
|
-
if (changed) {
|
|
631
|
-
await atomicJson(path.join(root, ".mozole", "backups", "package.json.before-adopt"), pkg);
|
|
632
|
-
await atomicJson(packagePath, next);
|
|
633
|
-
}
|
|
634
|
-
await writeFiles(root, files);
|
|
635
|
-
await atomicJson(configPath, config);
|
|
636
|
-
await atomicJson(path.join(root, ".mozole/toolchain.json"), JSON.parse(required(templates, ".mozole/toolchain.json")));
|
|
637
|
-
await ensureBiomeConfig(root, required(templates, "biome.json"), false);
|
|
638
|
-
await ensureGitignore(root, false);
|
|
639
|
-
movedDirectories = await applyCanonicalPlan(root, migration, snapshots);
|
|
640
|
-
progress.step("Synchronizing installed dependencies and finalizing migration");
|
|
641
|
-
if (process.env.MOZOLE_SKIP_INSTALL !== "1") {
|
|
642
|
-
for (const args of [
|
|
643
|
-
["install", "--no-fund", "--no-audit"],
|
|
644
|
-
...(process.env.MOZOLE_SKIP_BROWSER === "1"
|
|
645
|
-
? []
|
|
646
|
-
: [["exec", "--", "playwright", "install", "chromium"]]),
|
|
647
|
-
]) {
|
|
648
|
-
const result = await run(npmExecutable(), args, {
|
|
649
|
-
cwd: root,
|
|
650
|
-
timeoutMs: 600_000,
|
|
651
|
-
signal: controller.signal,
|
|
652
|
-
logFile: path.join(root, ".mozole", "logs", "adopt-install.log"),
|
|
653
|
-
});
|
|
654
|
-
if (result.exitCode !== 0)
|
|
655
|
-
throw new Error("Dependency setup failed. See .mozole/logs/adopt-install.log");
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
controller.signal.throwIfAborted();
|
|
659
|
-
progress.done();
|
|
660
|
-
output.log(`ADOPT PASS\n\n${root}\nCurrent scaffold system installed. Application code preserved.${migrationSummary(migration)}`);
|
|
661
|
-
return 0;
|
|
662
|
-
}
|
|
663
|
-
catch (error) {
|
|
664
|
-
progress.done();
|
|
665
|
-
let rollbackFailure = "";
|
|
666
|
-
try {
|
|
667
|
-
await restoreDirectories(movedDirectories);
|
|
668
|
-
if (snapshots)
|
|
669
|
-
await restoreFiles(snapshots);
|
|
670
|
-
}
|
|
671
|
-
catch (failure) {
|
|
672
|
-
rollbackFailure = `\nRollback failed: ${String(failure)}`;
|
|
673
|
-
}
|
|
674
|
-
output.error(`ADOPT FAIL\n\n${error instanceof Error ? error.message : String(error)}${rollbackFailure || "\nManaged project files restored."}\nIf installation started, rerun npm install to reconcile node_modules.`);
|
|
675
|
-
return controller.signal.aborted ? 130 : 1;
|
|
676
|
-
}
|
|
677
|
-
finally {
|
|
678
|
-
process.removeListener("SIGINT", abort);
|
|
679
|
-
process.removeListener("SIGTERM", abort);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
function required(values, key) {
|
|
683
|
-
const value = values[key];
|
|
684
|
-
if (value === undefined)
|
|
685
|
-
throw new Error(`Missing template value: ${key}`);
|
|
686
|
-
return value;
|
|
687
|
-
}
|
|
688
|
-
function knownGuides(name) {
|
|
689
|
-
const known = { "AGENTS.md": [], "CLAUDE.md": [] };
|
|
690
|
-
for (const scaffold of [true, false])
|
|
691
|
-
for (const backend of [true, false]) {
|
|
692
|
-
const files = projectTemplates(name, { scaffold, backend });
|
|
693
|
-
for (const file of Object.keys(known))
|
|
694
|
-
known[file]?.push(required(files, file));
|
|
695
|
-
}
|
|
696
|
-
for (const backend of [true, false])
|
|
697
|
-
known["AGENTS.md"]?.push(basicAgentGuide(backend).split("## Design implementation guardrails")[0] ?? "");
|
|
698
|
-
return known;
|
|
699
|
-
}
|
|
700
|
-
function adaptFrameworkGuide(templates, reactRouter) {
|
|
701
|
-
if (reactRouter)
|
|
702
|
-
return;
|
|
703
|
-
const replacement = "This is an existing React + Vite project. Preserve its source layout and routing conventions. The project's build script defines production output.";
|
|
704
|
-
templates["AGENTS.md"] = required(templates, "AGENTS.md")
|
|
705
|
-
.replace("React Router and Vite source is in `app/`; routes are declared in `app/routes.ts`. Production HTML and assets are in `build/client/`.", replacement)
|
|
706
|
-
.replace("React Router Framework Mode lives in `app/`; routes are in `app/routes.ts`; static output is `build/client/`.", replacement);
|
|
707
|
-
templates["docs/architecture.md"] = `# Architecture\n\n${replacement}\n`;
|
|
708
|
-
}
|
|
709
|
-
async function validateManagedPaths(root) {
|
|
710
|
-
for (const absolute of adoptionMutationFiles(root))
|
|
711
|
-
await safeRead(root, path.relative(root, absolute).replaceAll("\\", "/"));
|
|
712
|
-
for (const relative of [
|
|
713
|
-
".codex/config.toml",
|
|
714
|
-
"scripts/qa.mjs",
|
|
715
|
-
"scripts/verify-extra.mjs",
|
|
716
|
-
"scripts/check-php.mjs",
|
|
717
|
-
"scripts/deploy.mjs",
|
|
718
|
-
"api/index.php",
|
|
719
|
-
"api/config.php",
|
|
720
|
-
".htaccess",
|
|
721
|
-
"docs/architecture.md",
|
|
722
|
-
"docs/backend-php.md",
|
|
723
|
-
"docs/design-tokens.md",
|
|
724
|
-
".mozole/telemetry.json",
|
|
725
|
-
])
|
|
726
|
-
await safeRead(root, relative);
|
|
727
|
-
for (const relative of [".cce", ".mozole/state"]) {
|
|
728
|
-
try {
|
|
729
|
-
if ((await lstat(path.join(root, relative))).isSymbolicLink())
|
|
730
|
-
throw new Error(`Cannot adopt symlinked managed path: ${relative}`);
|
|
731
|
-
}
|
|
732
|
-
catch (error) {
|
|
733
|
-
if (error.code !== "ENOENT")
|
|
734
|
-
throw error;
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
async function restoreDirectories(moved) {
|
|
739
|
-
for (const { from, to } of [...moved].reverse()) {
|
|
740
|
-
await mkdir(path.dirname(from), { recursive: true });
|
|
741
|
-
await rm(from, { recursive: true, force: true });
|
|
742
|
-
await rename(to, from);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
async function applyCanonicalPlan(root, plan, snapshots) {
|
|
746
|
-
const moved = [];
|
|
747
|
-
let backup;
|
|
748
|
-
async function backupDirectory() {
|
|
749
|
-
if (!backup) {
|
|
750
|
-
const parent = path.join(root, ".mozole/backups");
|
|
751
|
-
await mkdir(parent, { recursive: true });
|
|
752
|
-
backup = await mkdtemp(path.join(parent, "adopt-"));
|
|
753
|
-
plan.backup = backup;
|
|
754
|
-
}
|
|
755
|
-
return backup;
|
|
756
|
-
}
|
|
757
|
-
try {
|
|
758
|
-
for (const snapshot of snapshots) {
|
|
759
|
-
if (!snapshot.contents ||
|
|
760
|
-
snapshot.file.includes(`${path.sep}backups${path.sep}`))
|
|
761
|
-
continue;
|
|
762
|
-
const relative = path.relative(root, snapshot.file).replaceAll("\\", "/");
|
|
763
|
-
const desired = plan.removes.includes(relative)
|
|
764
|
-
? undefined
|
|
765
|
-
: (plan.writes[relative] ?? (await safeRead(root, relative)));
|
|
766
|
-
if (desired !== undefined &&
|
|
767
|
-
snapshot.contents.equals(Buffer.from(desired)))
|
|
768
|
-
continue;
|
|
769
|
-
const file = path.join(await backupDirectory(), relative);
|
|
770
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
771
|
-
const temporary = `${file}.${process.pid}.tmp`;
|
|
772
|
-
await writeFile(temporary, snapshot.contents);
|
|
773
|
-
await rename(temporary, file);
|
|
774
|
-
}
|
|
775
|
-
await writeFiles(root, plan.writes, true);
|
|
776
|
-
for (const relative of plan.removes)
|
|
777
|
-
await rm(path.join(root, relative), { force: true });
|
|
778
|
-
for (const relative of plan.directories) {
|
|
779
|
-
const from = path.join(root, relative);
|
|
780
|
-
if (!(await exists(from)))
|
|
781
|
-
continue;
|
|
782
|
-
const to = path.join(await backupDirectory(), relative);
|
|
783
|
-
await mkdir(path.dirname(to), { recursive: true });
|
|
784
|
-
await rename(from, to);
|
|
785
|
-
moved.push({ from, to });
|
|
786
|
-
}
|
|
787
|
-
// Remove only empty integration directories; user-owned files prevent removal.
|
|
788
|
-
for (const relative of [
|
|
789
|
-
".agents/workflows",
|
|
790
|
-
".agents/rules",
|
|
791
|
-
".agents",
|
|
792
|
-
".codex",
|
|
793
|
-
".claude",
|
|
794
|
-
]) {
|
|
795
|
-
const directory = path.join(root, relative);
|
|
796
|
-
try {
|
|
797
|
-
if (!(await readdir(directory)).length)
|
|
798
|
-
await rmdir(directory);
|
|
799
|
-
}
|
|
800
|
-
catch (error) {
|
|
801
|
-
if (!["ENOENT", "ENOTEMPTY"].includes(error.code ?? ""))
|
|
802
|
-
throw error;
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
return moved;
|
|
806
|
-
}
|
|
807
|
-
catch (error) {
|
|
808
|
-
await restoreDirectories(moved);
|
|
809
|
-
throw error;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
function migrationSummary(plan) {
|
|
813
|
-
return `${plan.backup ? `\nMigration backup: ${plan.backup}` : ""}${plan.notes.length ? `\n${plan.notes.join("\n")}` : ""}`;
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import { scaffoldAgentPolicies } from "../scaffold/agents.js";
|
|
4
|
+
import { scaffoldDesign } from "../scaffold/design.js";
|
|
5
|
+
import { scaffoldPhases } from "../scaffold/phases.js";
|
|
6
|
+
import { scaffoldRepomap } from "../scaffold/repomap.js";
|
|
7
|
+
import { scaffoldTokens } from "../scaffold/tokens.js";
|
|
8
|
+
import { exists } from "../utils/fs.js";
|
|
9
|
+
export async function adoptProject(options = {}) {
|
|
10
|
+
const targetDir = options.targetDir ? path.resolve(options.targetDir) : process.cwd();
|
|
11
|
+
const projectName = options.projectName ?? path.basename(targetDir);
|
|
12
|
+
if (!(await exists(targetDir))) {
|
|
13
|
+
throw new Error(`Directory not found: ${targetDir}`);
|
|
14
|
+
}
|
|
15
|
+
console.log(pc.cyan(`\nš¦ Adopting existing project into Mozole Studio governance: ${pc.bold(projectName)}`));
|
|
16
|
+
console.log(` Target: ${targetDir}\n`);
|
|
17
|
+
// 1. Authoritative Policies
|
|
18
|
+
const agentsPath = path.join(targetDir, "AGENTS.md");
|
|
19
|
+
if (!(await exists(agentsPath))) {
|
|
20
|
+
await scaffoldAgentPolicies(targetDir, projectName);
|
|
21
|
+
console.log(pc.green(" ā Injected AGENTS.md, CLAUDE.md, and Cursor rules"));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.log(pc.yellow(" - AGENTS.md already exists, preserving"));
|
|
25
|
+
}
|
|
26
|
+
// 2. Phases & Repomap
|
|
27
|
+
const phasesPath = path.join(targetDir, "docs", "phases", "status.md");
|
|
28
|
+
if (!(await exists(phasesPath))) {
|
|
29
|
+
await scaffoldPhases(targetDir);
|
|
30
|
+
console.log(pc.green(" ā Generated docs/phases/status.md (Phase 00 initialized)"));
|
|
31
|
+
}
|
|
32
|
+
const repomapPath = path.join(targetDir, "docs", "repomap", "architecture.md");
|
|
33
|
+
if (!(await exists(repomapPath))) {
|
|
34
|
+
await scaffoldRepomap(targetDir, projectName, false);
|
|
35
|
+
console.log(pc.green(" ā Generated docs/repomap/ fihrist architecture"));
|
|
36
|
+
}
|
|
37
|
+
// 3. Design References
|
|
38
|
+
const designPath = path.join(targetDir, "docs", "design", "README.md");
|
|
39
|
+
if (!(await exists(designPath))) {
|
|
40
|
+
await scaffoldDesign(targetDir, projectName);
|
|
41
|
+
console.log(pc.green(" ā Generated docs/design/ evidence directory"));
|
|
42
|
+
}
|
|
43
|
+
// 4. Tokens
|
|
44
|
+
const tokensPath = path.join(targetDir, "src", "styles", "tokens.css");
|
|
45
|
+
if (!(await exists(tokensPath))) {
|
|
46
|
+
await scaffoldTokens(targetDir);
|
|
47
|
+
console.log(pc.green(" ā Generated canonical src/styles/tokens.css"));
|
|
48
|
+
}
|
|
49
|
+
console.log(pc.green("\nā Project successfully adopted into Mozole governance."));
|
|
50
|
+
console.log(`Run ${pc.cyan("mozole verify")} to audit current conformance.\n`);
|
|
814
51
|
}
|
|
815
52
|
//# sourceMappingURL=adopt.js.map
|