@xenonbyte/da-vinci-workflow 0.1.13 → 0.1.15
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/CHANGELOG.md +21 -1
- package/README.md +23 -1
- package/README.zh-CN.md +23 -1
- package/SKILL.md +15 -0
- package/commands/claude/dv/design.md +2 -0
- package/commands/claude/dv/verify.md +2 -0
- package/commands/codex/prompts/dv-design.md +2 -0
- package/commands/codex/prompts/dv-verify.md +1 -0
- package/commands/gemini/dv/design.toml +2 -0
- package/commands/gemini/dv/verify.toml +1 -0
- package/docs/mcp-aware-gate-implementation.md +291 -0
- package/docs/mcp-aware-gate-tests.md +244 -0
- package/docs/mcp-aware-gate.md +246 -0
- package/docs/mode-use-cases.md +2 -0
- package/docs/prompt-presets/README.md +1 -0
- package/docs/prompt-presets/desktop-app.md +4 -0
- package/docs/prompt-presets/mobile-app.md +4 -0
- package/docs/prompt-presets/tablet-app.md +4 -0
- package/docs/prompt-presets/web-app.md +4 -0
- package/docs/visual-adapters.md +9 -0
- package/docs/visual-assist-presets/README.md +4 -2
- package/docs/visual-assist-presets/desktop-app.md +2 -0
- package/docs/visual-assist-presets/mobile-app.md +2 -0
- package/docs/visual-assist-presets/tablet-app.md +2 -0
- package/docs/visual-assist-presets/web-app.md +2 -0
- package/docs/workflow-examples.md +9 -4
- package/docs/zh-CN/mcp-aware-gate-implementation.md +290 -0
- package/docs/zh-CN/mcp-aware-gate-tests.md +244 -0
- package/docs/zh-CN/mcp-aware-gate.md +249 -0
- package/docs/zh-CN/mode-use-cases.md +3 -0
- package/docs/zh-CN/prompt-presets/README.md +1 -0
- package/docs/zh-CN/prompt-presets/desktop-app.md +4 -0
- package/docs/zh-CN/prompt-presets/mobile-app.md +4 -0
- package/docs/zh-CN/prompt-presets/tablet-app.md +4 -0
- package/docs/zh-CN/prompt-presets/web-app.md +4 -0
- package/docs/zh-CN/visual-adapters.md +9 -0
- package/docs/zh-CN/visual-assist-presets/README.md +5 -3
- package/docs/zh-CN/visual-assist-presets/desktop-app.md +2 -0
- package/docs/zh-CN/visual-assist-presets/mobile-app.md +2 -0
- package/docs/zh-CN/visual-assist-presets/tablet-app.md +2 -0
- package/docs/zh-CN/visual-assist-presets/web-app.md +2 -0
- package/docs/zh-CN/workflow-examples.md +9 -4
- package/examples/greenfield-spec-markupflow/DA-VINCI.md +1 -0
- package/examples/greenfield-spec-markupflow/README.md +3 -0
- package/examples/greenfield-spec-markupflow/design-registry.md +3 -0
- package/examples/greenfield-spec-markupflow/pencil-design.md +4 -0
- package/lib/audit.js +348 -0
- package/lib/cli.js +47 -1
- package/lib/mcp-runtime-gate.js +342 -0
- package/package.json +3 -2
- package/references/artifact-templates.md +35 -3
- package/references/checkpoints.md +69 -1
- package/references/design-inputs.md +9 -1
- package/references/layout-hygiene.md +117 -0
- package/references/pencil-design-to-code.md +8 -0
- package/scripts/test-mcp-runtime-gate.js +199 -0
package/lib/audit.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
const IMAGE_EXPORT_PATTERN = /\.(png|jpe?g|webp|pdf)$/i;
|
|
5
|
+
|
|
6
|
+
function pathExists(targetPath) {
|
|
7
|
+
return fs.existsSync(targetPath);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function readTextIfExists(targetPath) {
|
|
11
|
+
if (!pathExists(targetPath)) {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
return fs.readFileSync(targetPath, "utf8");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function listFilesRecursive(rootDir) {
|
|
18
|
+
if (!pathExists(rootDir)) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return fs.readdirSync(rootDir, { withFileTypes: true }).flatMap((entry) => {
|
|
23
|
+
const fullPath = path.join(rootDir, entry.name);
|
|
24
|
+
if (entry.isDirectory()) {
|
|
25
|
+
return listFilesRecursive(fullPath);
|
|
26
|
+
}
|
|
27
|
+
return [fullPath];
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function listChildDirs(rootDir) {
|
|
32
|
+
if (!pathExists(rootDir)) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return fs
|
|
37
|
+
.readdirSync(rootDir, { withFileTypes: true })
|
|
38
|
+
.filter((entry) => entry.isDirectory())
|
|
39
|
+
.map((entry) => path.join(rootDir, entry.name));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function relativeTo(projectRoot, targetPath) {
|
|
43
|
+
return path.relative(projectRoot, targetPath) || ".";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function collectRegisteredPenPaths(projectRoot, designRegistryPath) {
|
|
47
|
+
const registryText = readTextIfExists(designRegistryPath);
|
|
48
|
+
const matches = registryText.match(/\.da-vinci\/designs\/[^\s`]+\.pen/g) || [];
|
|
49
|
+
return [...new Set(matches)].map((relativePath) => path.join(projectRoot, relativePath));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getNonEmptyChangeDirs(changesDir) {
|
|
53
|
+
return listChildDirs(changesDir).filter((changeDir) => listFilesRecursive(changeDir).length > 0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getExpectedChangeArtifacts(changeDir) {
|
|
57
|
+
return [
|
|
58
|
+
path.join(changeDir, "design-brief.md"),
|
|
59
|
+
path.join(changeDir, "design.md"),
|
|
60
|
+
path.join(changeDir, "pencil-design.md"),
|
|
61
|
+
path.join(changeDir, "pencil-bindings.md")
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isAllowedExportPath(projectRoot, targetPath) {
|
|
66
|
+
const relativePath = relativeTo(projectRoot, targetPath);
|
|
67
|
+
return /^\.da-vinci\/changes\/[^/]+\/exports\//.test(relativePath);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resolveScopedChangeDirs(projectRoot, changesDir, options, failures, warnings, notes) {
|
|
71
|
+
const requestedChangeId = options.changeId;
|
|
72
|
+
const requestedChangeDir = requestedChangeId ? path.join(changesDir, requestedChangeId) : null;
|
|
73
|
+
|
|
74
|
+
if (requestedChangeDir) {
|
|
75
|
+
if (!pathExists(requestedChangeDir)) {
|
|
76
|
+
failures.push(
|
|
77
|
+
`Requested change scope does not exist: ${relativeTo(projectRoot, requestedChangeDir)}`
|
|
78
|
+
);
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return [requestedChangeDir];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const nonEmptyChangeDirs = getNonEmptyChangeDirs(changesDir);
|
|
86
|
+
if (nonEmptyChangeDirs.length === 1) {
|
|
87
|
+
notes.push(
|
|
88
|
+
`Inferred completion change scope: ${relativeTo(projectRoot, nonEmptyChangeDirs[0])}`
|
|
89
|
+
);
|
|
90
|
+
return nonEmptyChangeDirs;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (nonEmptyChangeDirs.length === 0) {
|
|
94
|
+
failures.push(
|
|
95
|
+
"Completion audit requires a non-empty `.da-vinci/changes/<change-id>/` directory."
|
|
96
|
+
);
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
warnings.push(
|
|
101
|
+
"Multiple non-empty change directories exist. Pass `--change <change-id>` to run a strict completion audit."
|
|
102
|
+
);
|
|
103
|
+
failures.push("Completion audit scope is ambiguous without `--change <change-id>`.");
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function addMissingArtifacts(projectRoot, artifactPaths, targetList) {
|
|
108
|
+
for (const artifactPath of artifactPaths) {
|
|
109
|
+
if (!pathExists(artifactPath)) {
|
|
110
|
+
targetList.push(`Missing required artifact: ${relativeTo(projectRoot, artifactPath)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function auditProject(projectPathInput, options = {}) {
|
|
116
|
+
const projectRoot = path.resolve(projectPathInput || process.cwd());
|
|
117
|
+
const mode = options.mode || "integrity";
|
|
118
|
+
const daVinciDir = path.join(projectRoot, ".da-vinci");
|
|
119
|
+
const designsDir = path.join(daVinciDir, "designs");
|
|
120
|
+
const changesDir = path.join(daVinciDir, "changes");
|
|
121
|
+
const designRegistryPath = path.join(daVinciDir, "design-registry.md");
|
|
122
|
+
|
|
123
|
+
const failures = [];
|
|
124
|
+
const warnings = [];
|
|
125
|
+
const notes = [];
|
|
126
|
+
|
|
127
|
+
if (!["integrity", "completion"].includes(mode)) {
|
|
128
|
+
failures.push(`Unsupported audit mode: ${mode}`);
|
|
129
|
+
return {
|
|
130
|
+
projectRoot,
|
|
131
|
+
mode,
|
|
132
|
+
changeId: options.changeId || null,
|
|
133
|
+
status: "FAIL",
|
|
134
|
+
failures,
|
|
135
|
+
warnings,
|
|
136
|
+
notes
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!pathExists(projectRoot)) {
|
|
141
|
+
failures.push(`Project path does not exist: ${projectRoot}`);
|
|
142
|
+
return {
|
|
143
|
+
projectRoot,
|
|
144
|
+
mode,
|
|
145
|
+
changeId: options.changeId || null,
|
|
146
|
+
status: "FAIL",
|
|
147
|
+
failures,
|
|
148
|
+
warnings,
|
|
149
|
+
notes
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!pathExists(daVinciDir)) {
|
|
154
|
+
failures.push("Missing `.da-vinci/` directory.");
|
|
155
|
+
return {
|
|
156
|
+
projectRoot,
|
|
157
|
+
mode,
|
|
158
|
+
changeId: options.changeId || null,
|
|
159
|
+
status: "FAIL",
|
|
160
|
+
failures,
|
|
161
|
+
warnings,
|
|
162
|
+
notes
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const integrityRequiredArtifacts = [path.join(projectRoot, "DA-VINCI.md"), designsDir];
|
|
167
|
+
addMissingArtifacts(projectRoot, integrityRequiredArtifacts, failures);
|
|
168
|
+
|
|
169
|
+
const completionRequiredArtifacts = [
|
|
170
|
+
path.join(projectRoot, "DA-VINCI.md"),
|
|
171
|
+
path.join(daVinciDir, "project-inventory.md"),
|
|
172
|
+
path.join(daVinciDir, "page-map.md"),
|
|
173
|
+
designRegistryPath,
|
|
174
|
+
designsDir
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
if (mode === "completion") {
|
|
178
|
+
addMissingArtifacts(projectRoot, completionRequiredArtifacts, failures);
|
|
179
|
+
} else {
|
|
180
|
+
addMissingArtifacts(projectRoot, completionRequiredArtifacts.slice(1, 4), warnings);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const daVinciFiles = listFilesRecursive(daVinciDir);
|
|
184
|
+
const misplacedExports = daVinciFiles.filter(
|
|
185
|
+
(filePath) => IMAGE_EXPORT_PATTERN.test(filePath) && !isAllowedExportPath(projectRoot, filePath)
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (misplacedExports.length > 0) {
|
|
189
|
+
failures.push(
|
|
190
|
+
[
|
|
191
|
+
"Review exports were written outside `.da-vinci/changes/<change-id>/exports/`:",
|
|
192
|
+
...misplacedExports.map((filePath) => ` - ${relativeTo(projectRoot, filePath)}`)
|
|
193
|
+
].join("\n")
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const designFiles = listFilesRecursive(designsDir);
|
|
198
|
+
const penFiles = designFiles.filter((filePath) => filePath.endsWith(".pen"));
|
|
199
|
+
const pollutedDesignFiles = designFiles.filter((filePath) => !filePath.endsWith(".pen"));
|
|
200
|
+
|
|
201
|
+
if (pollutedDesignFiles.length > 0) {
|
|
202
|
+
failures.push(
|
|
203
|
+
[
|
|
204
|
+
"`.da-vinci/designs/` contains non-`.pen` files:",
|
|
205
|
+
...pollutedDesignFiles.map((filePath) => ` - ${relativeTo(projectRoot, filePath)}`)
|
|
206
|
+
].join("\n")
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (penFiles.length === 0) {
|
|
211
|
+
const message = "No shell-visible project-local `.pen` file exists under `.da-vinci/designs/`.";
|
|
212
|
+
if (mode === "completion") {
|
|
213
|
+
failures.push(message);
|
|
214
|
+
} else {
|
|
215
|
+
warnings.push(message);
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
notes.push(
|
|
219
|
+
`Detected project-local .pen source(s): ${penFiles
|
|
220
|
+
.map((filePath) => relativeTo(projectRoot, filePath))
|
|
221
|
+
.join(", ")}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const registeredPenPaths = collectRegisteredPenPaths(projectRoot, designRegistryPath);
|
|
226
|
+
for (const registeredPenPath of registeredPenPaths) {
|
|
227
|
+
if (!pathExists(registeredPenPath)) {
|
|
228
|
+
failures.push(
|
|
229
|
+
`Registered design source is missing on disk: ${relativeTo(projectRoot, registeredPenPath)}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const changeDirs = listChildDirs(changesDir);
|
|
235
|
+
if (changeDirs.length === 0) {
|
|
236
|
+
warnings.push("No `.da-vinci/changes/<change-id>/` directory was found.");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const scopedChangeDirs =
|
|
240
|
+
mode === "completion"
|
|
241
|
+
? resolveScopedChangeDirs(projectRoot, changesDir, options, failures, warnings, notes)
|
|
242
|
+
: changeDirs;
|
|
243
|
+
|
|
244
|
+
for (const changeDir of changeDirs) {
|
|
245
|
+
const changeFiles = listFilesRecursive(changeDir);
|
|
246
|
+
const changeRel = relativeTo(projectRoot, changeDir);
|
|
247
|
+
|
|
248
|
+
if (changeFiles.length === 0) {
|
|
249
|
+
if (mode === "completion" && scopedChangeDirs.includes(changeDir)) {
|
|
250
|
+
failures.push(`Completion scope is empty: ${changeRel}`);
|
|
251
|
+
}
|
|
252
|
+
warnings.push(`Empty change scaffold: ${changeRel}`);
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const exportsDir = path.join(changeDir, "exports");
|
|
257
|
+
const exportsFiles = listFilesRecursive(exportsDir);
|
|
258
|
+
if (exportsFiles.length > 0) {
|
|
259
|
+
notes.push(`Detected review exports under ${relativeTo(projectRoot, exportsDir)}.`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const expectedChangeArtifacts = getExpectedChangeArtifacts(changeDir);
|
|
263
|
+
const missingArtifacts = expectedChangeArtifacts.filter((artifactPath) => !pathExists(artifactPath));
|
|
264
|
+
|
|
265
|
+
if (mode === "completion" && scopedChangeDirs.includes(changeDir) && missingArtifacts.length > 0) {
|
|
266
|
+
failures.push(
|
|
267
|
+
[
|
|
268
|
+
`Completion scope is missing required change artifacts in ${changeRel}:`,
|
|
269
|
+
...missingArtifacts.map((artifactPath) => ` - ${relativeTo(projectRoot, artifactPath)}`)
|
|
270
|
+
].join("\n")
|
|
271
|
+
);
|
|
272
|
+
} else if (missingArtifacts.length > 0 && missingArtifacts.length < expectedChangeArtifacts.length) {
|
|
273
|
+
warnings.push(
|
|
274
|
+
[
|
|
275
|
+
`Partial change artifact set in ${changeRel}:`,
|
|
276
|
+
...missingArtifacts.map((artifactPath) => ` - missing ${relativeTo(projectRoot, artifactPath)}`)
|
|
277
|
+
].join("\n")
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (exportsFiles.length > 0 && !pathExists(path.join(changeDir, "pencil-design.md"))) {
|
|
282
|
+
failures.push(
|
|
283
|
+
`Exported screenshots exist in ${changeRel} but no pencil-design.md records the design pass.`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const specDirs = listChildDirs(path.join(changeDir, "specs"));
|
|
288
|
+
for (const specDir of specDirs) {
|
|
289
|
+
const specFile = path.join(specDir, "spec.md");
|
|
290
|
+
if (!pathExists(specFile)) {
|
|
291
|
+
warnings.push(`Spec scaffold exists without spec.md: ${relativeTo(projectRoot, specDir)}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const status = failures.length > 0 ? "FAIL" : warnings.length > 0 ? "WARN" : "PASS";
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
projectRoot,
|
|
300
|
+
mode,
|
|
301
|
+
changeId: options.changeId || null,
|
|
302
|
+
status,
|
|
303
|
+
failures,
|
|
304
|
+
warnings,
|
|
305
|
+
notes
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function formatAuditReport(result) {
|
|
310
|
+
const lines = [
|
|
311
|
+
"Da Vinci audit",
|
|
312
|
+
`Project: ${result.projectRoot}`,
|
|
313
|
+
`Mode: ${result.mode}`,
|
|
314
|
+
`Status: ${result.status}`
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
if (result.changeId) {
|
|
318
|
+
lines.splice(3, 0, `Change: ${result.changeId}`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (result.failures.length > 0) {
|
|
322
|
+
lines.push("", "Failures:");
|
|
323
|
+
for (const failure of result.failures) {
|
|
324
|
+
lines.push(`- ${failure}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (result.warnings.length > 0) {
|
|
329
|
+
lines.push("", "Warnings:");
|
|
330
|
+
for (const warning of result.warnings) {
|
|
331
|
+
lines.push(`- ${warning}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (result.notes.length > 0) {
|
|
336
|
+
lines.push("", "Notes:");
|
|
337
|
+
for (const note of result.notes) {
|
|
338
|
+
lines.push(`- ${note}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return lines.join("\n");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
module.exports = {
|
|
346
|
+
auditProject,
|
|
347
|
+
formatAuditReport
|
|
348
|
+
};
|
package/lib/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ const {
|
|
|
5
5
|
getStatus,
|
|
6
6
|
validateAssets
|
|
7
7
|
} = require("./install");
|
|
8
|
+
const { auditProject, formatAuditReport } = require("./audit");
|
|
8
9
|
|
|
9
10
|
function getOption(args, name) {
|
|
10
11
|
const direct = args.find((arg) => arg.startsWith(`${name}=`));
|
|
@@ -20,6 +21,31 @@ function getOption(args, name) {
|
|
|
20
21
|
return undefined;
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
function getPositionalArgs(args, optionsWithValues = []) {
|
|
25
|
+
const positional = [];
|
|
26
|
+
|
|
27
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
28
|
+
const arg = args[index];
|
|
29
|
+
|
|
30
|
+
if (optionsWithValues.includes(arg)) {
|
|
31
|
+
index += 1;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (optionsWithValues.some((name) => arg.startsWith(`${name}=`))) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (arg.startsWith("-")) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
positional.push(arg);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return positional;
|
|
47
|
+
}
|
|
48
|
+
|
|
23
49
|
function formatStatus(status) {
|
|
24
50
|
return [
|
|
25
51
|
`Da Vinci v${status.version}`,
|
|
@@ -40,11 +66,15 @@ function printHelp() {
|
|
|
40
66
|
" da-vinci uninstall --platform codex,claude,gemini",
|
|
41
67
|
" da-vinci status",
|
|
42
68
|
" da-vinci validate-assets",
|
|
69
|
+
" da-vinci audit [project-path]",
|
|
43
70
|
" da-vinci --version",
|
|
44
71
|
"",
|
|
45
72
|
"Options:",
|
|
46
73
|
" --platform <value> codex, claude, gemini, or all",
|
|
47
|
-
" --home <path> override HOME for installation targets"
|
|
74
|
+
" --home <path> override HOME for installation targets",
|
|
75
|
+
" --project <path> override project path for audit",
|
|
76
|
+
" --mode <value> integrity or completion",
|
|
77
|
+
" --change <id> scope completion audit to one change id"
|
|
48
78
|
].join("\n")
|
|
49
79
|
);
|
|
50
80
|
}
|
|
@@ -52,6 +82,7 @@ function printHelp() {
|
|
|
52
82
|
async function runCli(argv) {
|
|
53
83
|
const [command] = argv;
|
|
54
84
|
const homeDir = getOption(argv, "--home");
|
|
85
|
+
const positionalArgs = getPositionalArgs(argv.slice(1), ["--home", "--platform", "--project", "--mode", "--change"]);
|
|
55
86
|
|
|
56
87
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
57
88
|
printHelp();
|
|
@@ -92,6 +123,21 @@ async function runCli(argv) {
|
|
|
92
123
|
return;
|
|
93
124
|
}
|
|
94
125
|
|
|
126
|
+
if (command === "audit") {
|
|
127
|
+
const projectPath = getOption(argv, "--project") || positionalArgs[0] || process.cwd();
|
|
128
|
+
const mode = getOption(argv, "--mode");
|
|
129
|
+
const changeId = getOption(argv, "--change");
|
|
130
|
+
const result = auditProject(projectPath, { mode, changeId });
|
|
131
|
+
const report = formatAuditReport(result);
|
|
132
|
+
|
|
133
|
+
if (result.status === "FAIL") {
|
|
134
|
+
throw new Error(report);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log(report);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
95
141
|
throw new Error(`Unknown command: ${command}`);
|
|
96
142
|
}
|
|
97
143
|
|