feat: add markdown status snapshots

This commit is contained in:
Affaan Mustafa 2026-05-11 10:00:15 -04:00 committed by Affaan Mustafa
parent 4449bc77ce
commit d2760d0359
4 changed files with 170 additions and 2 deletions

View File

@ -94,6 +94,7 @@ This repo is the raw code only. The guides explain everything.
- **Media and launch tooling**`manim-video`, `remotion-video-creation`, and upgraded social publishing surfaces make technical explainers and launch content part of the same system. - **Media and launch tooling**`manim-video`, `remotion-video-creation`, and upgraded social publishing surfaces make technical explainers and launch content part of the same system.
- **Framework and product surface growth**`nestjs-patterns`, richer Codex/OpenCode install surfaces, and expanded cross-harness packaging keep the repo usable beyond Claude Code alone. - **Framework and product surface growth**`nestjs-patterns`, richer Codex/OpenCode install surfaces, and expanded cross-harness packaging keep the repo usable beyond Claude Code alone.
- **ECC 2.0 alpha is in-tree** — the Rust control-plane prototype in `ecc2/` now builds locally and exposes `dashboard`, `start`, `sessions`, `status`, `stop`, `resume`, and `daemon` commands. It is usable as an alpha, not yet a general release. - **ECC 2.0 alpha is in-tree** — the Rust control-plane prototype in `ecc2/` now builds locally and exposes `dashboard`, `start`, `sessions`, `status`, `stop`, `resume`, and `daemon` commands. It is usable as an alpha, not yet a general release.
- **Operator status snapshots**`ecc status --markdown --write status.md` turns the local state store into a portable handoff covering active sessions, skill-run health, install health, and pending governance events.
- **Ecosystem hardening** — AgentShield, ECC Tools cost controls, billing portal work, and website refreshes continue to ship around the core plugin instead of drifting into separate silos. - **Ecosystem hardening** — AgentShield, ECC Tools cost controls, billing portal work, and website refreshes continue to ship around the core plugin instead of drifting into separate silos.
### v1.9.0 — Selective Install & Language Expansion (Mar 2026) ### v1.9.0 — Selective Install & Language Expansion (Mar 2026)

View File

@ -108,6 +108,7 @@ Examples:
ecc repair --dry-run ecc repair --dry-run
ecc auto-update --dry-run ecc auto-update --dry-run
ecc status --json ecc status --json
ecc status --markdown --write status.md
ecc sessions ecc sessions
ecc sessions session-active --json ecc sessions session-active --json
ecc session-inspect claude:latest ecc session-inspect claude:latest

View File

