@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.
- package/LICENSE +663 -0
- package/README.md +319 -0
- package/dist/cli/commands/adapter.d.ts +2 -0
- package/dist/cli/commands/adapter.js +49 -0
- package/dist/cli/commands/aggregate.d.ts +2 -0
- package/dist/cli/commands/aggregate.js +60 -0
- package/dist/cli/commands/analyze.d.ts +2 -0
- package/dist/cli/commands/analyze.js +55 -0
- package/dist/cli/commands/apply-context.d.ts +12 -0
- package/dist/cli/commands/apply-context.js +80 -0
- package/dist/cli/commands/apply.d.ts +2 -0
- package/dist/cli/commands/apply.js +65 -0
- package/dist/cli/commands/approve.d.ts +2 -0
- package/dist/cli/commands/approve.js +55 -0
- package/dist/cli/commands/audit.d.ts +2 -0
- package/dist/cli/commands/audit.js +63 -0
- package/dist/cli/commands/billing-delta.d.ts +2 -0
- package/dist/cli/commands/billing-delta.js +246 -0
- package/dist/cli/commands/capture-claude-code.d.ts +19 -0
- package/dist/cli/commands/capture-claude-code.js +198 -0
- package/dist/cli/commands/capture-provider-usage.d.ts +35 -0
- package/dist/cli/commands/capture-provider-usage.js +113 -0
- package/dist/cli/commands/capture.d.ts +2 -0
- package/dist/cli/commands/capture.js +76 -0
- package/dist/cli/commands/compact.d.ts +2 -0
- package/dist/cli/commands/compact.js +206 -0
- package/dist/cli/commands/eval.d.ts +2 -0
- package/dist/cli/commands/eval.js +66 -0
- package/dist/cli/commands/export-session-seed.d.ts +11 -0
- package/dist/cli/commands/export-session-seed.js +42 -0
- package/dist/cli/commands/feedback.d.ts +2 -0
- package/dist/cli/commands/feedback.js +233 -0
- package/dist/cli/commands/import.d.ts +2 -0
- package/dist/cli/commands/import.js +60 -0
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +216 -0
- package/dist/cli/commands/integrations.d.ts +2 -0
- package/dist/cli/commands/integrations.js +29 -0
- package/dist/cli/commands/launch-treatment-session.d.ts +14 -0
- package/dist/cli/commands/launch-treatment-session.js +50 -0
- package/dist/cli/commands/optimize.d.ts +2 -0
- package/dist/cli/commands/optimize.js +33 -0
- package/dist/cli/commands/recommend.d.ts +4 -0
- package/dist/cli/commands/recommend.js +66 -0
- package/dist/cli/commands/review.d.ts +2 -0
- package/dist/cli/commands/review.js +60 -0
- package/dist/cli/commands/run.d.ts +2 -0
- package/dist/cli/commands/run.js +49 -0
- package/dist/cli/commands/spend.d.ts +2 -0
- package/dist/cli/commands/spend.js +24 -0
- package/dist/cli/commands/summary.d.ts +2 -0
- package/dist/cli/commands/summary.js +19 -0
- package/dist/cli/engine-degrade.d.ts +40 -0
- package/dist/cli/engine-degrade.js +59 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +59 -0
- package/dist/core/adapter-validation.d.ts +32 -0
- package/dist/core/adapter-validation.js +185 -0
- package/dist/core/adapters/claude-code-adapter.d.ts +51 -0
- package/dist/core/adapters/claude-code-adapter.js +539 -0
- package/dist/core/adapters/claude-code-discovery.d.ts +40 -0
- package/dist/core/adapters/claude-code-discovery.js +100 -0
- package/dist/core/aggregate-format.d.ts +62 -0
- package/dist/core/aggregate-format.js +349 -0
- package/dist/core/api-client/client.d.ts +43 -0
- package/dist/core/api-client/client.js +143 -0
- package/dist/core/api-client/config.d.ts +36 -0
- package/dist/core/api-client/config.js +44 -0
- package/dist/core/api-client/index.d.ts +13 -0
- package/dist/core/api-client/index.js +14 -0
- package/dist/core/api-client/payload.d.ts +93 -0
- package/dist/core/api-client/payload.js +180 -0
- package/dist/core/api-client/types.d.ts +95 -0
- package/dist/core/api-client/types.js +21 -0
- package/dist/core/artifact-writer.d.ts +2 -0
- package/dist/core/artifact-writer.js +14 -0
- package/dist/core/audit-report.d.ts +91 -0
- package/dist/core/audit-report.js +422 -0
- package/dist/core/billing-delta/billing-delta-record.d.ts +290 -0
- package/dist/core/billing-delta/billing-delta-record.js +394 -0
- package/dist/core/capture-adapter.d.ts +74 -0
- package/dist/core/capture-adapter.js +2 -0
- package/dist/core/command-runner.d.ts +29 -0
- package/dist/core/command-runner.js +96 -0
- package/dist/core/content-hash.d.ts +1 -0
- package/dist/core/content-hash.js +5 -0
- package/dist/core/cost-calculator.d.ts +6 -0
- package/dist/core/cost-calculator.js +20 -0
- package/dist/core/feedback-bundle.d.ts +159 -0
- package/dist/core/feedback-bundle.js +406 -0
- package/dist/core/openai-agents-capture.d.ts +66 -0
- package/dist/core/openai-agents-capture.js +475 -0
- package/dist/core/policy-types.d.ts +85 -0
- package/dist/core/policy-types.js +29 -0
- package/dist/core/pricing.d.ts +23 -0
- package/dist/core/pricing.js +55 -0
- package/dist/core/provider-usage/credential-redaction.d.ts +23 -0
- package/dist/core/provider-usage/credential-redaction.js +60 -0
- package/dist/core/provider-usage/provider-usage-adapter.d.ts +65 -0
- package/dist/core/provider-usage/provider-usage-adapter.js +147 -0
- package/dist/core/provider-usage/provider-usage-client.d.ts +89 -0
- package/dist/core/provider-usage/provider-usage-client.js +96 -0
- package/dist/core/report-generator.d.ts +7 -0
- package/dist/core/report-generator.js +275 -0
- package/dist/core/run-aggregator.d.ts +50 -0
- package/dist/core/run-aggregator.js +251 -0
- package/dist/core/run-labels.d.ts +52 -0
- package/dist/core/run-labels.js +80 -0
- package/dist/core/run-trace-converter.d.ts +3 -0
- package/dist/core/run-trace-converter.js +94 -0
- package/dist/core/safety-report.d.ts +52 -0
- package/dist/core/safety-report.js +440 -0
- package/dist/core/savings.d.ts +22 -0
- package/dist/core/savings.js +36 -0
- package/dist/core/session-aggregate.d.ts +211 -0
- package/dist/core/session-aggregate.js +490 -0
- package/dist/core/skill-injection-detector.d.ts +51 -0
- package/dist/core/skill-injection-detector.js +146 -0
- package/dist/core/spend-attribution.d.ts +169 -0
- package/dist/core/spend-attribution.js +611 -0
- package/dist/core/standalone-capsule-provenance.d.ts +4 -0
- package/dist/core/standalone-capsule-provenance.js +62 -0
- package/dist/core/state-capsule.d.ts +3 -0
- package/dist/core/state-capsule.js +147 -0
- package/dist/core/token-accounting.d.ts +62 -0
- package/dist/core/token-accounting.js +81 -0
- package/dist/core/token-estimator.d.ts +3 -0
- package/dist/core/token-estimator.js +18 -0
- package/dist/core/trace-adapters.d.ts +53 -0
- package/dist/core/trace-adapters.js +714 -0
- package/dist/core/trace-fingerprint.d.ts +79 -0
- package/dist/core/trace-fingerprint.js +102 -0
- package/dist/core/trace-intake.d.ts +41 -0
- package/dist/core/trace-intake.js +164 -0
- package/dist/core/trace-parser.d.ts +203 -0
- package/dist/core/trace-parser.js +143 -0
- package/dist/core/types.d.ts +238 -0
- package/dist/core/types.js +2 -0
- package/dist/core/usage-metadata.d.ts +51 -0
- package/dist/core/usage-metadata.js +122 -0
- package/dist/core/waste-detector.d.ts +8 -0
- package/dist/core/waste-detector.js +58 -0
- package/package.json +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# @compaction/cli
|
|
2
|
+
|
|
3
|
+
**The free, local-first CLI for capturing and measuring AI agent traces.** It
|
|
4
|
+
captures supported agent sessions on your own machine, shows input/output tokens
|
|
5
|
+
and estimated spend with honest labels, and writes local reports and redacted
|
|
6
|
+
share bundles — and, **when you opt in**, prepares those traces for optimization
|
|
7
|
+
and verification via the **Compaction API**.
|
|
8
|
+
|
|
9
|
+
> **The local CLI captures, measures, and prepares.** Optimization, advanced
|
|
10
|
+
> verification, apply guidance, and team reporting are **not** in the free CLI —
|
|
11
|
+
> they require the **Compaction API** (opt-in).
|
|
12
|
+
|
|
13
|
+
## What the free CLI does (local, no account, no upload by default)
|
|
14
|
+
|
|
15
|
+
`@compaction/cli` is a local-first command-line tool. Everything below runs on your
|
|
16
|
+
machine and writes local files only:
|
|
17
|
+
|
|
18
|
+
- **Capture supported agent sessions.** **Claude Code is the primary supported
|
|
19
|
+
workflow** (`capture claude-code`, Integration Readiness **Level 2**
|
|
20
|
+
command-wrapper). Also supported: **OpenAI Agents SDK** (`capture openai-agents`,
|
|
21
|
+
Level 2 command-wrapper) and **Codex** local-file/trace **import** (`import`,
|
|
22
|
+
Level 1). These are local capture/import integrations — **not** native/Level 3
|
|
23
|
+
SDK instrumentation.
|
|
24
|
+
- **Show input/output tokens and estimated spend.** `analyze` and the local report
|
|
25
|
+
show input/output token counts and a per-run **estimated** cost. Token counts are
|
|
26
|
+
**provider-reported** only when the captured run carries provider usage metadata;
|
|
27
|
+
otherwise they are a **local estimate (chars/4)**. Cost is a **price-table
|
|
28
|
+
estimate** labeled `local estimate` / `token-estimated cost` — **never
|
|
29
|
+
billing-confirmed**.
|
|
30
|
+
- **Create local reports.** `analyze` / `compact` write local JSON + Markdown
|
|
31
|
+
reports under an operator-chosen output directory. Local files only.
|
|
32
|
+
- **Create redacted, content-free share bundles.** `feedback --redact` writes a
|
|
33
|
+
redacted, privacy-safe bundle as a **local file only — never uploaded**, and only
|
|
34
|
+
with an explicit write gesture (`--yes`).
|
|
35
|
+
- **No upload by default.** There is **no telemetry**. The **only** network call in
|
|
36
|
+
the free CLI is the explicitly operator-invoked, read-only `capture
|
|
37
|
+
provider-usage` (credential supplied via an environment variable only; aggregate
|
|
38
|
+
usage/cost figures only, no prompt/completion content).
|
|
39
|
+
|
|
40
|
+
The free CLI deliberately answers the first-value question — *"where is my context
|
|
41
|
+
being wasted, and roughly what does it cost?"* — entirely locally, with honest
|
|
42
|
+
labels.
|
|
43
|
+
|
|
44
|
+
## What requires the Compaction API (opt-in — not in the free CLI)
|
|
45
|
+
|
|
46
|
+
The following are **not** part of the free local CLI. They are the **opt-in
|
|
47
|
+
Compaction API**:
|
|
48
|
+
|
|
49
|
+
- **Optimization** — producing a compacted context from a captured trace.
|
|
50
|
+
- **Advanced verification** — stronger checks on what a compacted context retains.
|
|
51
|
+
- **Apply guidance** — a readiness verdict and a review/approval path before any
|
|
52
|
+
context is applied.
|
|
53
|
+
- **Team reporting** — cross-run / cross-session reporting when enabled.
|
|
54
|
+
|
|
55
|
+
> **The Compaction API is not broadly hosted yet.** There is **no live hosted
|
|
56
|
+
> service today**. The API path is **designed to** optimize and verify **when you
|
|
57
|
+
> opt in** — this README does **not** claim those run as a live service. Until the
|
|
58
|
+
> API is available, these capabilities are **on the roadmap / opt-in**, not present
|
|
59
|
+
> in the free CLI.
|
|
60
|
+
|
|
61
|
+
When the API is available, the honesty rails carry over unchanged: cost stays an
|
|
62
|
+
**estimate** unless billing-confirmed (no figure carries that label today);
|
|
63
|
+
verification is **deterministic recoverability, NOT semantic/meaning preservation**;
|
|
64
|
+
integration stays **Level 1/2**; and there is **no auto-apply** — every application
|
|
65
|
+
is an explicit operator gesture.
|
|
66
|
+
|
|
67
|
+
## The free local loop (recommended first run: your OWN workflow)
|
|
68
|
+
|
|
69
|
+
**capture → analyze / measure → (local report) → redact / share**
|
|
70
|
+
|
|
71
|
+
The recommended first run is on **your own** captured/imported workflow — **not a
|
|
72
|
+
demo**. Synthetic/demo traces exist only as illustration; they are never the
|
|
73
|
+
recommended first value.
|
|
74
|
+
|
|
75
|
+
1. **Capture** a real workflow trace. **Claude Code is the primary supported
|
|
76
|
+
workflow** (`capture claude-code --session <path>`, Level 2 command-wrapper).
|
|
77
|
+
Also supported: OpenAI Agents SDK (`capture openai-agents`, Level 2) and Codex
|
|
78
|
+
local-file import (`import`, Level 1).
|
|
79
|
+
2. **Analyze / measure** input/output tokens and estimated spend, and see where the
|
|
80
|
+
spend came from (`analyze`).
|
|
81
|
+
3. **Local report** — read the local JSON / Markdown report written under your
|
|
82
|
+
chosen output directory.
|
|
83
|
+
4. **Redact / share** — `feedback --redact` writes a redacted, content-free bundle
|
|
84
|
+
you can hand a teammate or founder. Local file only, never uploaded.
|
|
85
|
+
|
|
86
|
+
> **Optimization, advanced verification, apply guidance, and team reporting** are
|
|
87
|
+
> the **opt-in Compaction API**, not steps of the free local CLI (see the section
|
|
88
|
+
> above).
|
|
89
|
+
|
|
90
|
+
## Evidence levels and claims
|
|
91
|
+
|
|
92
|
+
Every figure this tool produces carries one of these labels — and nothing stronger:
|
|
93
|
+
|
|
94
|
+
| Label | Meaning |
|
|
95
|
+
|---|---|
|
|
96
|
+
| **local estimate** | chars/4 token heuristics and price-table cost approximations computed locally |
|
|
97
|
+
| **provider-reported token delta** | token counts read from the provider's own API response/usage metadata (operator-entered or captured); input tokens are deterministic for a fixed context+prompt |
|
|
98
|
+
| **token-estimated cost** | a price-table estimate applied to provider-reported token counts (`price_table_estimate`) — an estimate, never a billed figure |
|
|
99
|
+
| **operator-entered billing record** | billed costs the operator read from their own provider console; recorded, not provider-verified by this tool |
|
|
100
|
+
| **billing-confirmed** | only with real provider billing/invoice evidence **AND** the accepted measurement criterion met on those billed runs (N ≥ 3 runs per arm AND the delta-of-means ±2·SE interval excludes zero) — the statistical criterion alone is a bar on operator-entered figures, not proof of billing, so it is never sufficient by itself — **no figure in this repository currently carries this label** |
|
|
101
|
+
|
|
102
|
+
**What is NOT claimed:** no billing-confirmed savings claim; no invoice savings claim; no general savings claim (all results are workflow-scoped and caveated); no auto-apply (every application is an explicit operator gesture); no hosted service or control plane (the free CLI is local-first, file-based); no universal "no context is lost" claim; no semantic/meaning preservation claim (it stays `not_evaluated`); no native/production/Level 3 integration claim.
|
|
103
|
+
|
|
104
|
+
## Install
|
|
105
|
+
|
|
106
|
+
This is a local-first CLI. It makes no network calls by default; the only
|
|
107
|
+
command that reaches the network is the explicitly operator-invoked
|
|
108
|
+
`capture provider-usage`, and only when the operator supplies a credential via
|
|
109
|
+
an environment variable. There is no telemetry.
|
|
110
|
+
|
|
111
|
+
### curl + npm / npx — AVAILABLE AT LAUNCH (pending publish; not live today)
|
|
112
|
+
|
|
113
|
+
**The public install path is not live yet.** The package is **not yet published**
|
|
114
|
+
to npm, and the installer is not yet hosted at `cli.compaction.dev`. The commands
|
|
115
|
+
below are the intended public install path and will work **only at launch**, once
|
|
116
|
+
the package is published **and** the script is hosted. Do not expect them to work
|
|
117
|
+
today.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# AVAILABLE AT LAUNCH — not live today.
|
|
121
|
+
# One-line installer (hosted at cli.compaction.dev at launch):
|
|
122
|
+
curl -fsSL https://cli.compaction.dev/install | sh
|
|
123
|
+
# Inspect it first (recommended): pipe to `less`, or download and read, before sh.
|
|
124
|
+
curl -fsSL https://cli.compaction.dev/install | less
|
|
125
|
+
|
|
126
|
+
# or with npm / npx, once published:
|
|
127
|
+
npm install -g @compaction/cli # then: compaction init
|
|
128
|
+
npx @compaction/cli init # no global install
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The installer ([`scripts/install.sh`](scripts/install.sh)) is checked into the repo
|
|
132
|
+
and readable now: it uses **no `sudo` by default** (installs into a user-writable
|
|
133
|
+
prefix), makes **no network call except the npm install** (no telemetry), supports
|
|
134
|
+
macOS + Linux, and **fails clearly if the package is not yet published** rather than
|
|
135
|
+
faking success. Preview it without installing anything:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
sh scripts/install.sh --dry-run
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
See [`docs/ops/install-readiness.md`](docs/ops/install-readiness.md) for the full
|
|
142
|
+
install-readiness contract (hosting, package name, version pinning, platforms, and
|
|
143
|
+
the pre-publish state).
|
|
144
|
+
|
|
145
|
+
This repository is licensed under **AGPL-3.0-only** (see [`LICENSE`](LICENSE)). The
|
|
146
|
+
npm **package name is `@compaction/cli`** (scoped, public; the installed binary
|
|
147
|
+
stays `compaction`). It is **not yet published** — the `npm install` / `npx`
|
|
148
|
+
commands above stay PENDING until the founder runs `npm publish`. Until publish,
|
|
149
|
+
use the from-source path (recommended) or the local-tarball fallback below.
|
|
150
|
+
|
|
151
|
+
### From source — recommended today
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
git clone <repo-url>
|
|
155
|
+
cd compaction-dev
|
|
156
|
+
npm ci
|
|
157
|
+
npm run build
|
|
158
|
+
# optional: expose the `compaction` binary on your PATH
|
|
159
|
+
npm link
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
For reproducible local validation, use `npm ci` (not `npm install`).
|
|
163
|
+
|
|
164
|
+
### Local tarball install (temporary internal/tester fallback)
|
|
165
|
+
|
|
166
|
+
Until the package is on npm, an external tester can install the packed tarball
|
|
167
|
+
directly. This is a **temporary fallback**, not the intended public path:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npm pack
|
|
171
|
+
npm install -g ./compaction-cli-0.1.0.tgz
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The package layout is verified by `npm run smoke:package`, which packs the
|
|
175
|
+
tarball, installs it into a scratch prefix outside the repo, and runs
|
|
176
|
+
`compaction --help`, `compaction init`, and a capture → compact step from the
|
|
177
|
+
installed binary. The packed tarball ships the local CLI, `README.md`, and
|
|
178
|
+
`LICENSE` only.
|
|
179
|
+
|
|
180
|
+
### Sanity check
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
compaction --help
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## CLI (free, local)
|
|
187
|
+
|
|
188
|
+
The free local commands:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
compaction init # scaffold local config
|
|
192
|
+
compaction capture claude-code --session <session.jsonl> [--include-subagents] --out <dir>
|
|
193
|
+
compaction capture claude-code --discover [--projects-dir <path>]
|
|
194
|
+
compaction capture openai-agents --out <dir> -- <command> [args...]
|
|
195
|
+
compaction capture openai-agents --out <dir> --export <local-jsonl-export>
|
|
196
|
+
compaction capture provider-usage --endpoint <url> [--credential-env-var <NAME>] [--out <dir>]
|
|
197
|
+
compaction import <trace-file> --source <source> --out <dir>
|
|
198
|
+
compaction import --list-sources
|
|
199
|
+
compaction analyze <trace-file>
|
|
200
|
+
compaction feedback --redact [--yes]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### `capture claude-code` (primary supported workflow)
|
|
204
|
+
|
|
205
|
+
Captures a real Claude Code session JSONL (optionally including subagent
|
|
206
|
+
transcripts) into the internal trace format, preserving provider-reported token
|
|
207
|
+
usage from the session's own API response metadata. `--discover` scans the local
|
|
208
|
+
Claude Code projects directory read-only and lists sessions with metadata (no
|
|
209
|
+
network, no upload). This is **Integration Readiness Level 2** (local
|
|
210
|
+
command-wrapper) — not Level 3 native SDK instrumentation. Captured traces may
|
|
211
|
+
contain local file content — review before sharing.
|
|
212
|
+
|
|
213
|
+
### `capture openai-agents` (Level 2 command-wrapper)
|
|
214
|
+
|
|
215
|
+
Runs a local command or reads a local JSONL export containing OpenAI Agents
|
|
216
|
+
SDK-style trace/span events, normalizes the supported events into the internal
|
|
217
|
+
trace format, and writes capture evidence locally. It distinguishes
|
|
218
|
+
provider-reported usage from local estimates: when supported events carry provider
|
|
219
|
+
token fields, those values are labeled provider-reported; when absent, token
|
|
220
|
+
metadata is marked `missing`/`unknown` and **never invented**. This is local
|
|
221
|
+
capture only (Level 2) — it does **not** scrape the ChatGPT app, use undocumented
|
|
222
|
+
APIs, upload traces, proxy provider/model calls, switch providers/models, or add a
|
|
223
|
+
dashboard/auth/billing.
|
|
224
|
+
|
|
225
|
+
### `capture provider-usage` (the only network call)
|
|
226
|
+
|
|
227
|
+
Explicitly operator-invoked, read-only read of aggregate usage/cost from a provider
|
|
228
|
+
usage API. The credential comes from an environment variable only (never a flag
|
|
229
|
+
value, never logged, never written into artifacts); absence is a clean refusal. It
|
|
230
|
+
captures aggregate token/cost figures only — no prompt/completion content. The
|
|
231
|
+
resulting record is classified as provider-usage evidence.
|
|
232
|
+
|
|
233
|
+
### `import` (Codex / local-file traces — Level 1)
|
|
234
|
+
|
|
235
|
+
Reads a local trace-like JSON/JSONL file (including a `codex exec --json` export),
|
|
236
|
+
validates whether it can be converted into the internal trace format, and writes
|
|
237
|
+
normalized intake artifacts to an output directory. Import is local and file-based
|
|
238
|
+
only: it does not mutate the source file, upload data, call provider APIs, route
|
|
239
|
+
models, create a hosted service, use a database, add auth, or add billing. List
|
|
240
|
+
supported sources with `compaction import --list-sources`.
|
|
241
|
+
|
|
242
|
+
Codex token counts on the import path are **locally estimated (chars/4)**, not
|
|
243
|
+
provider-reported. Live provider/runtime integrations (native Claude Code/Cursor
|
|
244
|
+
instrumentation, live Codex launching, hosted provider APIs) remain future work; the
|
|
245
|
+
Codex support here is intentionally local/export-file based only (Level 1).
|
|
246
|
+
|
|
247
|
+
### `analyze`
|
|
248
|
+
|
|
249
|
+
Reads a trace file and prints trace metadata, estimated input/output/total tokens,
|
|
250
|
+
an estimated local cost, conservative waste findings, and suggested
|
|
251
|
+
duplicate-output compaction opportunities (advisory only — the free CLI does **not**
|
|
252
|
+
apply them; optimization is the opt-in Compaction API).
|
|
253
|
+
|
|
254
|
+
### `feedback --redact`
|
|
255
|
+
|
|
256
|
+
Builds a **redacted, privacy-safe feedback bundle** as a **local file only — never
|
|
257
|
+
uploaded**. Redaction is best-effort and the bundle's own README states what is
|
|
258
|
+
included/excluded; writing the bundle requires an explicit write gesture
|
|
259
|
+
(`--yes`/`--confirm`). There is no upload path.
|
|
260
|
+
|
|
261
|
+
## Known limitations
|
|
262
|
+
|
|
263
|
+
- **The free CLI captures and measures; it does not optimize or verify.**
|
|
264
|
+
Optimization, advanced verification, apply guidance, and team reporting are the
|
|
265
|
+
**opt-in Compaction API** — not the free local CLI. The API is **not broadly
|
|
266
|
+
hosted yet**.
|
|
267
|
+
- **Cost figures are estimates or operator-entered records, not billing-confirmed.**
|
|
268
|
+
Locally computed cost figures are price-table estimates; provider-usage records
|
|
269
|
+
carry operator-entered/captured console figures (not provider-verified by this
|
|
270
|
+
tool). **No figure anywhere in this repository is billing-confirmed**; the accepted
|
|
271
|
+
criterion that could one day support such a label (N ≥ 3 per arm, ±2·SE interval
|
|
272
|
+
excluding zero, on real billed runs) has not been met.
|
|
273
|
+
- **Token counts are local estimates unless provider-reported usage is captured.**
|
|
274
|
+
The default is a chars/4 estimate, labeled as such; counts are provider-reported
|
|
275
|
+
only when the captured run carries provider usage metadata. Unknown stays unknown —
|
|
276
|
+
output tokens, provider, or cost the data does not carry are never inferred.
|
|
277
|
+
- **Integrations are Level 1/2, not native/production.** Claude Code and OpenAI
|
|
278
|
+
Agents capture are **Level 2** local command-wrappers; Codex/local-file import is
|
|
279
|
+
**Level 1**. There is **no** native/Level 3 SDK instrumentation, no production
|
|
280
|
+
proxy, no model routing, and no ChatGPT/Claude consumer-app support.
|
|
281
|
+
- **Advanced verification (API) is deterministic recoverability, not semantic
|
|
282
|
+
preservation.** Where the opt-in API verifies a compacted context, it checks
|
|
283
|
+
deterministic recoverability for a **supported** workflow; it does **not** evaluate
|
|
284
|
+
meaning or task equivalence, and it does **not** re-run your agent through a model.
|
|
285
|
+
SEMANTIC / meaning preservation remains **not evaluated**, and true model replay is
|
|
286
|
+
**future work**.
|
|
287
|
+
- **No universal "no context is lost" guarantee.** Any apply-guidance verdict from
|
|
288
|
+
the opt-in API is **fail-closed and scoped** to supported developer-agent
|
|
289
|
+
workflows — never a universal guarantee, and never a semantic/meaning claim.
|
|
290
|
+
|
|
291
|
+
## Product constraints
|
|
292
|
+
|
|
293
|
+
- CLI-first, not web-app-first. No web framework, no database, no auth or billing, no
|
|
294
|
+
streaming.
|
|
295
|
+
- No provider integrations in the free CLI except the explicitly operator-invoked,
|
|
296
|
+
read-only `capture provider-usage` (env-only credential; aggregate usage/cost only).
|
|
297
|
+
No proxying, no model routing, no live-call interception.
|
|
298
|
+
- No hosted service or control plane in the free CLI; no full observability dashboard.
|
|
299
|
+
|
|
300
|
+
**On the roadmap / opt-in API (NOT in the free CLI; reads as future/opt-in anywhere
|
|
301
|
+
public):** the Compaction API's optimization and advanced verification, apply
|
|
302
|
+
guidance, and team reporting; a hosted dashboard / control plane; native (Level 3)
|
|
303
|
+
SDK instrumentation; model routing / provider switching / live-call interception; an
|
|
304
|
+
IDE plugin; true model replay; semantic / meaning preservation (today not
|
|
305
|
+
evaluated); and billing-confirmed savings (no figure in this repository carries that
|
|
306
|
+
label). **The Compaction API is not broadly hosted yet** — never imply a live hosted
|
|
307
|
+
service exists.
|
|
308
|
+
|
|
309
|
+
## Contributing / internal docs
|
|
310
|
+
|
|
311
|
+
Repository development, the marketing/docs website (`apps/web`), and contributor
|
|
312
|
+
workflows live in the repo's internal documentation, **not** in this npm-facing
|
|
313
|
+
README:
|
|
314
|
+
|
|
315
|
+
- `AGENTS.md` — the agent operating contract.
|
|
316
|
+
- `ROADMAP.md` — canonical strategy and the long-term path.
|
|
317
|
+
- `docs/product/product-index.md` — the product-docs entry point (`mvp-plan.md`,
|
|
318
|
+
`decisions.md`).
|
|
319
|
+
- `docs/ops/install-readiness.md` — the install/publish readiness contract.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { validateAdapterFixtureFile } from "../../core/adapter-validation.js";
|
|
3
|
+
import { listTraceAdapters } from "../../core/trace-adapters.js";
|
|
4
|
+
function printSupportedAdapters() {
|
|
5
|
+
console.log(chalk.cyan("compaction adapter sources"));
|
|
6
|
+
for (const adapter of listTraceAdapters()) {
|
|
7
|
+
console.log(`- ${adapter.id}: ${adapter.displayName}`);
|
|
8
|
+
console.log(` ${adapter.description}`);
|
|
9
|
+
}
|
|
10
|
+
console.log("Adapter Fixture Harness v0 is local and file-based only; live provider/runtime integrations are future work.");
|
|
11
|
+
}
|
|
12
|
+
export function registerAdapterCommand(program) {
|
|
13
|
+
const adapter = program.command("adapter").description("Validate local adapter fixtures without live provider integrations.");
|
|
14
|
+
adapter.command("list").description("List local trace adapters available to the fixture harness.").action(() => {
|
|
15
|
+
printSupportedAdapters();
|
|
16
|
+
});
|
|
17
|
+
adapter
|
|
18
|
+
.command("validate")
|
|
19
|
+
.argument("<fixture-file>", "Path to a local adapter fixture JSON or JSONL file")
|
|
20
|
+
.requiredOption("--source <source>", "Input source from the local adapter registry")
|
|
21
|
+
.description("Validate a local fixture through adapter canHandle and normalize.")
|
|
22
|
+
.action(async (fixtureFile, options) => {
|
|
23
|
+
if (!options.source) {
|
|
24
|
+
throw new Error("Missing required option --source <source>.");
|
|
25
|
+
}
|
|
26
|
+
const artifacts = await validateAdapterFixtureFile(fixtureFile, options.source);
|
|
27
|
+
console.log(chalk.cyan("compaction adapter validate"));
|
|
28
|
+
console.log(`Status: ${artifacts.report.status}`);
|
|
29
|
+
console.log(`Validation ID: ${artifacts.report.validation_id}`);
|
|
30
|
+
console.log(`Source: ${artifacts.report.source}`);
|
|
31
|
+
console.log(`Adapter: ${artifacts.report.adapter_id}`);
|
|
32
|
+
console.log(`Supported format detected: ${artifacts.report.supported_format_detected ? "yes" : "no"}`);
|
|
33
|
+
console.log(`Messages: ${artifacts.report.message_count}`);
|
|
34
|
+
console.log(chalk.green(`Wrote ${artifacts.paths.validationReportJsonPath}`));
|
|
35
|
+
console.log(chalk.green(`Wrote ${artifacts.paths.validationReportMarkdownPath}`));
|
|
36
|
+
if (artifacts.normalizedTrace) {
|
|
37
|
+
console.log(chalk.green(`Wrote ${artifacts.paths.normalizedTracePath}`));
|
|
38
|
+
console.log(`Recommended next command: ${artifacts.report.recommended_next_command}`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.log(chalk.red("No normalized trace was generated."));
|
|
42
|
+
for (const failure of artifacts.report.failures) {
|
|
43
|
+
console.log(chalk.red(`Failure: ${failure}`));
|
|
44
|
+
}
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { writeJsonArtifact, writeTextArtifact } from "../../core/artifact-writer.js";
|
|
3
|
+
import { createAggregateReport, AGGREGATE_ARTIFACT_DIRECTORY } from "../../core/session-aggregate.js";
|
|
4
|
+
import { buildAggregateShareBundle, formatAggregateMarkdown, formatAggregateReport, formatShareBundleMarkdown } from "../../core/aggregate-format.js";
|
|
5
|
+
import { normalizeRunLabels } from "../../core/run-labels.js";
|
|
6
|
+
/** Collect ONLY the label fields that were actually supplied, so absent ≡ no filter. */
|
|
7
|
+
function suppliedLabels(options) {
|
|
8
|
+
return normalizeRunLabels({
|
|
9
|
+
project: options.project,
|
|
10
|
+
workflow: options.workflow,
|
|
11
|
+
provider: options.provider,
|
|
12
|
+
user_label: options.userLabel,
|
|
13
|
+
session: options.session
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export function registerAggregateCommand(program) {
|
|
17
|
+
program
|
|
18
|
+
.command("aggregate")
|
|
19
|
+
.description("Roll up local compaction runs into per-session + cross-session token / cost / estimated-savings " +
|
|
20
|
+
"and verification-outcome aggregates. LOCAL ONLY — reads .compaction/runs/*, makes NO network call, " +
|
|
21
|
+
"writes only local files. No hosted dashboard.")
|
|
22
|
+
.option("--runs <dir>", "Runs directory to aggregate", ".compaction/runs")
|
|
23
|
+
.option("--out <dir>", "Directory for the aggregate artifacts", AGGREGATE_ARTIFACT_DIRECTORY)
|
|
24
|
+
.option("--share", "Also write a redacted, content-free AGGREGATE share bundle (aggregate numbers + labels + evidence " +
|
|
25
|
+
"tiers, NOT raw traces) a developer can hand a founder/admin. Local file only — never uploaded.")
|
|
26
|
+
.option("--project <name>", "Local-only: roll up ONLY runs tagged with this project label")
|
|
27
|
+
.option("--workflow <name>", "Local-only: roll up ONLY runs tagged with this workflow label")
|
|
28
|
+
.option("--provider <name>", "Local-only: roll up ONLY runs tagged with this provider/runtime label (operator-asserted)")
|
|
29
|
+
.option("--user-label <text>", "Local-only: roll up ONLY runs tagged with this free-form user label")
|
|
30
|
+
.option("--session <id>", "Local-only: roll up ONLY runs tagged with this session id")
|
|
31
|
+
.action(async (options) => {
|
|
32
|
+
const filters = suppliedLabels(options);
|
|
33
|
+
const runsDir = options.runs ?? ".compaction/runs";
|
|
34
|
+
const outDir = options.out ?? AGGREGATE_ARTIFACT_DIRECTORY;
|
|
35
|
+
const report = await createAggregateReport(runsDir, {
|
|
36
|
+
filters,
|
|
37
|
+
rollupLabels: filters
|
|
38
|
+
});
|
|
39
|
+
const consoleReport = formatAggregateReport(report);
|
|
40
|
+
const markdown = formatAggregateMarkdown(report);
|
|
41
|
+
const aggregateDir = `${outDir}/${report.aggregate_id}`;
|
|
42
|
+
const jsonPath = await writeJsonArtifact(aggregateDir, "aggregate-report.json", report);
|
|
43
|
+
const markdownPath = await writeTextArtifact(aggregateDir, "aggregate-report.md", markdown);
|
|
44
|
+
console.log(chalk.cyan(consoleReport));
|
|
45
|
+
console.log("");
|
|
46
|
+
console.log(chalk.green(`Wrote ${jsonPath}`));
|
|
47
|
+
console.log(chalk.green(`Wrote ${markdownPath}`));
|
|
48
|
+
if (options.share === true) {
|
|
49
|
+
const shareBundle = buildAggregateShareBundle(report);
|
|
50
|
+
const shareJsonPath = await writeJsonArtifact(aggregateDir, "aggregate-share.json", shareBundle);
|
|
51
|
+
const shareMarkdownPath = await writeTextArtifact(aggregateDir, "aggregate-share.md", formatShareBundleMarkdown(shareBundle));
|
|
52
|
+
console.log("");
|
|
53
|
+
console.log(chalk.bold("Redacted aggregate share bundle (content-free): aggregate numbers + labels + evidence tiers only — " +
|
|
54
|
+
"NO raw traces, run ids, or paths. Local file only; never uploaded."));
|
|
55
|
+
console.log(chalk.green(`Wrote ${shareJsonPath}`));
|
|
56
|
+
console.log(chalk.green(`Wrote ${shareMarkdownPath}`));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=aggregate.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
import { calculateCost } from "../../core/cost-calculator.js";
|
|
5
|
+
import { formatAnalyzeReport } from "../../core/report-generator.js";
|
|
6
|
+
import { estimateTraceTokens } from "../../core/token-estimator.js";
|
|
7
|
+
import { detectWaste, buildSkillInjectionAdvisory } from "../../core/waste-detector.js";
|
|
8
|
+
import { parseTraceFile } from "../../core/trace-parser.js";
|
|
9
|
+
/**
|
|
10
|
+
* Try to load sibling capture-report.json from the same directory as the trace file.
|
|
11
|
+
* Returns the usage metadata if found and valid, otherwise undefined.
|
|
12
|
+
* Never throws — missing or malformed capture-report.json is silently ignored.
|
|
13
|
+
*/
|
|
14
|
+
async function tryLoadCaptureUsage(traceFile) {
|
|
15
|
+
try {
|
|
16
|
+
const dir = dirname(traceFile);
|
|
17
|
+
const captureReportPath = join(dir, "capture-report.json");
|
|
18
|
+
const raw = await readFile(captureReportPath, "utf8");
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
if (parsed.usage && typeof parsed.usage === "object") {
|
|
21
|
+
return parsed.usage;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// No capture-report.json, or unreadable/malformed — silently ignore.
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
export function registerAnalyzeCommand(program) {
|
|
30
|
+
program
|
|
31
|
+
.command("analyze")
|
|
32
|
+
.argument("<trace-file>", "Path to a local agent trace JSON file")
|
|
33
|
+
.description("Analyze a trace and print token, cost, and waste estimates.")
|
|
34
|
+
.action(async (traceFile) => {
|
|
35
|
+
const trace = await parseTraceFile(traceFile);
|
|
36
|
+
const tokens = estimateTraceTokens(trace);
|
|
37
|
+
const findings = detectWaste(trace);
|
|
38
|
+
// Report-only skill-injection advisory. Built from a SEPARATE detector; nothing is
|
|
39
|
+
// compacted and it never affects `findings` / the tool-output compaction path.
|
|
40
|
+
const skillInjectionAdvisory = buildSkillInjectionAdvisory(trace);
|
|
41
|
+
// Load usage from sibling capture-report.json if available (provides cache token counts).
|
|
42
|
+
const captureUsage = await tryLoadCaptureUsage(traceFile);
|
|
43
|
+
const cost = calculateCost(trace.model, tokens, captureUsage
|
|
44
|
+
? { cacheReadTokens: captureUsage.cache_read_input_tokens, cacheCreationTokens: captureUsage.cache_creation_input_tokens }
|
|
45
|
+
: undefined);
|
|
46
|
+
console.log(chalk.cyan("compaction analyze"));
|
|
47
|
+
console.log(formatAnalyzeReport(trace, tokens, cost, findings, captureUsage, skillInjectionAdvisory));
|
|
48
|
+
// Exact next command to continue the loop (chaining), with the trace path the user passed.
|
|
49
|
+
console.log("");
|
|
50
|
+
console.log(chalk.bold("Next: see the recommended policy and estimated delta:"));
|
|
51
|
+
console.log(` compaction recommend ${traceFile}`);
|
|
52
|
+
console.log(` compaction compact ${traceFile} --out <dir> # before/after delta + named policy + safety report`);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* `compaction apply-context <trace-file> --out <dir>` — LEVEL-5 IN-WORKFLOW APPLY, Form A.
|
|
4
|
+
*
|
|
5
|
+
* Operator-invoked, local-first. compaction.dev runs the existing compaction, the pre-apply safety
|
|
6
|
+
* checks, ALWAYS retains the un-compacted original, and produces the original-vs-compacted review.
|
|
7
|
+
* It emits the approved compacted context ONLY on the explicit per-application approval gesture
|
|
8
|
+
* `--approve-in-workflow-use`. It makes NO live provider call, holds NO credential, runs no proxy /
|
|
9
|
+
* hosted component, and performs no auto-apply. The unattended `apply` path is untouched and not
|
|
10
|
+
* reachable from here.
|
|
11
|
+
*/
|
|
12
|
+
export declare function registerApplyContextCommand(program: Command): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { Option } from "commander";
|
|
4
|
+
import { parseTraceFile } from "../../core/trace-parser.js";
|
|
5
|
+
import { runEngineCommand } from "../engine-degrade.js";
|
|
6
|
+
function defaultOutputDir(artifactRoot, traceId) {
|
|
7
|
+
return path.join(artifactRoot, traceId);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* `compaction apply-context <trace-file> --out <dir>` — LEVEL-5 IN-WORKFLOW APPLY, Form A.
|
|
11
|
+
*
|
|
12
|
+
* Operator-invoked, local-first. compaction.dev runs the existing compaction, the pre-apply safety
|
|
13
|
+
* checks, ALWAYS retains the un-compacted original, and produces the original-vs-compacted review.
|
|
14
|
+
* It emits the approved compacted context ONLY on the explicit per-application approval gesture
|
|
15
|
+
* `--approve-in-workflow-use`. It makes NO live provider call, holds NO credential, runs no proxy /
|
|
16
|
+
* hosted component, and performs no auto-apply. The unattended `apply` path is untouched and not
|
|
17
|
+
* reachable from here.
|
|
18
|
+
*/
|
|
19
|
+
export function registerApplyContextCommand(program) {
|
|
20
|
+
program
|
|
21
|
+
.command("apply-context")
|
|
22
|
+
.description("Form A in-workflow apply: review original-vs-compacted, run pre-apply safety checks, and (only with explicit per-application approval) emit an approved compacted context for YOUR OWN next provider call. compaction.dev makes no live call and holds no credential.")
|
|
23
|
+
.argument("<trace-file>", "Path to a local agent trace JSON file")
|
|
24
|
+
.option("--out <dir>", "Directory where in-workflow-apply artifacts should be written")
|
|
25
|
+
.addOption(new Option("--approve-in-workflow-use", "EXPLICIT per-application approval gesture. Without it: review artifacts are produced and NO approved context is emitted. This records 'approved for in-workflow use' (NOT 'applied')."))
|
|
26
|
+
.addOption(new Option("--approve-skill-injection-policy", "EXPLICITLY also authorize the approval-required repeated_skill_injection_to_state_capsule policy. Default off; no auto-apply."))
|
|
27
|
+
.addOption(new Option("--attest-used", "OPTIONAL operator attestation: AFTER an approval, the operator records that they used the approved context in a live call. Stored as operator-ATTESTED provenance (the operator's own claim), never observed by compaction.dev."))
|
|
28
|
+
.option("--attest-note <text>", "Optional free-text note recorded with the operator attestation")
|
|
29
|
+
.action(async (traceFile, options) => {
|
|
30
|
+
await runEngineCommand(async () => {
|
|
31
|
+
// Lazy-load the proprietary engine: present in-repo/API, excluded from the public package.
|
|
32
|
+
// When absent, runEngineCommand prints the boundary message and exits cleanly (no stack trace).
|
|
33
|
+
const { IN_WORKFLOW_APPLY_ARTIFACT_ROOT, recordOperatorAttestation, runInWorkflowApply } = await import("../../engine/in-workflow-apply.js");
|
|
34
|
+
const trace = await parseTraceFile(traceFile);
|
|
35
|
+
const outputDirectory = options.out ?? defaultOutputDir(IN_WORKFLOW_APPLY_ARTIFACT_ROOT, trace.id);
|
|
36
|
+
console.log(chalk.cyan("compaction apply-context (Form A)"));
|
|
37
|
+
console.log("Operator-invoked, local-first. compaction.dev makes NO live provider call and holds NO provider credential. No proxy, no hosted component, no auto-apply.");
|
|
38
|
+
const result = await runInWorkflowApply({
|
|
39
|
+
trace,
|
|
40
|
+
outputDirectory,
|
|
41
|
+
approveInWorkflowUse: options.approveInWorkflowUse === true,
|
|
42
|
+
approveSkillInjectionPolicy: options.approveSkillInjectionPolicy === true,
|
|
43
|
+
generatedAt: trace.generatedAt
|
|
44
|
+
});
|
|
45
|
+
console.log(result.consoleSummary);
|
|
46
|
+
console.log(chalk.green(`Wrote ${result.paths.preApplyReviewJsonPath}`));
|
|
47
|
+
console.log(chalk.green(`Wrote ${result.paths.preApplyReviewMarkdownPath}`));
|
|
48
|
+
console.log(chalk.green(`Wrote ${result.paths.retainedOriginalPath}`));
|
|
49
|
+
if (result.paths.approvedContextPath) {
|
|
50
|
+
console.log(chalk.green(`Wrote ${result.paths.approvedContextPath}`));
|
|
51
|
+
}
|
|
52
|
+
if (result.paths.applyApprovalRecordPath) {
|
|
53
|
+
console.log(chalk.green(`Wrote ${result.paths.applyApprovalRecordPath}`));
|
|
54
|
+
}
|
|
55
|
+
// Optional operator attestation (only meaningful once an approval record exists).
|
|
56
|
+
if (options.attestUsed === true) {
|
|
57
|
+
if (result.approvalRecord && result.paths.applyApprovalRecordPath) {
|
|
58
|
+
const { record, path: attestationPath } = await recordOperatorAttestation({
|
|
59
|
+
outputDirectory,
|
|
60
|
+
applyApprovalRecord: result.approvalRecord,
|
|
61
|
+
applyApprovalRecordPath: result.paths.applyApprovalRecordPath,
|
|
62
|
+
generatedAt: trace.generatedAt,
|
|
63
|
+
operatorNote: options.attestNote
|
|
64
|
+
});
|
|
65
|
+
console.log(chalk.yellow(`Operator attestation recorded (operator-attested, NOT observed): ${record.provenance_label}`));
|
|
66
|
+
console.log(chalk.green(`Wrote ${attestationPath}`));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(chalk.yellow("--attest-used ignored: there is no approval record to attest against (approve in-workflow use first)."));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
console.log(chalk.green(`Output directory: ${result.outputDirectory}`));
|
|
73
|
+
// Exit non-zero when no approved context was emitted (refusal / review-only), mirroring apply/approve.
|
|
74
|
+
if (!result.approvedContextEmitted) {
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=apply-context.js.map
|