@llm-dev-ops/agentics-cli 2.5.3 → 2.6.0

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.
Files changed (49) hide show
  1. package/dist/cli/index.js +334 -31
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/commands/agents.d.ts +7 -0
  4. package/dist/commands/agents.d.ts.map +1 -1
  5. package/dist/commands/agents.js +130 -23
  6. package/dist/commands/agents.js.map +1 -1
  7. package/dist/contracts/adr-command-semantics.d.ts.map +1 -1
  8. package/dist/contracts/adr-command-semantics.js +12 -0
  9. package/dist/contracts/adr-command-semantics.js.map +1 -1
  10. package/dist/errors/transient.d.ts +67 -0
  11. package/dist/errors/transient.d.ts.map +1 -0
  12. package/dist/errors/transient.js +260 -0
  13. package/dist/errors/transient.js.map +1 -0
  14. package/dist/gates/execution-gate.d.ts.map +1 -1
  15. package/dist/gates/execution-gate.js +11 -0
  16. package/dist/gates/execution-gate.js.map +1 -1
  17. package/dist/mcp/mcp-server.js +13 -1
  18. package/dist/mcp/mcp-server.js.map +1 -1
  19. package/dist/modules/command-parser.d.ts +1 -1
  20. package/dist/modules/command-parser.d.ts.map +1 -1
  21. package/dist/modules/command-parser.js +5 -0
  22. package/dist/modules/command-parser.js.map +1 -1
  23. package/dist/observability/degradations.d.ts +58 -0
  24. package/dist/observability/degradations.d.ts.map +1 -0
  25. package/dist/observability/degradations.js +74 -0
  26. package/dist/observability/degradations.js.map +1 -0
  27. package/dist/pipeline/phase1-verdict.d.ts +55 -0
  28. package/dist/pipeline/phase1-verdict.d.ts.map +1 -0
  29. package/dist/pipeline/phase1-verdict.js +186 -0
  30. package/dist/pipeline/phase1-verdict.js.map +1 -0
  31. package/dist/pipeline/phase2-preflight.d.ts +44 -0
  32. package/dist/pipeline/phase2-preflight.d.ts.map +1 -0
  33. package/dist/pipeline/phase2-preflight.js +120 -0
  34. package/dist/pipeline/phase2-preflight.js.map +1 -0
  35. package/dist/pipeline/swarm-orchestrator.d.ts.map +1 -1
  36. package/dist/pipeline/swarm-orchestrator.js +67 -5
  37. package/dist/pipeline/swarm-orchestrator.js.map +1 -1
  38. package/dist/synthesis/financial-claim-extractor.d.ts +11 -0
  39. package/dist/synthesis/financial-claim-extractor.d.ts.map +1 -1
  40. package/dist/synthesis/financial-claim-extractor.js +24 -0
  41. package/dist/synthesis/financial-claim-extractor.js.map +1 -1
  42. package/dist/synthesis/simulation-artifact-generator.d.ts.map +1 -1
  43. package/dist/synthesis/simulation-artifact-generator.js +28 -3
  44. package/dist/synthesis/simulation-artifact-generator.js.map +1 -1
  45. package/dist/synthesis/simulation-renderers.d.ts +1 -1
  46. package/dist/synthesis/simulation-renderers.d.ts.map +1 -1
  47. package/dist/synthesis/simulation-renderers.js +39 -13
  48. package/dist/synthesis/simulation-renderers.js.map +1 -1
  49. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -1842,51 +1842,173 @@ async function main() {
1842
1842
  // Auto-chain Phases 2-6: each phase builds on the previous phase's output
1843
1843
  let chainResult = null;
1844
1844
  if (routeResult.kind === 'multi') {
1845
- // Only auto-chain if the ruvector simulation actually succeeded (not just dispatched).
1846
- // Check invocations for a successful simulator/enterprise result — if it errored
1847
- // (e.g. ruvector-service unreachable), Phases 2-6 would build on empty artifacts.
1848
- const simSucceeded = routeResult.result.invocations.some((i) => i.intent.domain === 'simulator' && i.intent.agent === 'enterprise' && !('error' in i.result));
1845
+ // ADR-PIPELINE-089 §3: gate auto-chain on the Phase 1 Health
1846
+ // Gate verdict, not just whether the simulator intent returned
1847
+ // without an error field. The verdict classifier looks at the
1848
+ // simulator outcome AND agent error ratio AND artifact presence
1849
+ // AND degradation sink entries (tag-survival, consensus, sector,
1850
+ // local fallbacks), so we catch "it technically succeeded but
1851
+ // produced nothing useful" — the user's actual symptom.
1852
+ const verdict = routeResult.result.phase1Verdict;
1853
+ const verdictKind = verdict?.verdict ?? 'healthy'; // default healthy if older caller path
1849
1854
  const hasSimIntent = routeResult.result.intents.some((i) => i.domain === 'simulator' && i.agent === 'enterprise');
1850
- if (!simSucceeded && hasSimIntent) {
1851
- // Simulation was dispatched but failed — surface the error prominently
1852
- const simError = routeResult.result.invocations.find((i) => i.intent.domain === 'simulator' && i.intent.agent === 'enterprise');
1853
- const errDetail = simError ? simError.result.error ?? 'unknown error' : 'not dispatched';
1855
+ if (verdictKind === 'failed' && hasSimIntent) {
1854
1856
  console.error('');
1855
1857
  console.error('='.repeat(72));
1856
- console.error(' RUVECTOR SIMULATION FAILED — Phases 2-6 skipped');
1858
+ console.error(' PHASE 1 FAILED — Phases 2-6 skipped');
1857
1859
  console.error('='.repeat(72));
1858
- console.error(` Error: ${errDetail}`);
1860
+ console.error(` Reason: ${verdict?.reason ?? 'unknown failure'}`);
1861
+ if (verdict && verdict.degradations.length > 0) {
1862
+ console.error('');
1863
+ console.error(' Degradations:');
1864
+ for (const d of verdict.degradations) {
1865
+ console.error(` · [${d.code}] ${d.message}`);
1866
+ }
1867
+ }
1859
1868
  console.error('');
1860
- console.error(' The enterprise simulation via ruvector-service did not complete.');
1861
- console.error(' Phases 2-6 require simulation artifacts to build on.');
1869
+ console.error(' Phases 2-6 require a healthy Phase 1 to build on.');
1862
1870
  console.error('');
1863
1871
  console.error(' To retry:');
1864
1872
  console.error(` agentics ask "${nlQuery}"`);
1865
1873
  console.error('');
1866
1874
  }
1867
- if (simSucceeded) {
1875
+ if (verdictKind === 'degraded') {
1876
+ // Auto-chain still proceeds on degraded — the user may want
1877
+ // partial output — but we surface the specific degradations
1878
+ // prominently so they can judge whether to re-run instead.
1868
1879
  console.error('');
1869
- console.error('Auto-chaining Phases 2-6 (each phase builds on the previous)...');
1870
- console.error(' Phase 1 (109 agents + ruvector simulation) complete. Starting sequential pipeline:');
1871
- console.error(' Phase 2: Deep Research → Phase 3: SPARC+TDD → Phase 4: ADRs/DDDs → Phase 5: Build (ruflo) → Phase 6: ERP Push');
1880
+ console.error('='.repeat(72));
1881
+ console.error(' PHASE 1 DEGRADED proceeding to Phases 2-6 with caveats');
1882
+ console.error('='.repeat(72));
1883
+ console.error(` ${verdict?.reason ?? 'partial success'}`);
1884
+ if (verdict && verdict.degradations.length > 0) {
1885
+ for (const d of verdict.degradations) {
1886
+ console.error(` · [${d.code}] ${d.message}`);
1887
+ }
1888
+ }
1872
1889
  console.error('');
1873
- try {
1874
- const { executeAutoChain, formatAutoChainForDisplay } = await import('../pipeline/auto-chain.js');
1875
- const traceId = options.trace_id;
1876
- chainResult = await executeAutoChain(traceId, {
1877
- verbose: options.verbose,
1878
- });
1879
- // Output pipeline summary to stdout so it's visible alongside Phase 1 results
1880
- if (chainResult && options.format !== 'json') {
1881
- console.log(formatAutoChainForDisplay(chainResult));
1890
+ }
1891
+ if (verdictKind !== 'failed') {
1892
+ // ADR-PIPELINE-088: under MCP mode, detach the auto-chain into a
1893
+ // background subprocess so the tool call returns within Claude
1894
+ // Code's MCP client-side timeout. Auto-chain phases can take
1895
+ // 30-60 minutes (ruflo swarm start has a 15-minute per-phase
1896
+ // budget) which is far longer than the MCP client will wait.
1897
+ // Shell invocations keep the original inline behaviour.
1898
+ const mcpMode = process.env['MCP_SERVER_MODE'] === '1';
1899
+ const traceId = options.trace_id;
1900
+ if (mcpMode) {
1901
+ try {
1902
+ const { spawn } = await import('node:child_process');
1903
+ const fsMod = await import('node:fs');
1904
+ const pathMod = await import('node:path');
1905
+ const os = await import('node:os');
1906
+ const runDir = pathMod.join(os.homedir(), '.agentics', 'runs', traceId);
1907
+ fsMod.mkdirSync(runDir, { recursive: true, mode: 0o700 });
1908
+ const logPath = pathMod.join(runDir, 'autochain.log');
1909
+ const statusPath = pathMod.join(runDir, 'status.json');
1910
+ const plansDir = pathMod.join(process.cwd(), '.agentics', 'plans');
1911
+ // CLI_ENTRY must be the same dist/cli/index.js that is
1912
+ // running us — __filename-equivalent in ESM.
1913
+ const selfPath = new URL(import.meta.url).pathname;
1914
+ const logFd = fsMod.openSync(logPath, 'a');
1915
+ const child = spawn(process.execPath, [selfPath, '_autochain', traceId], {
1916
+ detached: true,
1917
+ stdio: ['ignore', logFd, logFd],
1918
+ // Clear MCP_SERVER_MODE on the child so it uses the inline
1919
+ // auto-chain path (the whole point of spawning it is to
1920
+ // let it run the full pipeline without timing out).
1921
+ env: { ...process.env, MCP_SERVER_MODE: '', AGENTICS_AUTOCHAIN_TRACE: traceId },
1922
+ cwd: process.cwd(),
1923
+ });
1924
+ child.unref();
1925
+ fsMod.closeSync(logFd);
1926
+ // Write an initial status record so `agentics status <id>`
1927
+ // has something to read before the child updates it.
1928
+ // ADR-PIPELINE-089 §6: seed Phase 1 verdict fields so the
1929
+ // status tool can surface HEALTHY / DEGRADED / FAILED plus
1930
+ // the specific degradation list on the very first poll.
1931
+ const phase1V = routeResult.result.phase1Verdict;
1932
+ const initialStatus = {
1933
+ traceId,
1934
+ pid: child.pid ?? null,
1935
+ startedAt: new Date().toISOString(),
1936
+ mode: 'mcp-fast-return',
1937
+ phase: 0,
1938
+ inProgress: true,
1939
+ completedPhases: [],
1940
+ logPath,
1941
+ plansDir,
1942
+ artifacts: {},
1943
+ phase1_verdict: phase1V?.verdict ?? null,
1944
+ phase1_reason: phase1V?.reason ?? null,
1945
+ phase1_degradations: phase1V?.degradations ?? [],
1946
+ phase1_stats: phase1V?.stats ?? null,
1947
+ };
1948
+ fsMod.writeFileSync(statusPath, JSON.stringify(initialStatus, null, 2), { encoding: 'utf-8', mode: 0o600 });
1949
+ // Emit structured markers so the MCP tool handler (and
1950
+ // downstream Claude Code consumers) can parse them out of
1951
+ // stdout without needing a second JSON-RPC round-trip.
1952
+ console.log('');
1953
+ console.log(`AGENTICS_TRACE_ID=${traceId}`);
1954
+ console.log('AGENTICS_AUTOCHAIN_STATUS=background');
1955
+ console.log(`AGENTICS_AUTOCHAIN_PID=${child.pid ?? ''}`);
1956
+ console.log(`AGENTICS_AUTOCHAIN_LOG=${logPath}`);
1957
+ console.log(`AGENTICS_AUTOCHAIN_STATUS_FILE=${statusPath}`);
1958
+ console.log(`AGENTICS_PLANS_DIR=${plansDir}`);
1959
+ // ADR-PIPELINE-089 §2: surface Phase 1 verdict as a
1960
+ // structured marker so the MCP tool wrapper (and any
1961
+ // log-grepping relay) can read it without parsing the
1962
+ // status file. Count only — the full degradation list is
1963
+ // in status.json.
1964
+ if (phase1V) {
1965
+ console.log(`AGENTICS_PHASE1_VERDICT=${phase1V.verdict}`);
1966
+ console.log(`AGENTICS_PHASE1_DEGRADATIONS=${phase1V.degradations.length}`);
1967
+ }
1968
+ console.log('');
1969
+ console.log('Phases 2-6 are running in the background. Poll with:');
1970
+ console.log(` agentics status ${traceId}`);
1971
+ console.log('ADR-PIPELINE-088.');
1972
+ }
1973
+ catch (detachErr) {
1974
+ const errMsg = detachErr instanceof Error ? detachErr.message : String(detachErr);
1975
+ console.error(`[WARN] MCP fast-return detach failed: ${errMsg}. Falling back to inline auto-chain.`);
1976
+ // Fall through to the inline path below by setting a flag.
1977
+ // We intentionally do not throw — better to block the MCP
1978
+ // call than to lose auto-chain entirely.
1979
+ try {
1980
+ const { executeAutoChain, formatAutoChainForDisplay } = await import('../pipeline/auto-chain.js');
1981
+ chainResult = await executeAutoChain(traceId, { verbose: options.verbose });
1982
+ if (chainResult && options.format !== 'json') {
1983
+ console.log(formatAutoChainForDisplay(chainResult));
1984
+ }
1985
+ }
1986
+ catch (chainErr) {
1987
+ console.error(`\nAuto-chain pipeline error: ${chainErr instanceof Error ? chainErr.message : String(chainErr)}`);
1988
+ }
1882
1989
  }
1883
1990
  }
1884
- catch (chainErr) {
1885
- // Always show auto-chain errors so users know the pipeline failed
1886
- const errMsg = chainErr instanceof Error ? chainErr.message : String(chainErr);
1887
- console.error(`\nAuto-chain pipeline error: ${errMsg}`);
1888
- if (chainErr instanceof Error && chainErr.stack) {
1889
- console.error(chainErr.stack);
1991
+ else {
1992
+ console.error('');
1993
+ console.error('Auto-chaining Phases 2-6 (each phase builds on the previous)...');
1994
+ console.error(' Phase 1 (109 agents + ruvector simulation) complete. Starting sequential pipeline:');
1995
+ console.error(' Phase 2: Deep Research → Phase 3: SPARC+TDD → Phase 4: ADRs/DDDs → Phase 5: Build (ruflo) Phase 6: ERP Push');
1996
+ console.error('');
1997
+ try {
1998
+ const { executeAutoChain, formatAutoChainForDisplay } = await import('../pipeline/auto-chain.js');
1999
+ chainResult = await executeAutoChain(traceId, {
2000
+ verbose: options.verbose,
2001
+ });
2002
+ if (chainResult && options.format !== 'json') {
2003
+ console.log(formatAutoChainForDisplay(chainResult));
2004
+ }
2005
+ }
2006
+ catch (chainErr) {
2007
+ const errMsg = chainErr instanceof Error ? chainErr.message : String(chainErr);
2008
+ console.error(`\nAuto-chain pipeline error: ${errMsg}`);
2009
+ if (chainErr instanceof Error && chainErr.stack) {
2010
+ console.error(chainErr.stack);
2011
+ }
1890
2012
  }
1891
2013
  }
1892
2014
  }
@@ -1944,6 +2066,187 @@ async function main() {
1944
2066
  throw error;
1945
2067
  }
1946
2068
  }
2069
+ // ADR-PIPELINE-088 §3: hidden helper command invoked by the MCP
2070
+ // fast-return path. Runs executeAutoChain for an existing trace id
2071
+ // (phase 1 artifacts already on disk) with stdio attached to the log
2072
+ // file the parent opened. NOT listed in help.
2073
+ case '_autochain': {
2074
+ const traceId = parsed.subcommand ?? parsed.positionalArgs[0];
2075
+ if (!traceId) {
2076
+ console.error('Usage (internal): agentics _autochain <traceId>');
2077
+ process.exit(EXIT_CODES.USAGE_ERROR);
2078
+ }
2079
+ try {
2080
+ const fsMod = await import('node:fs');
2081
+ const pathMod = await import('node:path');
2082
+ const os = await import('node:os');
2083
+ const runDir = pathMod.join(os.homedir(), '.agentics', 'runs', traceId);
2084
+ const statusPath = pathMod.join(runDir, 'status.json');
2085
+ const updateStatus = (patch) => {
2086
+ try {
2087
+ const current = fsMod.existsSync(statusPath)
2088
+ ? JSON.parse(fsMod.readFileSync(statusPath, 'utf-8'))
2089
+ : {};
2090
+ const merged = { ...current, ...patch, updatedAt: new Date().toISOString() };
2091
+ const tmp = statusPath + '.tmp';
2092
+ fsMod.writeFileSync(tmp, JSON.stringify(merged, null, 2), { encoding: 'utf-8', mode: 0o600 });
2093
+ fsMod.renameSync(tmp, statusPath);
2094
+ }
2095
+ catch { /* non-fatal */ }
2096
+ };
2097
+ updateStatus({ phase: 2, inProgress: true, startedAutoChainAt: new Date().toISOString() });
2098
+ const { executeAutoChain } = await import('../pipeline/auto-chain.js');
2099
+ const chainResult = await executeAutoChain(traceId, { verbose: false });
2100
+ updateStatus({
2101
+ phase: 6,
2102
+ inProgress: false,
2103
+ completedAt: new Date().toISOString(),
2104
+ exitCode: 0,
2105
+ chainResult: chainResult ?? null,
2106
+ });
2107
+ process.exit(EXIT_CODES.SUCCESS);
2108
+ }
2109
+ catch (err) {
2110
+ const errMsg = err instanceof Error ? err.message : String(err);
2111
+ console.error(`_autochain failed for ${traceId}: ${errMsg}`);
2112
+ try {
2113
+ const fsMod = await import('node:fs');
2114
+ const pathMod = await import('node:path');
2115
+ const os = await import('node:os');
2116
+ const statusPath = pathMod.join(os.homedir(), '.agentics', 'runs', traceId, 'status.json');
2117
+ const current = fsMod.existsSync(statusPath)
2118
+ ? JSON.parse(fsMod.readFileSync(statusPath, 'utf-8'))
2119
+ : {};
2120
+ const merged = { ...current, inProgress: false, failedAt: new Date().toISOString(), error: errMsg };
2121
+ fsMod.writeFileSync(statusPath, JSON.stringify(merged, null, 2), { encoding: 'utf-8', mode: 0o600 });
2122
+ }
2123
+ catch { /* ignore */ }
2124
+ process.exit(EXIT_CODES.INTERNAL_CLI_ERROR);
2125
+ }
2126
+ }
2127
+ // ADR-PIPELINE-088 §4: public status command. Reports phase completion
2128
+ // of a running or completed agentics-ask background pipeline.
2129
+ case 'status': {
2130
+ const traceId = parsed.subcommand ?? parsed.positionalArgs[0];
2131
+ if (!traceId) {
2132
+ console.error('Usage: agentics status <traceId>');
2133
+ console.error('');
2134
+ console.error('The trace id is emitted as AGENTICS_TRACE_ID=... by agentics-ask under MCP.');
2135
+ process.exit(EXIT_CODES.USAGE_ERROR);
2136
+ }
2137
+ try {
2138
+ const fsMod = await import('node:fs');
2139
+ const pathMod = await import('node:path');
2140
+ const os = await import('node:os');
2141
+ const runDir = pathMod.join(os.homedir(), '.agentics', 'runs', traceId);
2142
+ const statusPath = pathMod.join(runDir, 'status.json');
2143
+ if (!fsMod.existsSync(statusPath)) {
2144
+ console.error(`No status file for trace id ${traceId} at ${statusPath}`);
2145
+ process.exit(EXIT_CODES.CANNOT_OPEN_INPUT);
2146
+ }
2147
+ const status = JSON.parse(fsMod.readFileSync(statusPath, 'utf-8'));
2148
+ // Liveness: if status claims in-progress but the pid is dead, mark crashed.
2149
+ let liveness = 'unknown';
2150
+ const pid = status['pid'];
2151
+ const inProgress = status['inProgress'] === true;
2152
+ if (!inProgress) {
2153
+ liveness = status['failedAt'] ? 'crashed' : 'done';
2154
+ }
2155
+ else if (pid) {
2156
+ try {
2157
+ process.kill(pid, 0); // signal 0 = liveness probe, no kill
2158
+ liveness = 'running';
2159
+ }
2160
+ catch {
2161
+ liveness = 'crashed';
2162
+ }
2163
+ }
2164
+ // Artifact paths on disk — authoritative completion signal per the ADR.
2165
+ const plansDir = status['plansDir']
2166
+ ?? pathMod.join(process.cwd(), '.agentics', 'plans');
2167
+ const artifactSummary = {};
2168
+ for (const sub of ['adrs', 'ddd', 'sparc', 'tdd', 'prompts']) {
2169
+ const subDir = pathMod.join(plansDir, sub);
2170
+ try {
2171
+ artifactSummary[sub] = fsMod.existsSync(subDir)
2172
+ ? fsMod.readdirSync(subDir).length
2173
+ : 0;
2174
+ }
2175
+ catch {
2176
+ artifactSummary[sub] = 0;
2177
+ }
2178
+ }
2179
+ const report = {
2180
+ traceId,
2181
+ liveness,
2182
+ phase: status['phase'] ?? 0,
2183
+ inProgress,
2184
+ startedAt: status['startedAt'] ?? null,
2185
+ updatedAt: status['updatedAt'] ?? null,
2186
+ completedAt: status['completedAt'] ?? null,
2187
+ failedAt: status['failedAt'] ?? null,
2188
+ error: status['error'] ?? null,
2189
+ logPath: status['logPath'] ?? null,
2190
+ plansDir,
2191
+ artifacts: artifactSummary,
2192
+ // ADR-PIPELINE-089 §6: pass Phase 1 verdict + Phase 2 preflight
2193
+ // through verbatim. Null-safe so runs predating 089 still report.
2194
+ phase1_verdict: status['phase1_verdict'] ?? null,
2195
+ phase1_reason: status['phase1_reason'] ?? null,
2196
+ phase1_degradations: status['phase1_degradations'] ?? [],
2197
+ phase1_stats: status['phase1_stats'] ?? null,
2198
+ phase2_preflight: status['phase2_preflight'] ?? null,
2199
+ };
2200
+ if (options.format === 'json') {
2201
+ console.log(JSON.stringify(report, null, parsed.flags['pretty'] ? 2 : 0));
2202
+ }
2203
+ else {
2204
+ console.log(`Trace id: ${report.traceId}`);
2205
+ console.log(`Liveness: ${report.liveness}`);
2206
+ console.log(`Phase: ${report.phase}${report.inProgress ? ' (in progress)' : ' (done)'}`);
2207
+ if (report.startedAt)
2208
+ console.log(`Started: ${report.startedAt}`);
2209
+ if (report.updatedAt)
2210
+ console.log(`Updated: ${report.updatedAt}`);
2211
+ if (report.completedAt)
2212
+ console.log(`Completed: ${report.completedAt}`);
2213
+ if (report.failedAt)
2214
+ console.log(`Failed: ${report.failedAt}`);
2215
+ if (report.error)
2216
+ console.log(`Error: ${report.error}`);
2217
+ console.log(`Logs: ${report.logPath ?? '(none)'}`);
2218
+ console.log(`Plans dir: ${report.plansDir}`);
2219
+ // ADR-PIPELINE-089 §6: verdict + preflight in the text report.
2220
+ if (report.phase1_verdict) {
2221
+ const v = String(report.phase1_verdict).toUpperCase();
2222
+ console.log(`Phase 1: ${v}${report.phase1_reason ? ' — ' + report.phase1_reason : ''}`);
2223
+ const degs = report.phase1_degradations;
2224
+ for (const d of degs) {
2225
+ console.log(` · [${d.code}] ${d.message}`);
2226
+ }
2227
+ }
2228
+ const pf = report.phase2_preflight;
2229
+ if (pf) {
2230
+ console.log(`Phase 2 pre: ${pf.healthy.length}/${pf.probed} backends healthy (${pf.verdict})`);
2231
+ if (pf.unhealthy.length > 0) {
2232
+ console.log(` unhealthy: ${pf.unhealthy.join(', ')}`);
2233
+ }
2234
+ if (pf.skipped.length > 0) {
2235
+ console.log(` skipped: ${pf.skipped.join(', ')}`);
2236
+ }
2237
+ }
2238
+ console.log('Artifacts:');
2239
+ for (const [k, n] of Object.entries(artifactSummary)) {
2240
+ console.log(` ${k.padEnd(8)} ${n} files`);
2241
+ }
2242
+ }
2243
+ process.exit(EXIT_CODES.SUCCESS);
2244
+ }
2245
+ catch (err) {
2246
+ console.error(`status failed: ${err instanceof Error ? err.message : String(err)}`);
2247
+ process.exit(EXIT_CODES.INTERNAL_CLI_ERROR);
2248
+ }
2249
+ }
1947
2250
  default: {
1948
2251
  // Before failing, attempt NL routing for unrecognized multi-word input
1949
2252
  const nlParts = [parsed.command];