@compaction/cli 0.1.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 (143) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +319 -0
  3. package/dist/cli/commands/adapter.d.ts +2 -0
  4. package/dist/cli/commands/adapter.js +49 -0
  5. package/dist/cli/commands/aggregate.d.ts +2 -0
  6. package/dist/cli/commands/aggregate.js +60 -0
  7. package/dist/cli/commands/analyze.d.ts +2 -0
  8. package/dist/cli/commands/analyze.js +55 -0
  9. package/dist/cli/commands/apply-context.d.ts +12 -0
  10. package/dist/cli/commands/apply-context.js +80 -0
  11. package/dist/cli/commands/apply.d.ts +2 -0
  12. package/dist/cli/commands/apply.js +65 -0
  13. package/dist/cli/commands/approve.d.ts +2 -0
  14. package/dist/cli/commands/approve.js +55 -0
  15. package/dist/cli/commands/audit.d.ts +2 -0
  16. package/dist/cli/commands/audit.js +63 -0
  17. package/dist/cli/commands/billing-delta.d.ts +2 -0
  18. package/dist/cli/commands/billing-delta.js +246 -0
  19. package/dist/cli/commands/capture-claude-code.d.ts +19 -0
  20. package/dist/cli/commands/capture-claude-code.js +198 -0
  21. package/dist/cli/commands/capture-provider-usage.d.ts +35 -0
  22. package/dist/cli/commands/capture-provider-usage.js +113 -0
  23. package/dist/cli/commands/capture.d.ts +2 -0
  24. package/dist/cli/commands/capture.js +76 -0
  25. package/dist/cli/commands/compact.d.ts +2 -0
  26. package/dist/cli/commands/compact.js +206 -0
  27. package/dist/cli/commands/eval.d.ts +2 -0
  28. package/dist/cli/commands/eval.js +66 -0
  29. package/dist/cli/commands/export-session-seed.d.ts +11 -0
  30. package/dist/cli/commands/export-session-seed.js +42 -0
  31. package/dist/cli/commands/feedback.d.ts +2 -0
  32. package/dist/cli/commands/feedback.js +233 -0
  33. package/dist/cli/commands/import.d.ts +2 -0
  34. package/dist/cli/commands/import.js +60 -0
  35. package/dist/cli/commands/init.d.ts +2 -0
  36. package/dist/cli/commands/init.js +216 -0
  37. package/dist/cli/commands/integrations.d.ts +2 -0
  38. package/dist/cli/commands/integrations.js +29 -0
  39. package/dist/cli/commands/launch-treatment-session.d.ts +14 -0
  40. package/dist/cli/commands/launch-treatment-session.js +50 -0
  41. package/dist/cli/commands/optimize.d.ts +2 -0
  42. package/dist/cli/commands/optimize.js +33 -0
  43. package/dist/cli/commands/recommend.d.ts +4 -0
  44. package/dist/cli/commands/recommend.js +66 -0
  45. package/dist/cli/commands/review.d.ts +2 -0
  46. package/dist/cli/commands/review.js +60 -0
  47. package/dist/cli/commands/run.d.ts +2 -0
  48. package/dist/cli/commands/run.js +49 -0
  49. package/dist/cli/commands/spend.d.ts +2 -0
  50. package/dist/cli/commands/spend.js +24 -0
  51. package/dist/cli/commands/summary.d.ts +2 -0
  52. package/dist/cli/commands/summary.js +19 -0
  53. package/dist/cli/engine-degrade.d.ts +40 -0
  54. package/dist/cli/engine-degrade.js +59 -0
  55. package/dist/cli/index.d.ts +2 -0
  56. package/dist/cli/index.js +59 -0
  57. package/dist/core/adapter-validation.d.ts +32 -0
  58. package/dist/core/adapter-validation.js +185 -0
  59. package/dist/core/adapters/claude-code-adapter.d.ts +51 -0
  60. package/dist/core/adapters/claude-code-adapter.js +539 -0
  61. package/dist/core/adapters/claude-code-discovery.d.ts +40 -0
  62. package/dist/core/adapters/claude-code-discovery.js +100 -0
  63. package/dist/core/aggregate-format.d.ts +62 -0
  64. package/dist/core/aggregate-format.js +349 -0
  65. package/dist/core/api-client/client.d.ts +43 -0
  66. package/dist/core/api-client/client.js +143 -0
  67. package/dist/core/api-client/config.d.ts +36 -0
  68. package/dist/core/api-client/config.js +44 -0
  69. package/dist/core/api-client/index.d.ts +13 -0
  70. package/dist/core/api-client/index.js +14 -0
  71. package/dist/core/api-client/payload.d.ts +93 -0
  72. package/dist/core/api-client/payload.js +180 -0
  73. package/dist/core/api-client/types.d.ts +95 -0
  74. package/dist/core/api-client/types.js +21 -0
  75. package/dist/core/artifact-writer.d.ts +2 -0
  76. package/dist/core/artifact-writer.js +14 -0
  77. package/dist/core/audit-report.d.ts +91 -0
  78. package/dist/core/audit-report.js +422 -0
  79. package/dist/core/billing-delta/billing-delta-record.d.ts +290 -0
  80. package/dist/core/billing-delta/billing-delta-record.js +394 -0
  81. package/dist/core/capture-adapter.d.ts +74 -0
  82. package/dist/core/capture-adapter.js +2 -0
  83. package/dist/core/command-runner.d.ts +29 -0
  84. package/dist/core/command-runner.js +96 -0
  85. package/dist/core/content-hash.d.ts +1 -0
  86. package/dist/core/content-hash.js +5 -0
  87. package/dist/core/cost-calculator.d.ts +6 -0
  88. package/dist/core/cost-calculator.js +20 -0
  89. package/dist/core/feedback-bundle.d.ts +159 -0
  90. package/dist/core/feedback-bundle.js +406 -0
  91. package/dist/core/openai-agents-capture.d.ts +66 -0
  92. package/dist/core/openai-agents-capture.js +475 -0
  93. package/dist/core/policy-types.d.ts +85 -0
  94. package/dist/core/policy-types.js +29 -0
  95. package/dist/core/pricing.d.ts +23 -0
  96. package/dist/core/pricing.js +55 -0
  97. package/dist/core/provider-usage/credential-redaction.d.ts +23 -0
  98. package/dist/core/provider-usage/credential-redaction.js +60 -0
  99. package/dist/core/provider-usage/provider-usage-adapter.d.ts +65 -0
  100. package/dist/core/provider-usage/provider-usage-adapter.js +147 -0
  101. package/dist/core/provider-usage/provider-usage-client.d.ts +89 -0
  102. package/dist/core/provider-usage/provider-usage-client.js +96 -0
  103. package/dist/core/report-generator.d.ts +7 -0
  104. package/dist/core/report-generator.js +275 -0
  105. package/dist/core/run-aggregator.d.ts +50 -0
  106. package/dist/core/run-aggregator.js +251 -0
  107. package/dist/core/run-labels.d.ts +52 -0
  108. package/dist/core/run-labels.js +80 -0
  109. package/dist/core/run-trace-converter.d.ts +3 -0
  110. package/dist/core/run-trace-converter.js +94 -0
  111. package/dist/core/safety-report.d.ts +52 -0
  112. package/dist/core/safety-report.js +440 -0
  113. package/dist/core/savings.d.ts +22 -0
  114. package/dist/core/savings.js +36 -0
  115. package/dist/core/session-aggregate.d.ts +211 -0
  116. package/dist/core/session-aggregate.js +490 -0
  117. package/dist/core/skill-injection-detector.d.ts +51 -0
  118. package/dist/core/skill-injection-detector.js +146 -0
  119. package/dist/core/spend-attribution.d.ts +169 -0
  120. package/dist/core/spend-attribution.js +611 -0
  121. package/dist/core/standalone-capsule-provenance.d.ts +4 -0
  122. package/dist/core/standalone-capsule-provenance.js +62 -0
  123. package/dist/core/state-capsule.d.ts +3 -0
  124. package/dist/core/state-capsule.js +147 -0
  125. package/dist/core/token-accounting.d.ts +62 -0
  126. package/dist/core/token-accounting.js +81 -0
  127. package/dist/core/token-estimator.d.ts +3 -0
  128. package/dist/core/token-estimator.js +18 -0
  129. package/dist/core/trace-adapters.d.ts +53 -0
  130. package/dist/core/trace-adapters.js +714 -0
  131. package/dist/core/trace-fingerprint.d.ts +79 -0
  132. package/dist/core/trace-fingerprint.js +102 -0
  133. package/dist/core/trace-intake.d.ts +41 -0
  134. package/dist/core/trace-intake.js +164 -0
  135. package/dist/core/trace-parser.d.ts +203 -0
  136. package/dist/core/trace-parser.js +143 -0
  137. package/dist/core/types.d.ts +238 -0
  138. package/dist/core/types.js +2 -0
  139. package/dist/core/usage-metadata.d.ts +51 -0
  140. package/dist/core/usage-metadata.js +122 -0
  141. package/dist/core/waste-detector.d.ts +8 -0
  142. package/dist/core/waste-detector.js +58 -0
  143. package/package.json +90 -0
