audit-tools 0.32.51 → 0.32.53
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/package.json +1 -1
- package/remediate-code.mjs +50 -24
- package/wrapper/audit-code-wrapper-lib.mjs +54 -11
package/package.json
CHANGED
package/remediate-code.mjs
CHANGED
|
@@ -36,44 +36,66 @@ function newestMtimeMs(path) {
|
|
|
36
36
|
return newest;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
function shouldBuildDist() {
|
|
39
|
+
export function shouldBuildDist() {
|
|
40
40
|
if (!existsSync(sourceRoot)) {
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
|
-
if (!existsSync(tsconfigPath)) {
|
|
44
|
-
return !existsSync(distEntry);
|
|
45
|
-
}
|
|
46
43
|
if (!existsSync(distEntry)) {
|
|
47
44
|
return true;
|
|
48
45
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
)
|
|
46
|
+
// Compare dist freshness against src/ (and tsconfig.json when present). A
|
|
47
|
+
// missing tsconfig.json must NOT collapse this to an existence-only check: a
|
|
48
|
+
// dist older than src/ is still stale and must rebuild (CE-003). Previously
|
|
49
|
+
// the tsconfig-absent branch returned `!existsSync(distEntry)`, so a present
|
|
50
|
+
// but stale dist was silently used.
|
|
51
|
+
const newestSourceMs = existsSync(tsconfigPath)
|
|
52
|
+
? Math.max(newestMtimeMs(sourceRoot), statSync(tsconfigPath).mtimeMs)
|
|
53
|
+
: newestMtimeMs(sourceRoot);
|
|
54
|
+
return statSync(distEntry).mtimeMs < newestSourceMs;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Default build runner (platform-branched npm run build). Injectable in
|
|
58
|
+
// ensureBuilt so the build-failure control flow is unit-testable without
|
|
59
|
+
// spawning a real build.
|
|
60
|
+
function runNpmBuild() {
|
|
61
|
+
return process.platform === "win32"
|
|
62
|
+
? spawnSync(process.env.ComSpec ?? "cmd.exe", ["/d", "/s", "/c", "npm run build"], {
|
|
63
|
+
cwd: __dirname,
|
|
64
|
+
encoding: "utf8",
|
|
65
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
+
})
|
|
67
|
+
: spawnSync("npm", ["run", "build"], {
|
|
68
|
+
cwd: __dirname,
|
|
69
|
+
encoding: "utf8",
|
|
70
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
71
|
+
});
|
|
53
72
|
}
|
|
54
73
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
// Returns true when it is safe to proceed to dist forwarding, false when a
|
|
75
|
+
// failed/signaled build has initiated a terminating exit action and the caller
|
|
76
|
+
// MUST stop. The signal branch of applyWrapperExitAction re-raises the signal
|
|
77
|
+
// and schedules a fallback exit but RETURNS control (the fallback can only fire
|
|
78
|
+
// once the event loop is free); without this boolean guard main() would then
|
|
79
|
+
// run its blocking spawnSync and forward the command against a stale/absent
|
|
80
|
+
// dist before the fallback ever fires (CE-002). The exit-code branch terminates
|
|
81
|
+
// synchronously, so it never reaches the `return false`.
|
|
82
|
+
export function ensureBuilt({
|
|
83
|
+
shouldBuild = shouldBuildDist,
|
|
84
|
+
runBuild = runNpmBuild,
|
|
85
|
+
applyExit = applyWrapperExitAction,
|
|
86
|
+
} = {}) {
|
|
87
|
+
if (!shouldBuild()) return true;
|
|
88
|
+
const result = runBuild();
|
|
69
89
|
if (result.stdout) process.stderr.write(result.stdout);
|
|
70
90
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
71
91
|
if (result.error) {
|
|
72
92
|
console.error(`remediate-code: failed to auto-build dist (${result.error.message})`);
|
|
73
93
|
}
|
|
74
94
|
if (result.status !== 0 || result.signal) {
|
|
75
|
-
|
|
95
|
+
applyExit(getWrapperExitAction(result));
|
|
96
|
+
return false;
|
|
76
97
|
}
|
|
98
|
+
return true;
|
|
77
99
|
}
|
|
78
100
|
|
|
79
101
|
export function getWrapperExitAction(result, platform = process.platform) {
|
|
@@ -119,7 +141,11 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
119
141
|
return;
|
|
120
142
|
}
|
|
121
143
|
|
|
122
|
-
|
|
144
|
+
// A failed/signaled build returns false here; stop rather than forward the
|
|
145
|
+
// command against a stale/absent dist (CE-002). The exit action is already
|
|
146
|
+
// in flight (synchronous exit for a nonzero status, pending fallback exit for
|
|
147
|
+
// a re-raised signal).
|
|
148
|
+
if (!ensureBuilt()) return;
|
|
123
149
|
if (!existsSync(distEntry)) {
|
|
124
150
|
console.error("remediate-code: dist/remediate/index.js not found. Run: npm run build");
|
|
125
151
|
process.exit(1);
|
|
@@ -23,12 +23,31 @@ const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
23
23
|
const distEntry = join(repoRoot, 'dist', 'audit', 'index.js');
|
|
24
24
|
const packageJsonPath = join(repoRoot, 'package.json');
|
|
25
25
|
const promptAssetPath = join(repoRoot, 'skills', 'audit-code', 'audit-code.prompt.md');
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
// Deferred (NOT a top-level await): package.json is only needed by the
|
|
28
|
+
// `--version` branch, and a top-level read would fail EVERY invocation —
|
|
29
|
+
// including `--help` — whenever package.json is unreadable (CE-006).
|
|
30
|
+
async function readPackageVersion() {
|
|
31
|
+
return JSON.parse(await readFile(packageJsonPath, 'utf8')).version;
|
|
32
|
+
}
|
|
27
33
|
|
|
28
34
|
function hasFlag(argv, name) {
|
|
29
35
|
return argv.includes(name);
|
|
30
36
|
}
|
|
31
37
|
|
|
38
|
+
// Informational flags (--help/--version) short-circuit the wrapper only when
|
|
39
|
+
// they appear BEFORE the first non-flag token (the command). A whole-argv scan
|
|
40
|
+
// hijacks post-command tokens that belong to the dist CLI — e.g.
|
|
41
|
+
// `audit-code explain-task -v` printed the wrapper's version instead of
|
|
42
|
+
// forwarding `-v` to the dist command (CE-007).
|
|
43
|
+
function hasLeadingFlag(argv, name) {
|
|
44
|
+
for (const token of argv) {
|
|
45
|
+
if (token === name) return true;
|
|
46
|
+
if (!token.startsWith('-')) return false;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
32
51
|
function getFlag(argv, name) {
|
|
33
52
|
const index = argv.indexOf(name);
|
|
34
53
|
if (index < 0) return undefined;
|
|
@@ -41,6 +60,22 @@ function setDefaultFlag(argv, name, value) {
|
|
|
41
60
|
}
|
|
42
61
|
}
|
|
43
62
|
|
|
63
|
+
// Overwrite an existing flag's value (or append when absent). setDefaultFlag
|
|
64
|
+
// only fills a MISSING flag, so a user-supplied RELATIVE --root/--artifacts-dir
|
|
65
|
+
// was forwarded raw and then re-resolved against the child's cwd (repoRoot),
|
|
66
|
+
// not the caller's cwd — e.g. `--root .` pointed at the package dir (CE-001).
|
|
67
|
+
// Normalizing to an absolute path here makes the forwarded value cwd-stable.
|
|
68
|
+
function setFlag(argv, name, value) {
|
|
69
|
+
const index = argv.indexOf(name);
|
|
70
|
+
if (index < 0) {
|
|
71
|
+
argv.push(name, value);
|
|
72
|
+
} else {
|
|
73
|
+
argv[index + 1] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { hasLeadingFlag, setFlag };
|
|
78
|
+
|
|
44
79
|
function nodeExecutable() {
|
|
45
80
|
return process.execPath;
|
|
46
81
|
}
|
|
@@ -193,8 +228,10 @@ async function runDistCommand(commandName, argv, { ensureArtifactsDir = false }
|
|
|
193
228
|
const rootValue = resolve(getFlag(commandArgs, '--root') ?? '.');
|
|
194
229
|
const artifactsDir = resolve(getFlag(commandArgs, '--artifacts-dir') ?? join(rootValue, '.audit-tools', 'audit'));
|
|
195
230
|
|
|
196
|
-
|
|
197
|
-
|
|
231
|
+
// Overwrite (not default) so a user-supplied relative value is normalized to
|
|
232
|
+
// the caller-cwd-resolved absolute path before it reaches the child (CE-001).
|
|
233
|
+
setFlag(commandArgs, '--root', rootValue);
|
|
234
|
+
setFlag(commandArgs, '--artifacts-dir', artifactsDir);
|
|
198
235
|
|
|
199
236
|
if (ensureArtifactsDir) {
|
|
200
237
|
await mkdir(artifactsDir, { recursive: true });
|
|
@@ -206,15 +243,21 @@ async function runDistCommand(commandName, argv, { ensureArtifactsDir = false }
|
|
|
206
243
|
});
|
|
207
244
|
}
|
|
208
245
|
|
|
209
|
-
async function runDistCommandInline(commandName, argv) {
|
|
246
|
+
async function runDistCommandInline(commandName, argv, { ensureArtifactsDir = false } = {}) {
|
|
210
247
|
const commandArgs = [...argv];
|
|
211
248
|
const rootValue = resolve(getFlag(commandArgs, '--root') ?? '.');
|
|
212
249
|
const artifactsDir = resolve(getFlag(commandArgs, '--artifacts-dir') ?? join(rootValue, '.audit-tools', 'audit'));
|
|
213
250
|
|
|
214
|
-
|
|
215
|
-
|
|
251
|
+
setFlag(commandArgs, '--root', rootValue);
|
|
252
|
+
setFlag(commandArgs, '--artifacts-dir', artifactsDir);
|
|
216
253
|
|
|
217
|
-
|
|
254
|
+
// Gate the mkdir behind the same ensureArtifactsDir flag as runDistCommand so
|
|
255
|
+
// "the artifacts directory is created only for designated stateful commands"
|
|
256
|
+
// holds on this path too (CE-001); mcp is a designated stateful command and
|
|
257
|
+
// opts in explicitly at its call site.
|
|
258
|
+
if (ensureArtifactsDir) {
|
|
259
|
+
await mkdir(artifactsDir, { recursive: true });
|
|
260
|
+
}
|
|
218
261
|
await ensureBuilt();
|
|
219
262
|
|
|
220
263
|
// Propagate the invocation hint into this (long-lived) server process so it
|
|
@@ -238,13 +281,13 @@ export async function runAuditCodeWrapper({
|
|
|
238
281
|
argv = process.argv.slice(2),
|
|
239
282
|
preferredEntrypoint
|
|
240
283
|
}) {
|
|
241
|
-
if (
|
|
284
|
+
if (hasLeadingFlag(argv, '--help') || hasLeadingFlag(argv, '-h')) {
|
|
242
285
|
printHelp({ usageName, preferredEntrypoint });
|
|
243
286
|
return;
|
|
244
287
|
}
|
|
245
288
|
|
|
246
|
-
if (
|
|
247
|
-
console.log(
|
|
289
|
+
if (hasLeadingFlag(argv, '--version') || hasLeadingFlag(argv, '-v')) {
|
|
290
|
+
console.log(await readPackageVersion());
|
|
248
291
|
return;
|
|
249
292
|
}
|
|
250
293
|
|
|
@@ -280,7 +323,7 @@ export async function runAuditCodeWrapper({
|
|
|
280
323
|
// because they may be the FIRST call in a fresh repo and must create the
|
|
281
324
|
// run directory before dist reads it.
|
|
282
325
|
if (argv[0] === 'mcp') {
|
|
283
|
-
await runDistCommandInline('mcp', argv.slice(1));
|
|
326
|
+
await runDistCommandInline('mcp', argv.slice(1), { ensureArtifactsDir: true });
|
|
284
327
|
return;
|
|
285
328
|
}
|
|
286
329
|
|