openclaw/scripts/lib/report-cli-helpers.mts
RoboClaw fea8a1499a
refactor(scripts): share inventory report CLI parsing (#150212)
Consolidate the two inventory argument loops over existing flag primitives while preserving strict values, errors, ordering, defaults, and report output.

Related: #137293 (retired validator-only proposal); this active QA deliverable retires the complete duplicate parsers.

Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
2026-09-16 18:09:53 -07:00

96 lines
2.9 KiB
TypeScript

// Parses report CLI output arguments and writes optional artifacts.
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import {
booleanFlag,
classifyBoundedUnsignedDecimal,
parseFlagArgs,
stringFlag,
} from "./arg-utils.mts";
type ReportCliArgs = { jsonPath: string | null; markdownPath: string | null; rootDir: string };
export const REPORT_CLI_PARSE_OPTIONS = {
duplicateOptionMessage: (flag: string) => `${flag} was provided more than once.`,
onUnhandledArg(arg: string) {
throw new Error(`Unsupported argument: ${arg}`);
},
};
export function parseReportCliArgs(argv: string[]) {
const options: ReportCliArgs = {
rootDir: process.cwd(),
jsonPath: null,
markdownPath: null,
};
const flagEntries = [
["--root", "rootDir"],
["--json", "jsonPath"],
["--markdown", "markdownPath"],
] satisfies Array<[string, keyof ReportCliArgs]>;
const flagSpecs = flagEntries.map(([flag, key]) =>
stringFlag<ReportCliArgs>(flag, key, {
allowInline: false,
missingValueMessage: `Expected ${flag} <value>.`,
rejectShortOptions: true,
}),
);
return parseFlagArgs(argv, options, flagSpecs, REPORT_CLI_PARSE_OPTIONS);
}
export function parseInventoryReportCliArgs(
argv: string[],
options: { allowIncludeAllowed?: boolean } = {},
) {
const args = {
help: false,
includeAllowed: false,
json: false,
limit: 120,
repoRoot: process.cwd(),
};
const flags = [
booleanFlag<typeof args>("--help", "help", true, { repeatable: true }),
booleanFlag<typeof args>("-h", "help", true, { repeatable: true }),
booleanFlag<typeof args>("--json", "json", true, { repeatable: true }),
stringFlag<typeof args>("--repo-root", "repoRoot", {
allowInline: false,
repeatable: true,
rejectShortOptions: true,
missingValueMessage: "--repo-root expects a path",
}),
stringFlag<typeof args>("--limit", "limit", {
allowInline: false,
repeatable: true,
missingValueMessage: "--limit expects a non-negative integer",
transform(raw) {
const result = classifyBoundedUnsignedDecimal(raw, 0, Number.MAX_SAFE_INTEGER);
if (result.kind !== "value") {
throw new Error("--limit expects a non-negative integer");
}
return result.value;
},
}),
];
if (options.allowIncludeAllowed) {
flags.push(
booleanFlag<typeof args>("--include-allowed", "includeAllowed", true, { repeatable: true }),
);
}
return parseFlagArgs(argv, args, flags, {
onUnhandledArg(arg) {
throw new Error(`Unknown argument: ${arg}`);
},
});
}
/**
* Writes an optional report artifact, creating its parent directory first.
*/
export async function writeReportArtifact(filePath: string | null, content: string) {
if (!filePath) {
return;
}
await mkdir(path.dirname(filePath), { recursive: true });
await writeFile(filePath, content, "utf8");
}