@@ -0,0 +1,198 @@
1
+ import { writeJsonArtifact, writeTextArtifact } from "../../core/artifact-writer.js";
2
+ import { ClaudeCodeAdapter, PRIVACY_WARNING } from "../../core/adapters/claude-code-adapter.js";
3
+ import { discoverClaudeCodeSessions, DISCOVERY_PRIVACY_NOTE } from "../../core/adapters/claude-code-discovery.js";
4
+ function buildOutputDir(sessionPath, out) {
5
+ if (out)
6
+ return out;
7
+ // Default: .compaction/runs/<session-id-prefix>/
8
+ const sessionFileName = sessionPath.split("/").pop() ?? "session";
9
+ const sessionIdPrefix = sessionFileName.replace(/\.jsonl$/, "").slice(0, 8);
10
+ return `.compaction/runs/${sessionIdPrefix}`;
11
+ }
12
+ function buildMarkdownReport(sessionPath, outDir, provenance, usage) {
13
+ const lines = [
14
+ "# Capture Report",
15
+ "",
16
+ `**Session:** ${sessionPath}`,
17
+ `**Captured at:** ${provenance.capturedAt}`,
18
+ `**Adapter:** ${provenance.captureAdapter}`,
19
+ `**Session ID:** ${provenance.sessionId ?? "unknown"}`,
20
+ "",
21
+ "## Usage",
22
+ "",
23
+ `- Input tokens: ${usage.input_tokens ?? "unknown"}`,
24
+ `- Output tokens: ${usage.output_tokens ?? "unknown"}`,
25
+ `- Cache read tokens: ${usage.cache_read_input_tokens ?? 0}`,
26
+ `- Cache creation tokens: ${usage.cache_creation_input_tokens ?? 0}`,
27
+ `- Total tokens: ${usage.total_tokens ?? "unknown"}`,
28
+ `- Token-count source: ${usage.provider_reported_tokens ? "provider-reported (Claude Code session usage fields) — NOT billing-confirmed" : usage.estimated_tokens ? "locally estimated (chars/4) — NOT provider-reported" : "missing / unknown"}`,
29
+ `- Provider reported: ${usage.provider_reported_tokens}`,
30
+ `- Cost source: ${usage.cost_source}`,
31
+ `- Cost confidence: ${usage.cost_confidence}`,
32
+ "",
33
+ "## Distinctness",
34
+ "",
35
+ ...(provenance.traceFingerprint
36
+ ? [
37
+ `- Trace fingerprint (${provenance.traceFingerprint.algorithm}): ${provenance.traceFingerprint.content_sha256}`,
38
+ `- Messages hashed: ${provenance.traceFingerprint.message_count}`,
39
+ "- This is a one-way SHA-256 DIGEST over the canonical normalized trace content — NOT raw",
40
+ " messages/prompts/tool-output. It is a per-run distinctness proof: re-capturing the same",
41
+ " session yields the same fingerprint, so re-captures are NOT counted as new sessions.",
42
+ " Matching aggregate metrics alone do NOT prove two sessions are the same — only a matching",
43
+ " fingerprint does."
44
+ ]
45
+ : [
46
+ "- Trace fingerprint: not available — distinctness is `not_verified` for this run.",
47
+ " Without a fingerprint this run must NOT be counted as a distinct session."
48
+ ]),
49
+ "",
50
+ "## Warnings",
51
+ "",
52
+ ...provenance.warnings.map((w) => `- ${w}`),
53
+ "",
54
+ "## Limitations",
55
+ "",
56
+ ...provenance.limitations.map((l) => `- ${l}`),
57
+ "",
58
+ "## Artifacts",
59
+ "",
60
+ `- \`${outDir}/captured-trace.json\` — AgentTrace (source: real_captured)`,
61
+ `- \`${outDir}/capture-report.json\` — CapturedRun metadata`,
62
+ `- \`${outDir}/capture-report.md\` — this file`,
63
+ ""
64
+ ];
65
+ return lines.join("\n");
66
+ }
67
+ export async function captureClaudeCodeCommand(options) {
68
+ const maxToolResultChars = options.maxToolResultChars !== undefined
69
+ ? parseInt(options.maxToolResultChars, 10)
70
+ : 32_000;
71
+ const includeSubagents = options.includeSubagents === true;
72
+ // 1. Print privacy warning
73
+ console.log(PRIVACY_WARNING);
74
+ console.log();
75
+ const adapter = new ClaudeCodeAdapter();
76
+ // 2. Print reading session
77
+ console.log(`Reading session: ${options.session}`);
78
+ const capturedRun = await adapter.normalize({
79
+ sourcePath: options.session,
80
+ options: { maxToolResultChars, includeSubagents }
81
+ });
82
+ const { trace, usage, provenance } = capturedRun;
83
+ // 3. Print entry counts (from limitations field)
84
+ const assistantLine = provenance.limitations.find((l) => l.startsWith("Assistant entries:"));
85
+ const userLine = provenance.limitations.find((l) => l.startsWith("User entries:"));
86
+ const otherLine = provenance.limitations.find((l) => l.startsWith("Other/excluded entries:"));
87
+ const assistantCount = assistantLine ? assistantLine.replace("Assistant entries: ", "") : "?";
88
+ const userCount = userLine ? userLine.replace("User entries: ", "") : "?";
89
+ const otherCount = otherLine ? otherLine.replace("Other/excluded entries: ", "") : "?";
90
+ console.log(`Entries: ${assistantCount} assistant, ${userCount} user, ${otherCount} other`);
91
+ // 4. Print thinking blocks excluded
92
+ const thinkingLine = provenance.limitations.find((l) => l.startsWith("Thinking blocks excluded:"));
93
+ const thinkingCount = thinkingLine ? thinkingLine.replace("Thinking blocks excluded: ", "") : "0";
94
+ console.log(`Thinking blocks excluded: ${thinkingCount}`);
95
+ // 4b. Print subagents included (when --include-subagents was passed)
96
+ if (includeSubagents && provenance.subagents !== undefined) {
97
+ console.log(`Subagents included: ${provenance.subagents.length}`);
98
+ }
99
+ // 5. Print messages extracted
100
+ console.log(`Messages extracted: ${trace.messages.length}`);
101
+ // 6. Print token totals (cache counts from structured usage fields)
102
+ console.log(`Token totals: input=${usage.input_tokens ?? 0}, output=${usage.output_tokens ?? 0}, cache_creation=${usage.cache_creation_input_tokens ?? 0}, cache_read=${usage.cache_read_input_tokens ?? 0}`);
103
+ // 6b. Label the token-count source honestly (§13): provider-reported vs locally estimated.
104
+ // Claude Code session files embed provider usage, so these are typically provider-reported;
105
+ // never present an estimate as provider-reported.
106
+ const tokenSourceLabel = usage.provider_reported_tokens
107
+ ? "provider-reported (from the Claude Code session usage fields) — NOT billing-confirmed"
108
+ : usage.estimated_tokens
109
+ ? "locally estimated (chars/4) — NOT provider-reported, NOT billing-confirmed"
110
+ : "missing / unknown (not invented)";
111
+ console.log(`Token-count source: ${tokenSourceLabel}`);
112
+ // 6b-ii. Set expectations honestly for the downstream commands. The totals above are
113
+ // SESSION-LEVEL provider-reported figures (from the session's own API usage metadata). The
114
+ // per-message / policy before-after figures that `spend` and `compact` compute are deterministic
115
+ // LOCAL ESTIMATES (chars/4) — a different measurement — and are labeled as such there. This is the
116
+ // repo's standing discipline (session totals provider-reported; per-message figures chars/4
117
+ // estimates), not a defect: the two are not conflated, and neither is billing-confirmed.
118
+ if (usage.provider_reported_tokens) {
119
+ console.log("Note: the totals above are SESSION-LEVEL provider-reported counts. The per-message before/after");
120
+ console.log(" figures shown by 'spend' and 'compact' are deterministic LOCAL ESTIMATES (chars/4) — a");
121
+ console.log(" different measurement, labeled there as local estimates; neither figure is billing-confirmed.");
122
+ }
123
+ // 6c. Per-run distinctness fingerprint (a one-way digest over canonical normalized
124
+ // content — NOT raw content). Lets a re-capture of the same session be told apart
125
+ // from a genuinely new one; when unavailable, distinctness is `not_verified`.
126
+ if (provenance.traceFingerprint) {
127
+ console.log(`Trace fingerprint (distinctness, digest only — NOT raw content): ${provenance.traceFingerprint.content_sha256}`);
128
+ }
129
+ else {
130
+ console.log("Trace fingerprint: not available — distinctness not_verified (do not count as distinct).");
131
+ }
132
+ // Write artifacts
133
+ const outDir = buildOutputDir(options.session, options.out);
134
+ const capturedRunArtifact = { trace, usage, provenance };
135
+ const markdownReport = buildMarkdownReport(options.session, outDir, provenance, usage);
136
+ await writeJsonArtifact(outDir, "captured-trace.json", trace);
137
+ await writeJsonArtifact(outDir, "capture-report.json", capturedRunArtifact);
138
+ await writeTextArtifact(outDir, "capture-report.md", markdownReport);
139
+ // 7. Print artifacts written
140
+ console.log(`Artifacts written to: ${outDir}/`);
141
+ // 8. Print next step — chain into the verified strong-MVP Claude Code flow:
142
+ // spend (where the spend goes) → compact --eval (compaction + strong apply-readiness:
143
+ // deterministic recoverability + commitment-preservation + fixture task-check + token/cost).
144
+ // `analyze` remains available for a quick read-only estimate, but the strong flow is `spend`
145
+ // then `compact --eval`, so point the user there.
146
+ console.log("");
147
+ console.log("Next (Claude Code strong flow):");
148
+ console.log(` 1. compaction spend ${outDir}/captured-trace.json # where the tokens/cost go (local estimate)`);
149
+ console.log(` 2. compaction compact ${outDir}/captured-trace.json --eval --out <dir>`);
150
+ console.log(" # compaction + strong apply-readiness: deterministic recoverability, commitment-preservation,");
151
+ console.log(" # fixture task-check, and token/cost accounting — nothing is applied automatically.");
152
+ console.log(` (or: compaction analyze ${outDir}/captured-trace.json for a quick read-only estimate)`);
153
+ }
154
+ /**
155
+ * Discover local Claude Code sessions (metadata only) and print, for each, the
156
+ * ready-to-run `capture claude-code --session <path>` command (guided capture).
157
+ *
158
+ * Local and read-only: scans the projects directory only, makes no network call
159
+ * and no upload, and never prints session message content — metadata only.
160
+ */
161
+ export async function discoverClaudeCodeCommand(options) {
162
+ // 1. Privacy / scope note (one line, consistent with capture warnings).
163
+ console.log(DISCOVERY_PRIVACY_NOTE);
164
+ console.log();
165
+ const { projectsDir, projectsDirExists, sessions } = await discoverClaudeCodeSessions({
166
+ projectsDir: options.projectsDir
167
+ });
168
+ console.log(`Scanning Claude Code projects directory: ${projectsDir}`);
169
+ if (!projectsDirExists) {
170
+ console.log("No Claude Code projects directory found at that path. Nothing to discover.");
171
+ return;
172
+ }
173
+ if (sessions.length === 0) {
174
+ console.log("No Claude Code sessions discovered.");
175
+ return;
176
+ }
177
+ console.log(`Discovered ${sessions.length} session(s):`);
178
+ console.log();
179
+ for (const s of sessions) {
180
+ const timestamp = s.lastTimestamp ?? s.firstTimestamp ?? "unknown";
181
+ console.log(`- session: ${s.sessionId ?? "unknown"}`);
182
+ console.log(` project: ${s.projectSlug}${s.projectDir ? ` (${s.projectDir})` : ""}`);
183
+ console.log(` timestamp: ${timestamp}`);
184
+ console.log(` messages: ${s.messageCount}`);
185
+ console.log(` subagents: ${s.subagentCount}`);
186
+ console.log(` provider usage: ${s.providerReportedUsagePresent ? "present" : "absent"}`);
187
+ console.log(` run: compaction capture claude-code --session ${s.sessionPath}`);
188
+ console.log();
189
+ }
190
+ console.log("Sessions are listed newest-first. Longer sessions (more messages / subagents) are the most");
191
+ console.log("likely to contain avoidable repeated or stale tool output, so they tend to show the clearest");
192
+ console.log("before/after delta; a short session may legitimately have little or nothing to compact.");
193
+ console.log();
194
+ console.log("Pick one session above and run its 'compaction capture claude-code --session <path>' command,");
195
+ console.log("then run 'compaction spend' followed by 'compaction compact --eval' on the resulting");
196
+ console.log("captured-trace.json (the strong flow: spend, then compaction + apply-readiness eval).");
197
+ }
198
+ //# sourceMappingURL=capture-claude-code.js.map
@@ -0,0 +1,35 @@
1
+ import type { ProviderUsageClient } from "../../core/provider-usage/provider-usage-client.js";
2
+ export interface CaptureProviderUsageOptions {
3
+ endpoint?: string;
4
+ credentialEnvVar?: string;
5
+ windowStart?: string;
6
+ windowEnd?: string;
7
+ label?: string;
8
+ out?: string;
9
+ }
10
+ /**
11
+ * Optional, additive, backward-compatible testability seam. Tests pass a MOCK
12
+ * provider-usage client (and may override the env reader) so the CLI-level
13
+ * stdout/stderr/artifact no-secret-leak + refusal paths can be exercised with a
14
+ * sentinel credential and NO real network. Defaults are the real client and
15
+ * process.env, so the production command path is unchanged.
16
+ */
17
+ export interface CaptureProviderUsageDeps {
18
+ /** Injectable provider-usage client. Default: the real fetch-based client. */
19
+ client?: ProviderUsageClient;
20
+ /** Env reader override (tests). Default: process.env (used to read the credential for the redaction backstop). */
21
+ env?: Record<string, string | undefined>;
22
+ /** Fixed timestamp for deterministic test artifacts. Default: now (via the adapter). */
23
+ capturedAt?: string;
24
+ /** Fixed run id for deterministic test artifacts. Default: derived (via the adapter). */
25
+ runId?: string;
26
+ }
27
+ /**
28
+ * Read-only, aggregate-only provider usage/cost capture (Option A).
29
+ *
30
+ * Reads the credential from the named env var (default COMPACTION_PROVIDER_USAGE_TOKEN),
31
+ * refuses cleanly if it is absent/empty (clear message naming the env var, non-zero
32
+ * exit, NO artifact), performs the single read via the real fetch client, and writes
33
+ * a usage-only CapturedRun under .compaction/. ALL output is redacted before printing.
34
+ */
35
+ export declare function captureProviderUsageCommand(options: CaptureProviderUsageOptions, deps?: CaptureProviderUsageDeps): Promise<void>;
@@ -0,0 +1,113 @@
1
+ import { writeJsonArtifact, writeTextArtifact } from "../../core/artifact-writer.js";
2
+ import { redactSecrets } from "../../core/provider-usage/credential-redaction.js";
3
+ import { captureProviderUsage, DEFAULT_CREDENTIAL_ENV_VAR, MissingProviderCredentialError, PROVIDER_USAGE_PRIVACY_NOTE } from "../../core/provider-usage/provider-usage-adapter.js";
4
+ function buildOutputDir(out) {
5
+ if (out)
6
+ return out;
7
+ return ".compaction/runs/provider-usage";
8
+ }
9
+ function buildMarkdownReport(outDir, provenance, usage) {
10
+ const lines = [
11
+ "# Provider Usage Capture Report",
12
+ "",
13
+ `**Source:** ${provenance.sourcePath}`,
14
+ `**Captured at:** ${provenance.capturedAt}`,
15
+ `**Adapter:** ${provenance.captureAdapter}`,
16
+ "",
17
+ "## Usage (aggregate only — no content)",
18
+ "",
19
+ `- Input tokens: ${usage.input_tokens ?? "unknown"}`,
20
+ `- Output tokens: ${usage.output_tokens ?? "unknown"}`,
21
+ `- Total tokens: ${usage.total_tokens ?? "unknown"}`,
22
+ `- Provider reported tokens: ${usage.provider_reported_tokens}`,
23
+ `- Cost source: ${usage.cost_source}`,
24
+ `- Cost confidence: ${usage.cost_confidence}`,
25
+ ...(usage.currency ? [`- Currency: ${usage.currency}`] : []),
26
+ "",
27
+ "## Warnings",
28
+ "",
29
+ ...provenance.warnings.map((w) => `- ${w}`),
30
+ "",
31
+ "## Limitations",
32
+ "",
33
+ ...provenance.limitations.map((l) => `- ${l}`),
34
+ "",
35
+ "## Artifacts",
36
+ "",
37
+ `- \`${outDir}/captured-trace.json\` — AgentTrace (source: provider_usage, no messages)`,
38
+ `- \`${outDir}/capture-report.json\` — CapturedRun metadata (usage/cost only)`,
39
+ `- \`${outDir}/capture-report.md\` — this file`,
40
+ ""
41
+ ];
42
+ return lines.join("\n");
43
+ }
44
+ /**
45
+ * Read-only, aggregate-only provider usage/cost capture (Option A).
46
+ *
47
+ * Reads the credential from the named env var (default COMPACTION_PROVIDER_USAGE_TOKEN),
48
+ * refuses cleanly if it is absent/empty (clear message naming the env var, non-zero
49
+ * exit, NO artifact), performs the single read via the real fetch client, and writes
50
+ * a usage-only CapturedRun under .compaction/. ALL output is redacted before printing.
51
+ */
52
+ export async function captureProviderUsageCommand(options, deps = {}) {
53
+ const envVar = options.credentialEnvVar ?? DEFAULT_CREDENTIAL_ENV_VAR;
54
+ // Env reader: process.env by default; tests may inject an override.
55
+ const env = deps.env ?? process.env;
56
+ // Capture the configured secret (if any) ONLY to feed the redaction backstop —
57
+ // it is never printed and never written. Primary control is that nothing on this
58
+ // path puts it into output; this is the defense-in-depth value for redaction.
59
+ const configuredSecret = env[envVar];
60
+ // Redacted printer: every line written by this command passes through redaction.
61
+ const say = (line) => console.log(redactSecrets(line, configuredSecret));
62
+ const sayErr = (line) => console.error(redactSecrets(line, configuredSecret));
63
+ say(PROVIDER_USAGE_PRIVACY_NOTE);
64
+ say("");
65
+ if (!options.endpoint) {
66
+ sayErr("error: --endpoint <url> is required (the provider usage/cost reporting endpoint).");
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+ let capturedRun;
71
+ try {
72
+ capturedRun = await captureProviderUsage({
73
+ endpoint: options.endpoint,
74
+ credentialEnvVar: envVar,
75
+ windowStart: options.windowStart,
76
+ windowEnd: options.windowEnd,
77
+ label: options.label,
78
+ // Seam: default undefined => the adapter uses the real fetch client / process.env / now.
79
+ client: deps.client,
80
+ env: deps.env,
81
+ capturedAt: deps.capturedAt,
82
+ runId: deps.runId
83
+ });
84
+ }
85
+ catch (error) {
86
+ if (error instanceof MissingProviderCredentialError) {
87
+ // Clean refusal: clear message naming the env var, non-zero exit, NO artifact.
88
+ sayErr(`error: ${error.message}`);
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+ // Any other failure (auth error, network, parse): redact before printing,
93
+ // exit non-zero, write NO partial artifact.
94
+ const message = error instanceof Error ? error.message : String(error);
95
+ sayErr(`error: provider usage read failed: ${message}`);
96
+ process.exitCode = 1;
97
+ return;
98
+ }
99
+ const { trace, usage, provenance } = capturedRun;
100
+ const outDir = buildOutputDir(options.out);
101
+ say(`Source: ${provenance.sourcePath}`);
102
+ say(`Token totals (aggregate): input=${usage.input_tokens ?? 0}, output=${usage.output_tokens ?? 0}, total=${usage.total_tokens ?? 0}`);
103
+ say(`Cost source: ${usage.cost_source} (confidence: ${usage.cost_confidence})`);
104
+ say("Messages captured: 0 (aggregate usage only — no prompt/completion content)");
105
+ const capturedRunArtifact = { trace, usage, provenance };
106
+ const markdownReport = buildMarkdownReport(outDir, provenance, usage);
107
+ await writeJsonArtifact(outDir, "captured-trace.json", trace);
108
+ await writeJsonArtifact(outDir, "capture-report.json", capturedRunArtifact);
109
+ await writeTextArtifact(outDir, "capture-report.md", markdownReport);
110
+ say(`Artifacts written to: ${outDir}/`);
111
+ say(`Next: compaction analyze ${outDir}/captured-trace.json`);
112
+ }
113
+ //# sourceMappingURL=capture-provider-usage.js.map
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerCaptureCommand(program: Command): void;
@@ -0,0 +1,76 @@
1
+ import chalk from "chalk";
2
+ import { captureOpenAIAgentsCommand, captureOpenAIAgentsExport } from "../../core/openai-agents-capture.js";
3
+ import { captureClaudeCodeCommand, discoverClaudeCodeCommand } from "./capture-claude-code.js";
4
+ import { captureProviderUsageCommand } from "./capture-provider-usage.js";
5
+ import { DEFAULT_CREDENTIAL_ENV_VAR } from "../../core/provider-usage/provider-usage-adapter.js";
6
+ export function registerCaptureCommand(program) {
7
+ const capture = program.command("capture").description("Capture local integration traces into compaction.dev AgentTrace format.");
8
+ capture
9
+ .command("openai-agents")
10
+ .description("Capture OpenAI Agents SDK-style local trace events from a command wrapper or local export.")
11
+ .requiredOption("--out <dir>", "Output root directory for capture artifacts")
12
+ .option("--export <file>", "Read a local OpenAI Agents SDK-style JSONL trace/span export instead of running a command")
13
+ .argument("[commandParts...]", "Command and arguments to execute after --")
14
+ .allowUnknownOption(true)
15
+ .action(async (commandParts, options) => {
16
+ console.log(chalk.cyan("compaction capture openai-agents"));
17
+ console.log("Capturing local OpenAI Agents SDK-style events. No ChatGPT scraping, provider proxying, or upload is performed.");
18
+ const artifacts = options.export
19
+ ? await captureOpenAIAgentsExport(options.export, options.out)
20
+ : await captureOpenAIAgentsCommand(commandParts, options.out);
21
+ console.log(artifacts.terminalSummary);
22
+ console.log(chalk.green(`Wrote ${artifacts.paths.capturedTracePath}`));
23
+ console.log(chalk.green(`Wrote ${artifacts.paths.captureReportJsonPath}`));
24
+ console.log(chalk.green(`Wrote ${artifacts.paths.captureReportMarkdownPath}`));
25
+ if (artifacts.report.status === "fail") {
26
+ process.exitCode = 1;
27
+ }
28
+ });
29
+ capture
30
+ .command("claude-code")
31
+ .description("Capture a Claude Code session JSONL file into compaction.dev AgentTrace format (source: real_captured). Local only — no network calls, no upload.")
32
+ .option("--session <path>", "Path to the Claude Code session JSONL file (e.g. ~/.claude/projects/<slug>/<session-id>.jsonl)")
33
+ .option("--discover", "List discoverable local Claude Code sessions (metadata only) and print the ready-to-run --session command for each. Local, read-only.")
34
+ .option("--projects-dir <path>", "Override the Claude Code projects root to scan with --discover (default: ~/.claude/projects). Read-only.")
35
+ .option("--out <dir>", "Output directory for captured artifacts (default: .compaction/runs/<session-id-prefix>/)")
36
+ .option("--max-tool-result-chars <N>", "Maximum characters to extract from each tool result before truncation (default: 32000)")
37
+ .option("--include-subagents", "Include subagent JSONL files from <session-id>/subagents/ directory, merging their messages and usage into the captured trace")
38
+ .action(async (options) => {
39
+ if (options.discover) {
40
+ await discoverClaudeCodeCommand({ projectsDir: options.projectsDir });
41
+ return;
42
+ }
43
+ if (!options.session) {
44
+ console.error("error: either --session <path> or --discover is required.");
45
+ console.error("Run 'compaction capture claude-code --discover' to list local sessions.");
46
+ process.exitCode = 1;
47
+ return;
48
+ }
49
+ await captureClaudeCodeCommand({
50
+ session: options.session,
51
+ out: options.out,
52
+ maxToolResultChars: options.maxToolResultChars,
53
+ includeSubagents: options.includeSubagents
54
+ });
55
+ });
56
+ capture
57
+ .command("provider-usage")
58
+ .description("Read-only, aggregate-only provider usage/cost capture (Option A). Reads aggregate token/cost only — NO prompt/completion content. The credential is read from an environment variable (never a flag value), used only for the single read-only request, and never logged or written to any artifact. Local only — the only egress is the configured provider endpoint.")
59
+ .requiredOption("--endpoint <url>", "Provider usage/cost reporting endpoint to read (non-secret).")
60
+ .option("--credential-env-var <name>", `Name of the environment variable to read the read-only credential from (default: ${DEFAULT_CREDENTIAL_ENV_VAR}). This NAMES the env var only — it never carries the secret value.`, DEFAULT_CREDENTIAL_ENV_VAR)
61
+ .option("--window-start <iso>", "Reporting window start (ISO 8601).")
62
+ .option("--window-end <iso>", "Reporting window end (ISO 8601).")
63
+ .option("--label <label>", "Human-readable label for the captured run/window.")
64
+ .option("--out <dir>", "Output directory for captured artifacts (default: .compaction/runs/provider-usage/).")
65
+ .action(async (options) => {
66
+ await captureProviderUsageCommand({
67
+ endpoint: options.endpoint,
68
+ credentialEnvVar: options.credentialEnvVar,
69
+ windowStart: options.windowStart,
70
+ windowEnd: options.windowEnd,
71
+ label: options.label,
72
+ out: options.out
73
+ });
74
+ });
75
+ }
76
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerCompactCommand(program: Command): void;
@@ -0,0 +1,206 @@
1
+ import path from "node:path";
2
+ import chalk from "chalk";
3
+ import { Option } from "commander";
4
+ import { writeJsonArtifact, writeTextArtifact } from "../../core/artifact-writer.js";
5
+ import { describeTokenAccounting } from "../../core/token-accounting.js";
6
+ import { parseTraceFile } from "../../core/trace-parser.js";
7
+ import { buildRunLabelsFile, hasAnyLabel } from "../../core/run-labels.js";
8
+ import { runEngineCommand } from "../engine-degrade.js";
9
+ const VALID_REVIEWER_TYPES = ["human", "automated", "none"];
10
+ /**
11
+ * Print the combined compaction→eval summary for `compact --eval`. The five signals are kept
12
+ * STRICTLY SEPARATE and each carries its honest label, so the strong deterministic recoverability
13
+ * fact is never blurred with the weak local token/cost estimate:
14
+ * 1. token/cost — local estimate (chars/4 + price-table), NOT billing-confirmed, NOT realized savings.
15
+ * 2. deterministic recoverability — passed/failed/not_computed + the recoverability check counts.
16
+ * 3. evidence tier — fixture / imported / real_captured, derived at the weakest honest level from
17
+ * the trace source (a fixture/imported trace can never be reported as real_captured).
18
+ * 4. semantic_preservation — always not_evaluated (never scored).
19
+ * 5. commitment_preservation — always not_evaluated (never scored).
20
+ * `recoverability: passed` means byte/hash/source-pointer recoverability ONLY.
21
+ */
22
+ function printCombinedEvalSummary(result) {
23
+ const checks = result.recoverability_checks;
24
+ const passedChecks = checks.filter((c) => c.status === "pass").length;
25
+ console.log("");
26
+ console.log(chalk.bold("Recoverability eval (--eval): deterministic, in-process — no hand-assembled bundle"));
27
+ // 1. token/cost estimate — local estimate ONLY.
28
+ console.log(chalk.bold("[1] Token/cost estimate (local estimate):"));
29
+ console.log(" Locally estimated (chars/4 trace tokens + price-table cost) — NOT provider-reported, " +
30
+ "NOT billing-confirmed, NOT realized savings. See compaction report above for the before/after figures.");
31
+ // 2. deterministic recoverability result + counts.
32
+ console.log(chalk.bold("[2] Deterministic recoverability:"));
33
+ console.log(` ${result.recoverability.toUpperCase()} (byte/hash/source-pointer recoverability ONLY)`);
34
+ console.log(` ${result.recoverability_reason}`);
35
+ console.log(` Recoverability checks: ${passedChecks}/${checks.length} passed`);
36
+ for (const check of checks) {
37
+ console.log(` [${check.status}] ${check.label}`);
38
+ }
39
+ // 3. evidence tier (derived from trace source; never elevated).
40
+ console.log(chalk.bold("[3] Evidence tier:"));
41
+ console.log(` ${result.evidence_source} (from ${result.evidence_source_type}); real_captured=${result.real_captured}`);
42
+ // 4 + 5. preservation axes — always not_evaluated.
43
+ console.log(chalk.bold("[4] Semantic preservation:"));
44
+ console.log(` ${result.semantic_preservation} (NOT scored by this harness)`);
45
+ console.log(chalk.bold("[5] Commitment preservation:"));
46
+ console.log(` ${result.commitment_preservation} (NOT scored by this harness)`);
47
+ console.log(" A passing recoverability verdict is byte/hash/source-pointer recoverability ONLY — " +
48
+ "it is NOT a meaning-preservation or task-critical-commitment claim.");
49
+ }
50
+ /**
51
+ * Print the STRONG eval summary for `compact --eval` (Strong-MVP Tracks B/C/D): the deterministic
52
+ * commitment-preservation recoverability check, the fixture-based task-check, the token/cost
53
+ * accounting (each figure labeled provider-reported vs local estimate), and the composed strong
54
+ * apply-readiness verdict with a per-axis reason. Every label is the weakest honest level:
55
+ * commitment-preservation and task-check are RECOVERABILITY checks, NOT semantic/meaning guarantees,
56
+ * and the task-check is fixture-based, NOT real model replay.
57
+ */
58
+ function printStrongEvalSummary(strong) {
59
+ console.log("");
60
+ console.log(chalk.bold("Strong apply-readiness eval (Tracks B/C/D): deterministic, in-process"));
61
+ // [6] commitment preservation (recoverability of extracted task-critical items — NOT semantic).
62
+ const c = strong.commitment_preservation;
63
+ console.log(chalk.bold("[6] Commitment preservation (deterministic recoverability check, NOT semantic):"));
64
+ console.log(` ${c.status.toUpperCase()} — ${c.reason}`);
65
+ console.log(` commitments=${c.commitment_count} (retained=${c.retained_count}, recoverable=${c.recoverable_count}, missing=${c.missing_count})`);
66
+ for (const detail of c.details) {
67
+ console.log(` [${detail.recoverability}] ${detail.category}: ${detail.text}`);
68
+ }
69
+ // [7] fixture-based task-check (NOT real model replay).
70
+ const t = strong.task_check;
71
+ console.log(chalk.bold("[7] Task-check (fixture-based, NOT real model replay):"));
72
+ console.log(` ${t.status.toUpperCase()} (workflow=${t.workflow}) — ${t.reason}`);
73
+ // [8] token & cost accounting (each figure labeled).
74
+ console.log(chalk.bold("[8] Token & cost accounting:"));
75
+ for (const line of describeTokenAccounting(strong.token_accounting)) {
76
+ console.log(` ${line}`);
77
+ }
78
+ // [9] strong apply-readiness verdict (per-axis reasons).
79
+ const r = strong.readiness;
80
+ console.log(chalk.bold(`[9] Strong apply-readiness: ${r.readiness.toUpperCase()}`));
81
+ console.log(` ${r.reason}`);
82
+ for (const check of r.checks) {
83
+ console.log(` [${check.status}] ${check.axis}: ${check.reason}`);
84
+ }
85
+ }
86
+ function runIdFromOutputDirectory(outputDirectory) {
87
+ return path.basename(path.resolve(outputDirectory));
88
+ }
89
+ export function registerCompactCommand(program) {
90
+ program
91
+ .command("compact")
92
+ .argument("<trace-file>", "Path to a local agent trace JSON file")
93
+ .requiredOption("--out <dir>", "Directory where compaction artifacts should be written")
94
+ .addOption(new Option("--reviewer <type>", "Reviewer type for approval readiness signal")
95
+ .choices(VALID_REVIEWER_TYPES)
96
+ .default("none"))
97
+ .option("--review-summary <text>", "Review summary text (presence sets review_summary_present: true)")
98
+ .addOption(new Option("--approve-skill-injection-policy", "Approve and apply the byte-identical same-skill skill-injection compaction policy (default: off; nothing compacted unless provided)"))
99
+ .option("--eval", "After compacting, run the deterministic byte/hash/source-pointer recoverability eval on the SAME " +
100
+ "in-process compaction (no hand-assembled bundle) and print a combined summary. Exits non-zero if " +
101
+ "recoverability fails (fail-closed). Recoverability is byte/hash/source-pointer only — NOT semantic " +
102
+ "or commitment preservation, and NOT a savings claim.")
103
+ .option("--project <name>", "Local-only label: tag this run's project (for the multi-session aggregate; never uploaded)")
104
+ .option("--workflow <name>", "Local-only label: tag this run's workflow (for the aggregate; never uploaded)")
105
+ .option("--provider <name>", "Local-only label: tag this run's provider/runtime (operator-asserted, NOT provider-verified)")
106
+ .option("--user-label <text>", "Local-only free-form label for this run (for the aggregate; never uploaded)")
107
+ .option("--session <id>", "Local-only session id: group this run with others under one session in the aggregate")
108
+ .description("Compact a trace and write local artifacts.")
109
+ .action(async (traceFile, options) => {
110
+ await runEngineCommand(async () => {
111
+ // Lazy-load the proprietary engine: present in-repo/API, excluded from the public package.
112
+ // When absent, runEngineCommand prints the boundary message and exits cleanly (no stack trace).
113
+ const { writeCompactionArtifacts } = await import("../../engine/compaction-artifacts.js");
114
+ const { applyCompactionPolicy } = await import("../../engine/policy-middleware.js");
115
+ const { evaluateCompactionResult, evaluateStrongCompactionResult, formatStrongEvalMarkdownReport } = await import("../../engine/eval-harness.js");
116
+ const trace = await parseTraceFile(traceFile);
117
+ const reviewerType = options.reviewer;
118
+ const reviewSummaryPresent = typeof options.reviewSummary === "string" && options.reviewSummary.length > 0;
119
+ const approveSkillInjectionPolicy = options.approveSkillInjectionPolicy === true;
120
+ const runId = runIdFromOutputDirectory(options.out);
121
+ // Run the compaction policy ONCE, in-process, and reuse the SAME in-memory result for both the
122
+ // persisted artifacts and (when --eval) the recoverability eval. No disk round-trip, no
123
+ // hand-assembled bundle — the eval consumes the identical PolicyMiddlewareResult.
124
+ const policyResult = applyCompactionPolicy({ trace, compactSkillInjections: approveSkillInjectionPolicy });
125
+ const artifacts = await writeCompactionArtifacts(trace, options.out, runId, policyResult, undefined, { reviewerType, reviewSummaryPresent, approveSkillInjectionPolicy });
126
+ console.log(chalk.cyan("compaction compact"));
127
+ console.log(artifacts.consoleReport);
128
+ // Operator-visible only when the approval gesture was given; default output is unchanged.
129
+ if (approveSkillInjectionPolicy) {
130
+ const summary = artifacts.skillInjectionCompaction;
131
+ console.log(chalk.cyan(`Skill-injection policy (approved): compacted ${summary.compacted_count} byte-identical same-skill skill injection(s); ` +
132
+ `est ${summary.estimated_tokens_removed} tokens removed (${summary.estimate_label})`));
133
+ }
134
+ // Token-count source label (honest): the compaction report's before/after token figures
135
+ // are locally estimated (chars/4), never provider-reported. Stating this prevents an
136
+ // estimate from being read as a billing-confirmed count.
137
+ console.log("Token-count source: locally estimated (chars/4) — NOT provider-reported, NOT billing-confirmed");
138
+ // Safety + recoverability linkage (safety-report.json fields), so the compaction value and
139
+ // its reversibility/risk evidence are visible together, not split across files.
140
+ const safety = artifacts.safetyReport;
141
+ console.log(`Risk level: ${safety.risk_level} (safety-report.json risk_level)`);
142
+ console.log(`Source recoverability: ${safety.source_recoverability} ` +
143
+ "(source pointers + content hashes + state capsules recorded; original trace retained)");
144
+ console.log(`Approval readiness: ${safety.approval_readiness_status}`);
145
+ console.log("Approval requirement: nothing is applied to your workflow automatically. " +
146
+ "apply-context emits an approved context ONLY with the explicit --approve-in-workflow-use gesture (no auto-apply).");
147
+ console.log(chalk.green(`Wrote ${artifacts.paths.reportPath}`));
148
+ console.log(chalk.green(`Wrote ${artifacts.paths.markdownReportPath}`));
149
+ console.log(chalk.green(`Wrote ${artifacts.paths.policyPath}`));
150
+ console.log(chalk.green(`Wrote ${artifacts.paths.capsulePath}`));
151
+ console.log(chalk.green(`Wrote ${artifacts.paths.compactedTracePath}`));
152
+ console.log(chalk.green(`Wrote ${artifacts.paths.safetyReportPath}`));
153
+ console.log(chalk.green(`Wrote ${artifacts.paths.safetyMarkdownReportPath}`));
154
+ console.log(chalk.green(`Wrote ${artifacts.paths.prCommentReportPath}`));
155
+ // Optional LOCAL-ONLY labels: tag this run for the multi-session aggregate. Written
156
+ // to run-labels.json in the run directory; never uploaded, carries no trace content.
157
+ const suppliedLabels = {
158
+ project: options.project,
159
+ workflow: options.workflow,
160
+ provider: options.provider,
161
+ user_label: options.userLabel,
162
+ session: options.session
163
+ };
164
+ if (hasAnyLabel(suppliedLabels)) {
165
+ const labelsPath = await writeJsonArtifact(options.out, "run-labels.json", buildRunLabelsFile(suppliedLabels));
166
+ console.log(chalk.green(`Wrote ${labelsPath} (local-only labels; never uploaded)`));
167
+ }
168
+ // Exact next command to continue the loop (chaining), with the trace path the user passed.
169
+ console.log("");
170
+ console.log(chalk.bold("Next: review the compaction and emit an approved context (review-only by default):"));
171
+ console.log(` compaction apply-context ${traceFile} --out <dir>`);
172
+ console.log(" (add --approve-in-workflow-use once you have reviewed the safety report to emit the approved context)");
173
+ // --eval: run the deterministic recoverability eval on the SAME in-memory compaction result
174
+ // (no disk round-trip, no hand-assembled bundle) and print the combined summary. Fail closed:
175
+ // a failing recoverability verdict exits non-zero, identical to standalone `compaction eval`.
176
+ if (options.eval === true) {
177
+ const evalResult = evaluateCompactionResult(trace, policyResult, runId);
178
+ printCombinedEvalSummary(evalResult);
179
+ // Strong eval (Tracks B/C/D): commitment-preservation recoverability + fixture task-check +
180
+ // token/cost accounting + the composed strong apply-readiness gate. Fails closed: recoverability
181
+ // `failed` OR strong readiness `not_ready` exits non-zero.
182
+ // Thread the SAME --reviewer/--review-summary evidence the persisted safety-report
183
+ // artifacts already received (above) into the strong-eval recompute, so a real_captured
184
+ // trace WITH valid recoverability + commitment + task-check + reviewer + summary can
185
+ // legitimately reach `ready`. Fail-closed: missing/invalid review evidence still caps at
186
+ // `conditional` (the safety-report apply-readiness gate is unchanged).
187
+ const strong = evaluateStrongCompactionResult({
188
+ originalTrace: trace,
189
+ policyResult,
190
+ runId,
191
+ reviewerType,
192
+ reviewSummaryPresent
193
+ });
194
+ printStrongEvalSummary(strong);
195
+ const strongJsonPath = await writeJsonArtifact(options.out, "strong-eval.json", strong);
196
+ const strongMarkdownPath = await writeTextArtifact(options.out, "strong-eval.md", formatStrongEvalMarkdownReport(strong));
197
+ console.log(chalk.green(`Wrote ${strongJsonPath}`));
198
+ console.log(chalk.green(`Wrote ${strongMarkdownPath}`));
199
+ if (evalResult.recoverability === "failed" || strong.readiness.readiness === "not_ready") {
200
+ process.exitCode = 1;
201
+ }
202
+ }
203
+ });
204
+ });
205
+ }
206
+ //# sourceMappingURL=compact.js.map
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerEvalCommand(program: Command): void;