mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-16 13:53:04 +08:00
add platform audit export output
Adds JSON/markdown export and write-to-file support for the platform audit operator artifact.
This commit is contained in:
@@ -29,7 +29,11 @@ function usage() {
|
||||
'Operator readiness audit for ECC queue, discussion, roadmap, release, and security evidence.',
|
||||
'',
|
||||
'Options:',
|
||||
' --format <text|json> Output format (default: text)',
|
||||
' --format <text|json|markdown>',
|
||||
' Output format (default: text)',
|
||||
' --json Alias for --format json',
|
||||
' --markdown Alias for --format markdown',
|
||||
' --write <path> Write json or markdown output to a file',
|
||||
' --root <dir> Repository root to inspect (default: cwd)',
|
||||
' --repo <owner/repo> GitHub repo to inspect; repeatable',
|
||||
' --skip-github Skip live GitHub queue/discussion checks',
|
||||
@@ -71,6 +75,7 @@ function parseArgs(argv) {
|
||||
skipGithub: false,
|
||||
thresholds: { ...DEFAULT_THRESHOLDS },
|
||||
useEnvGithubToken: false,
|
||||
writePath: null,
|
||||
};
|
||||
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
@@ -92,6 +97,16 @@ function parseArgs(argv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--json') {
|
||||
parsed.format = 'json';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--markdown') {
|
||||
parsed.format = 'markdown';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--root') {
|
||||
parsed.root = path.resolve(readValue(args, index, arg));
|
||||
index += 1;
|
||||
@@ -130,6 +145,17 @@ function parseArgs(argv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--write') {
|
||||
parsed.writePath = path.resolve(readValue(args, index, arg));
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.startsWith('--write=')) {
|
||||
parsed.writePath = path.resolve(arg.slice('--write='.length));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg === '--max-open-prs') {
|
||||
parsed.thresholds.maxOpenPrs = parseIntegerFlag(readValue(args, index, arg), arg);
|
||||
index += 1;
|
||||
@@ -176,8 +202,12 @@ function parseArgs(argv) {
|
||||
throw new Error(`Unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
if (!['text', 'json'].includes(parsed.format)) {
|
||||
throw new Error(`Invalid format: ${parsed.format}. Use text or json.`);
|
||||
if (!['text', 'json', 'markdown'].includes(parsed.format)) {
|
||||
throw new Error(`Invalid format: ${parsed.format}. Use text, json, or markdown.`);
|
||||
}
|
||||
|
||||
if (parsed.writePath && parsed.format === 'text') {
|
||||
throw new Error('--write requires --json, --markdown, or --format json|markdown');
|
||||
}
|
||||
|
||||
parsed.allowUntracked = parsed.allowUntracked.map(normalizeRelativePrefix);
|
||||
@@ -595,6 +625,101 @@ function renderText(report) {
|
||||
return `${lines.join('\n')}\n`;
|
||||
}
|
||||
|
||||
function markdownEscape(value) {
|
||||
return String(value === undefined || value === null ? '' : value)
|
||||
.replace(/\|/g, '\\|')
|
||||
.replace(/\r?\n/g, '<br>');
|
||||
}
|
||||
|
||||
function markdownStatus(status) {
|
||||
switch (status) {
|
||||
case 'pass':
|
||||
return 'PASS';
|
||||
case 'fail':
|
||||
return 'FAIL';
|
||||
case 'warn':
|
||||
return 'WARN';
|
||||
default:
|
||||
return String(status || 'UNKNOWN').toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
function renderMarkdown(report) {
|
||||
const lines = [
|
||||
'# ECC Platform Audit',
|
||||
'',
|
||||
`Generated: ${report.generatedAt}`,
|
||||
`Status: ${report.ready ? 'ready' : 'attention required'}`,
|
||||
`Root: \`${report.root}\``,
|
||||
'',
|
||||
'## Queue Summary',
|
||||
'',
|
||||
'| Surface | Count | Threshold | Status |',
|
||||
'| --- | ---: | ---: | --- |',
|
||||
`| Open PRs | ${report.github.totals.openPrs} | ${report.thresholds.maxOpenPrs} | ${report.github.totals.openPrs <= report.thresholds.maxOpenPrs ? 'PASS' : 'FAIL'} |`,
|
||||
`| Open issues | ${report.github.totals.openIssues} | ${report.thresholds.maxOpenIssues} | ${report.github.totals.openIssues <= report.thresholds.maxOpenIssues ? 'PASS' : 'FAIL'} |`,
|
||||
`| Discussions needing maintainer touch | ${report.github.totals.discussionsNeedingMaintainerTouch} | 0 | ${report.github.totals.discussionsNeedingMaintainerTouch === 0 ? 'PASS' : 'FAIL'} |`,
|
||||
`| Conflicting open PRs | ${report.github.totals.dirtyPrs} | 0 | ${report.github.totals.dirtyPrs === 0 ? 'PASS' : 'FAIL'} |`,
|
||||
`| Blocking dirty files | ${report.git.blockingDirtyCount} | ${report.thresholds.maxDirtyFiles} | ${report.git.blockingDirtyCount <= report.thresholds.maxDirtyFiles ? 'PASS' : 'FAIL'} |`,
|
||||
'',
|
||||
'## Repositories',
|
||||
'',
|
||||
'| Repository | PRs | Issues | Discussions sampled | Needs maintainer | Dirty PRs |',
|
||||
'| --- | ---: | ---: | ---: | ---: | ---: |',
|
||||
];
|
||||
|
||||
for (const repo of report.github.repos) {
|
||||
lines.push(
|
||||
`| \`${markdownEscape(repo.repo)}\` | ${repo.openPrs || 0} | ${repo.openIssues || 0} | ${repo.discussions ? repo.discussions.sampledCount : 0} | ${repo.discussions ? repo.discussions.needingMaintainerTouch.length : 0} | ${repo.dirtyPrs ? repo.dirtyPrs.length : 0} |`
|
||||
);
|
||||
}
|
||||
|
||||
lines.push(
|
||||
'',
|
||||
'## Checks',
|
||||
'',
|
||||
'| Status | Check | Summary | Evidence |',
|
||||
'| --- | --- | --- | --- |'
|
||||
);
|
||||
|
||||
for (const check of report.checks) {
|
||||
lines.push(
|
||||
`| ${markdownStatus(check.status)} | \`${markdownEscape(check.id)}\` | ${markdownEscape(check.summary)} | ${check.path ? `\`${markdownEscape(check.path)}\`` : ''} |`
|
||||
);
|
||||
}
|
||||
|
||||
lines.push('', '## Top Actions', '');
|
||||
if (report.top_actions.length === 0) {
|
||||
lines.push('- none');
|
||||
} else {
|
||||
for (const action of report.top_actions) {
|
||||
lines.push(`- \`${markdownEscape(action.id)}\`: ${markdownEscape(action.fix)}`);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push('', '## Git State', '');
|
||||
lines.push(`- Branch: ${report.git.branch ? `\`${markdownEscape(report.git.branch)}\`` : '(unknown)'}`);
|
||||
lines.push(`- Ignored dirty files: ${report.git.ignoredDirty.length}`);
|
||||
if (report.git.ignoredDirty.length > 0) {
|
||||
for (const line of report.git.ignoredDirty) {
|
||||
lines.push(` - \`${markdownEscape(line)}\``);
|
||||
}
|
||||
}
|
||||
lines.push(`- Blocking dirty files: ${report.git.blockingDirty.length}`);
|
||||
if (report.git.blockingDirty.length > 0) {
|
||||
for (const line of report.git.blockingDirty) {
|
||||
lines.push(` - \`${markdownEscape(line)}\``);
|
||||
}
|
||||
}
|
||||
|
||||
return `${lines.join('\n')}\n`;
|
||||
}
|
||||
|
||||
function writeOutput(writePath, output) {
|
||||
fs.mkdirSync(path.dirname(writePath), { recursive: true });
|
||||
fs.writeFileSync(writePath, output, 'utf8');
|
||||
}
|
||||
|
||||
function main() {
|
||||
try {
|
||||
const options = parseArgs(process.argv);
|
||||
@@ -606,7 +731,12 @@ function main() {
|
||||
const report = buildReport(options);
|
||||
const output = options.format === 'json'
|
||||
? `${JSON.stringify(report, null, 2)}\n`
|
||||
: renderText(report);
|
||||
: options.format === 'markdown'
|
||||
? renderMarkdown(report)
|
||||
: renderText(report);
|
||||
if (options.writePath) {
|
||||
writeOutput(options.writePath, output);
|
||||
}
|
||||
process.stdout.write(output);
|
||||
|
||||
if (options.exitCode && !report.ready) {
|
||||
@@ -625,6 +755,7 @@ if (require.main === module) {
|
||||
module.exports = {
|
||||
buildReport,
|
||||
parseArgs,
|
||||
renderMarkdown,
|
||||
renderText,
|
||||
runGhJson,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user