@ -1,12 +1,14 @@
#!/usr/bin/env node #!/usr/bin/env node
'use strict'; 'use strict';
const fs = require('fs');
const os = require('os'); const os = require('os');
const path = require('path');
const { createStateStore } = require('./lib/state-store'); const { createStateStore } = require('./lib/state-store');
function showHelp(exitCode = 0) { function showHelp(exitCode = 0) {
console.log(` console.log(`
Usage: node scripts/status.js [--db <path>] [--json] [--limit <n>] Usage: node scripts/status.js [--db <path>] [--json|--markdown] [--write <path>] [--limit <n>]
Query the ECC SQLite state store for active sessions, recent skill runs, Query the ECC SQLite state store for active sessions, recent skill runs,
install health, and pending governance events. install health, and pending governance events.
@ -19,6 +21,8 @@ function parseArgs(argv) {
const parsed = { const parsed = {
dbPath: null, dbPath: null,
json: false, json: false,
markdown: false,
writePath: null,
help: false, help: false,
limit: 5, limit: 5,
}; };
@ -31,6 +35,11 @@ function parseArgs(argv) {
index += 1; index += 1;
} else if (arg === '--json') { } else if (arg === '--json') {
parsed.json = true; parsed.json = true;
} else if (arg === '--markdown') {
parsed.markdown = true;
} else if (arg === '--write') {
parsed.writePath = args[index + 1] || null;
index += 1;
} else if (arg === '--limit') { } else if (arg === '--limit') {
parsed.limit = args[index + 1] || null; parsed.limit = args[index + 1] || null;
index += 1; index += 1;
@ -41,6 +50,22 @@ function parseArgs(argv) {
} }
} }
if (parsed.json && parsed.markdown) {
throw new Error('Choose only one output format: --json or --markdown');
}
if (args.includes('--db') && !parsed.dbPath) {
throw new Error('Missing value for --db');
}
if (args.includes('--write') && !parsed.writePath) {
throw new Error('Missing value for --write');
}
if (args.includes('--limit') && !parsed.limit) {
throw new Error('Missing value for --limit');
}
return parsed; return parsed;
} }
@ -129,6 +154,108 @@ function printHuman(payload) {
printGovernance(payload.governance); printGovernance(payload.governance);
} }
function formatPercent(value) {
return value === null ? 'n/a' : `${value}%`;
}
function formatCode(value) {
return `\`${String(value || '').replace(/`/g, '\\`')}\``;
}
function renderMarkdown(payload) {
const lines = [
'# ECC Status',
'',
`Generated: ${payload.generatedAt}`,
`Database: ${formatCode(payload.dbPath)}`,
'',
'## Active Sessions',
'',
`Active sessions: ${payload.activeSessions.activeCount}`,
];
if (payload.activeSessions.sessions.length === 0) {
lines.push('- none');
} else {
for (const session of payload.activeSessions.sessions) {
lines.push(`- ${formatCode(session.id)} [${session.harness}/${session.adapterId}] ${session.state}`);
lines.push(` - Repo: ${session.repoRoot || '(unknown)'}`);
lines.push(` - Started: ${session.startedAt || '(unknown)'}`);
lines.push(` - Workers: ${session.workerCount}`);
}
}
const skillSummary = payload.skillRuns.summary;
lines.push(
'',
'## Skill Runs',
'',
`Window size: ${payload.skillRuns.windowSize}`,
`Success: ${skillSummary.successCount}`,
`Failure: ${skillSummary.failureCount}`,
`Unknown: ${skillSummary.unknownCount}`,
`Success rate: ${formatPercent(skillSummary.successRate)}`,
`Failure rate: ${formatPercent(skillSummary.failureRate)}`
);
if (payload.skillRuns.recent.length === 0) {
lines.push('', 'Recent runs: none');
} else {
lines.push('', 'Recent runs:');
for (const skillRun of payload.skillRuns.recent.slice(0, 5)) {
lines.push(`- ${formatCode(skillRun.id)} ${skillRun.outcome} ${skillRun.skillId}@${skillRun.skillVersion}`);
}
}
lines.push(
'',
'## Install Health',
'',
`Install health: ${payload.installHealth.status}`,
`Targets recorded: ${payload.installHealth.totalCount}`,
`Healthy: ${payload.installHealth.healthyCount}`,
`Warning: ${payload.installHealth.warningCount}`
);
if (payload.installHealth.installations.length === 0) {
lines.push('', 'Installations: none');
} else {
lines.push('', 'Installations:');
for (const installation of payload.installHealth.installations.slice(0, 5)) {
lines.push(`- ${formatCode(installation.targetId)} ${installation.status}`);
lines.push(` - Root: ${installation.targetRoot}`);
lines.push(` - Profile: ${installation.profile || '(custom)'}`);
lines.push(` - Modules: ${installation.moduleCount}`);
lines.push(` - Source version: ${installation.sourceVersion || '(unknown)'}`);
}
}
lines.push(
'',
'## Governance',
'',
`Pending governance events: ${payload.governance.pendingCount}`
);
if (payload.governance.events.length === 0) {
lines.push('- none');
} else {
for (const event of payload.governance.events) {
lines.push(`- ${formatCode(event.id)} ${event.eventType}`);
lines.push(` - Session: ${event.sessionId || '(none)'}`);
lines.push(` - Created: ${event.createdAt}`);
}
}
return `${lines.join('\n')}\n`;
}
function writeOutput(writePath, output) {
const absolutePath = path.resolve(writePath);
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
fs.writeFileSync(absolutePath, output, 'utf8');
}
async function main() { async function main() {
let store = null; let store = null;
@ -153,8 +280,21 @@ async function main() {
}; };
if (options.json) { if (options.json) {
console.log(JSON.stringify(payload, null, 2)); const output = `${JSON.stringify(payload, null, 2)}\n`;
if (options.writePath) {
writeOutput(options.writePath, output);
}
process.stdout.write(output);
} else if (options.markdown) {
const output = renderMarkdown(payload);
if (options.writePath) {
writeOutput(options.writePath, output);
}
process.stdout.write(output);
} else { } else {
if (options.writePath) {
throw new Error('--write requires --json or --markdown');
}
printHuman(payload); printHuman(payload);
} }
} catch (error) { } catch (error) {
@ -174,4 +314,5 @@ if (require.main === module) {
module.exports = { module.exports = {
main, main,
parseArgs, parseArgs,
renderMarkdown,
}; };

View File

@ -576,6 +576,31 @@ async function runTests() {
} }
})) passed += 1; else failed += 1; })) passed += 1; else failed += 1;
if (await test('status CLI can emit and write markdown operator snapshots', async () => {
const testDir = createTempDir('ecc-state-cli-');
const dbPath = path.join(testDir, 'state.db');
const outputPath = path.join(testDir, 'status.md');
try {
await seedStore(dbPath);
const result = runNode(STATUS_SCRIPT, ['--db', dbPath, '--markdown', '--write', outputPath]);
assert.strictEqual(result.status, 0, result.stderr);
assert.ok(fs.existsSync(outputPath));
const written = fs.readFileSync(outputPath, 'utf8');
assert.strictEqual(result.stdout, written);
assert.match(written, /^# ECC Status/m);
assert.match(written, /Database: `[^`]+state\.db`/);
assert.match(written, /- `session-active` \[claude\/dmux-tmux\] active/);
assert.match(written, /Success rate: 66\.7%/);
assert.match(written, /Install health: healthy/);
assert.match(written, /Pending governance events: 1/);
} finally {
cleanupTempDir(testDir);
}
})) passed += 1; else failed += 1;
if (await test('sessions CLI supports list and detail views in human-readable and --json output', async () => { if (await test('sessions CLI supports list and detail views in human-readable and --json output', async () => {
const testDir = createTempDir('ecc-state-cli-'); const testDir = createTempDir('ecc-state-cli-');
const dbPath = path.join(testDir, 'state.db'); const dbPath = path.join(testDir, 'state.db');