@compaction/cli 0.1.4 → 0.3.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 (170) hide show
  1. package/README.md +51 -12
  2. package/dist/cli/commands/activity.d.ts +8 -0
  3. package/dist/cli/commands/activity.js +35 -0
  4. package/dist/cli/commands/apply-context.js +68 -0
  5. package/dist/cli/commands/billing-delta.js +12 -1
  6. package/dist/cli/commands/capture-claude-code.d.ts +44 -0
  7. package/dist/cli/commands/capture-claude-code.js +206 -0
  8. package/dist/cli/commands/capture.js +283 -1
  9. package/dist/cli/commands/compact.js +24 -11
  10. package/dist/cli/commands/context.d.ts +2 -0
  11. package/dist/cli/commands/context.js +130 -0
  12. package/dist/cli/commands/dev.d.ts +17 -0
  13. package/dist/cli/commands/dev.js +131 -0
  14. package/dist/cli/commands/gateway.d.ts +31 -0
  15. package/dist/cli/commands/gateway.js +312 -0
  16. package/dist/cli/commands/hooks.d.ts +5 -0
  17. package/dist/cli/commands/hooks.js +181 -0
  18. package/dist/cli/commands/import.js +3 -1
  19. package/dist/cli/commands/init.js +422 -17
  20. package/dist/cli/commands/input-compaction-ab.d.ts +2 -0
  21. package/dist/cli/commands/input-compaction-ab.js +125 -0
  22. package/dist/cli/commands/optimize-hosted.d.ts +54 -0
  23. package/dist/cli/commands/optimize-hosted.js +123 -0
  24. package/dist/cli/commands/optimize.js +8 -0
  25. package/dist/cli/commands/output-shaping-ab.d.ts +2 -0
  26. package/dist/cli/commands/output-shaping-ab.js +132 -0
  27. package/dist/cli/commands/output-shaping.d.ts +7 -0
  28. package/dist/cli/commands/output-shaping.js +43 -0
  29. package/dist/cli/commands/policies.d.ts +2 -0
  30. package/dist/cli/commands/policies.js +80 -0
  31. package/dist/cli/commands/precall.d.ts +19 -0
  32. package/dist/cli/commands/precall.js +436 -0
  33. package/dist/cli/commands/recommend.js +1 -1
  34. package/dist/cli/commands/run.js +310 -18
  35. package/dist/cli/commands/upgrade-status.d.ts +42 -0
  36. package/dist/cli/commands/upgrade-status.js +152 -0
  37. package/dist/cli/cursor-export-read.d.ts +13 -0
  38. package/dist/cli/cursor-export-read.js +53 -0
  39. package/dist/cli/cursor-live-preflight.d.ts +19 -0
  40. package/dist/cli/cursor-live-preflight.js +46 -0
  41. package/dist/cli/index.js +39 -2
  42. package/dist/cli/onboarding/GatewayTui.d.ts +33 -0
  43. package/dist/cli/onboarding/GatewayTui.js +113 -0
  44. package/dist/cli/onboarding/model.d.ts +32 -1
  45. package/dist/cli/onboarding/model.js +27 -2
  46. package/dist/cli/terminal-logo.d.ts +27 -0
  47. package/dist/cli/terminal-logo.js +49 -0
  48. package/dist/core/activity-event.d.ts +107 -0
  49. package/dist/core/activity-event.js +182 -0
  50. package/dist/core/activity-store.d.ts +63 -0
  51. package/dist/core/activity-store.js +254 -0
  52. package/dist/core/activity-view.d.ts +76 -0
  53. package/dist/core/activity-view.js +120 -0
  54. package/dist/core/api-client/index.d.ts +3 -1
  55. package/dist/core/api-client/index.js +4 -0
  56. package/dist/core/api-client/payload.d.ts +5 -1
  57. package/dist/core/api-client/payload.js +8 -6
  58. package/dist/core/api-client/persisted-config.d.ts +57 -0
  59. package/dist/core/api-client/persisted-config.js +156 -0
  60. package/dist/core/api-client/tool.d.ts +29 -0
  61. package/dist/core/api-client/tool.js +50 -0
  62. package/dist/core/api-client/types.d.ts +15 -4
  63. package/dist/core/auto-apply-ask.d.ts +58 -0
  64. package/dist/core/auto-apply-ask.js +105 -0
  65. package/dist/core/auto-apply-gates.d.ts +76 -0
  66. package/dist/core/auto-apply-gates.js +113 -0
  67. package/dist/core/before-call-activity.d.ts +35 -0
  68. package/dist/core/before-call-activity.js +103 -0
  69. package/dist/core/before-call-recovery.d.ts +19 -0
  70. package/dist/core/before-call-recovery.js +46 -0
  71. package/dist/core/before-call-stdin.d.ts +59 -0
  72. package/dist/core/before-call-stdin.js +78 -0
  73. package/dist/core/before-call.d.ts +149 -0
  74. package/dist/core/before-call.js +358 -0
  75. package/dist/core/billing-delta/billing-delta-record.d.ts +28 -0
  76. package/dist/core/billing-delta/billing-delta-record.js +26 -0
  77. package/dist/core/capture-record.d.ts +40 -0
  78. package/dist/core/capture-record.js +55 -0
  79. package/dist/core/claude-code-before-call.d.ts +37 -0
  80. package/dist/core/claude-code-before-call.js +120 -0
  81. package/dist/core/claude-code-connect.d.ts +31 -0
  82. package/dist/core/claude-code-connect.js +87 -0
  83. package/dist/core/claude-code-hook-record.d.ts +71 -0
  84. package/dist/core/claude-code-hook-record.js +0 -0
  85. package/dist/core/claude-code-hooks.d.ts +77 -0
  86. package/dist/core/claude-code-hooks.js +141 -0
  87. package/dist/core/codex-capture.d.ts +45 -0
  88. package/dist/core/codex-capture.js +204 -0
  89. package/dist/core/command-runner.js +5 -1
  90. package/dist/core/compaction-artifacts.d.ts +51 -0
  91. package/dist/core/compaction-artifacts.js +242 -0
  92. package/dist/core/compactor.d.ts +9 -0
  93. package/dist/core/compactor.js +95 -0
  94. package/dist/core/context-store-eval-cases.d.ts +6 -0
  95. package/dist/core/context-store-eval-cases.js +331 -0
  96. package/dist/core/context-store-eval.d.ts +140 -0
  97. package/dist/core/context-store-eval.js +138 -0
  98. package/dist/core/context-store-fs.d.ts +73 -0
  99. package/dist/core/context-store-fs.js +157 -0
  100. package/dist/core/context-store-sufficiency.d.ts +98 -0
  101. package/dist/core/context-store-sufficiency.js +135 -0
  102. package/dist/core/context-store.d.ts +155 -0
  103. package/dist/core/context-store.js +228 -0
  104. package/dist/core/cross-surface-event.d.ts +306 -0
  105. package/dist/core/cross-surface-event.js +330 -0
  106. package/dist/core/cursor-capture.d.ts +54 -0
  107. package/dist/core/cursor-capture.js +215 -0
  108. package/dist/core/cursor-preflight-probe.d.ts +8 -0
  109. package/dist/core/cursor-preflight-probe.js +88 -0
  110. package/dist/core/cursor-preflight.d.ts +86 -0
  111. package/dist/core/cursor-preflight.js +126 -0
  112. package/dist/core/gateway/apply-activation.d.ts +39 -0
  113. package/dist/core/gateway/apply-activation.js +84 -0
  114. package/dist/core/gateway/apply-policy.d.ts +64 -0
  115. package/dist/core/gateway/apply-policy.js +221 -0
  116. package/dist/core/gateway/apply-receipt.d.ts +36 -0
  117. package/dist/core/gateway/apply-receipt.js +75 -0
  118. package/dist/core/gateway/cache-proof.d.ts +45 -0
  119. package/dist/core/gateway/cache-proof.js +65 -0
  120. package/dist/core/gateway/configure.d.ts +50 -0
  121. package/dist/core/gateway/configure.js +169 -0
  122. package/dist/core/gateway/openai-usage.d.ts +56 -0
  123. package/dist/core/gateway/openai-usage.js +128 -0
  124. package/dist/core/gateway/receipt.d.ts +138 -0
  125. package/dist/core/gateway/receipt.js +120 -0
  126. package/dist/core/gateway/recovery.d.ts +23 -0
  127. package/dist/core/gateway/recovery.js +68 -0
  128. package/dist/core/gateway/server.d.ts +51 -0
  129. package/dist/core/gateway/server.js +276 -0
  130. package/dist/core/gateway/status.d.ts +45 -0
  131. package/dist/core/gateway/status.js +109 -0
  132. package/dist/core/hook-usage-aggregate.d.ts +47 -0
  133. package/dist/core/hook-usage-aggregate.js +161 -0
  134. package/dist/core/input-compaction-ab.d.ts +111 -0
  135. package/dist/core/input-compaction-ab.js +158 -0
  136. package/dist/core/local-run-record.d.ts +109 -0
  137. package/dist/core/local-run-record.js +223 -0
  138. package/dist/core/output-shaping-ab.d.ts +140 -0
  139. package/dist/core/output-shaping-ab.js +146 -0
  140. package/dist/core/output-shaping-attach.d.ts +31 -0
  141. package/dist/core/output-shaping-attach.js +57 -0
  142. package/dist/core/output-shaping.d.ts +56 -0
  143. package/dist/core/output-shaping.js +89 -0
  144. package/dist/core/policy-middleware.d.ts +121 -0
  145. package/dist/core/policy-middleware.js +919 -0
  146. package/dist/core/policy-preferences.d.ts +99 -0
  147. package/dist/core/policy-preferences.js +232 -0
  148. package/dist/core/report-generator.d.ts +19 -1
  149. package/dist/core/report-generator.js +51 -0
  150. package/dist/core/run-aggregator.d.ts +56 -1
  151. package/dist/core/run-aggregator.js +93 -2
  152. package/dist/core/run-flow-report.d.ts +82 -0
  153. package/dist/core/run-flow-report.js +71 -0
  154. package/dist/core/safety-report.js +8 -1
  155. package/dist/core/shim-capture-bridge.d.ts +32 -0
  156. package/dist/core/shim-capture-bridge.js +88 -0
  157. package/dist/core/skill-injection-policy.d.ts +72 -0
  158. package/dist/core/skill-injection-policy.js +183 -0
  159. package/dist/core/spend-attribution.js +1 -1
  160. package/dist/core/token-accounting.d.ts +1 -1
  161. package/dist/core/tool-shim.d.ts +129 -0
  162. package/dist/core/tool-shim.js +447 -0
  163. package/dist/core/trace-adapters.d.ts +8 -0
  164. package/dist/core/trace-adapters.js +4 -0
  165. package/dist/core/trace-parser.d.ts +13 -13
  166. package/dist/core/trace-parser.js +6 -6
  167. package/dist/core/types.d.ts +45 -2
  168. package/dist/core/waste-detector.d.ts +20 -0
  169. package/dist/core/waste-detector.js +160 -6
  170. package/package.json +1 -1
@@ -0,0 +1,181 @@
1
+ /**
2
+ * `compaction hooks install|uninstall|status` — consented Claude Code Stop-hook management + a content-free
3
+ * source-status view of the records the hook produces (PUBLIC CLI).
4
+ * Installs a Stop hook that runs `compaction capture claude-code --from-hook` (content-free usage capture).
5
+ *
6
+ * Consent + safety: running this command IS the consent — it prints exactly what it will write and where,
7
+ * MERGES (never replaces) existing settings, is idempotent, supports `--dry-run` (writes nothing), and only
8
+ * ever removes Compaction's own hook on uninstall. `init` may suggest this command but must never write
9
+ * settings silently. `status` is read-only, local-first, content-free.
10
+ */
11
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
12
+ import { homedir } from "node:os";
13
+ import path from "node:path";
14
+ import chalk from "chalk";
15
+ import { CLAUDE_CODE_HOOK_COMMAND, CLAUDE_CODE_BEFORE_CALL_HOOK_COMMAND, installStopHook, installBeforeCallHook, uninstallStopHook, uninstallBeforeCallHook } from "../../core/claude-code-hooks.js";
16
+ import { aggregateHookUsageRecords, loadHookUsageRecords } from "../../core/hook-usage-aggregate.js";
17
+ /** Resolve the target settings file from the scope flags. Default: project `.claude/settings.json`. */
18
+ function resolveSettingsPath(options) {
19
+ if (options.settings)
20
+ return options.settings;
21
+ if (options.user)
22
+ return path.join(homedir(), ".claude", "settings.json");
23
+ if (options.local)
24
+ return path.join(process.cwd(), ".claude", "settings.local.json");
25
+ return path.join(process.cwd(), ".claude", "settings.json");
26
+ }
27
+ async function readSettings(file) {
28
+ try {
29
+ const raw = await readFile(file, "utf8");
30
+ const parsed = JSON.parse(raw);
31
+ return { settings: parsed && typeof parsed === "object" ? parsed : {}, existed: true };
32
+ }
33
+ catch {
34
+ return { settings: {}, existed: false };
35
+ }
36
+ }
37
+ async function writeSettings(file, settings) {
38
+ await mkdir(path.dirname(file), { recursive: true });
39
+ await writeFile(file, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
40
+ }
41
+ export function registerHooksCommand(program) {
42
+ const hooks = program
43
+ .command("hooks")
44
+ .description("Manage the Claude Code Stop hook that records CONTENT-FREE usage via `capture claude-code --from-hook`. " +
45
+ "Consented, merge-not-replace, idempotent, dry-run supported. Never writes settings without you running it.");
46
+ hooks
47
+ .command("install")
48
+ .description("Install the Compaction Stop (measurement) hook into Claude Code settings (merge-not-replace, idempotent). " +
49
+ "Add --before-call to ALSO install the UserPromptSubmit before-call RECOMMENDATION hook (run-13).")
50
+ .option("--settings <path>", "Explicit settings file to edit (overrides --local/--user).")
51
+ .option("--local", "Target project .claude/settings.local.json (personal, untracked).")
52
+ .option("--user", "Target ~/.claude/settings.json (all your projects).")
53
+ .option("--before-call", "ALSO install the UserPromptSubmit before-call RECOMMENDATION hook (a genuine pre-call event; content-free, fail-open, silent; recommendation-only — apply is a proven blocker on Claude Code hooks).")
54
+ .option("--dry-run", "Print the resulting settings without writing anything.")
55
+ .action(async (options) => {
56
+ console.log(chalk.cyan("compaction hooks install"));
57
+ const file = resolveSettingsPath(options);
58
+ const { settings, existed } = await readSettings(file);
59
+ let result;
60
+ try {
61
+ result = installStopHook(settings);
62
+ // The before-call hook merges on TOP of the Stop result (both merge-not-replace, both idempotent).
63
+ if (options.beforeCall)
64
+ result = installBeforeCallHook(result.settings);
65
+ }
66
+ catch (error) {
67
+ console.error(chalk.red(` Not installed: ${error instanceof Error ? error.message : String(error)}`));
68
+ process.exitCode = 1;
69
+ return;
70
+ }
71
+ console.log(` target settings: ${file}${existed ? "" : " (will be created)"}`);
72
+ console.log(` Stop hook: ${CLAUDE_CODE_HOOK_COMMAND} (measurement, content-free, post-session)`);
73
+ if (options.beforeCall) {
74
+ console.log(` before-call: ${CLAUDE_CODE_BEFORE_CALL_HOOK_COMMAND} (UserPromptSubmit; recommendation-only, content-free, silent)`);
75
+ }
76
+ console.log(" posture: content-free; fail-open (never breaks Claude Code); merge-not-replace; nothing is applied to your prompts.");
77
+ // `result.settings` reflects the FULL merge; `changed` reflects whether the last merge changed
78
+ // anything. Recompute "did anything change" honestly across both hooks by comparing to the read.
79
+ const changed = JSON.stringify(result.settings) !== JSON.stringify(settings);
80
+ if (!changed) {
81
+ console.log(chalk.green(" Already installed — no change (idempotent)."));
82
+ return;
83
+ }
84
+ if (options.dryRun) {
85
+ console.log(chalk.yellow(" --dry-run: NOT written. Resulting settings would be:"));
86
+ console.log(JSON.stringify(result.settings, null, 2));
87
+ return;
88
+ }
89
+ await writeSettings(file, result.settings);
90
+ console.log(chalk.green(` Installed. Open Claude Code's /hooks once (or restart) to load it.`));
91
+ console.log(chalk.gray(" Uninstall anytime: compaction hooks uninstall" + (options.settings ? ` --settings ${file}` : options.user ? " --user" : options.local ? " --local" : "")));
92
+ });
93
+ hooks
94
+ .command("uninstall")
95
+ .description("Remove ONLY Compaction's hooks (Stop measurement + UserPromptSubmit before-call) from Claude Code settings (preserves all other settings/hooks).")
96
+ .option("--settings <path>", "Explicit settings file to edit (overrides --local/--user).")
97
+ .option("--local", "Target project .claude/settings.local.json.")
98
+ .option("--user", "Target ~/.claude/settings.json.")
99
+ .option("--dry-run", "Print the resulting settings without writing anything.")
100
+ .action(async (options) => {
101
+ console.log(chalk.cyan("compaction hooks uninstall"));
102
+ const file = resolveSettingsPath(options);
103
+ const { settings, existed } = await readSettings(file);
104
+ if (!existed) {
105
+ console.log(` No settings file at ${file} — nothing to uninstall.`);
106
+ return;
107
+ }
108
+ // Remove BOTH Compaction hooks (only ours; each is scoped to its own event + command identity).
109
+ const stop = uninstallStopHook(settings);
110
+ const beforeCall = uninstallBeforeCallHook(stop.settings);
111
+ const removedCount = stop.removedCount + beforeCall.removedCount;
112
+ const finalSettings = beforeCall.settings;
113
+ console.log(` target settings: ${file}`);
114
+ if (removedCount === 0) {
115
+ console.log(" No Compaction hooks present — no change.");
116
+ return;
117
+ }
118
+ if (options.dryRun) {
119
+ console.log(chalk.yellow(` --dry-run: NOT written. Would remove ${removedCount} hook entr(y/ies). Resulting settings:`));
120
+ console.log(JSON.stringify(finalSettings, null, 2));
121
+ return;
122
+ }
123
+ await writeSettings(file, finalSettings);
124
+ console.log(chalk.green(` Removed ${removedCount} Compaction hook entr(y/ies) (Stop + before-call). Other settings preserved.`));
125
+ });
126
+ hooks
127
+ .command("status")
128
+ .description("Source-status view of the CONTENT-FREE usage records produced by `capture claude-code --from-hook`. " +
129
+ "Aggregates by tool over your local records (by source/session/time). Read-only, local-first, no network. " +
130
+ "Distinguishes provider-reported usage from unavailable; makes NO savings claim.")
131
+ .option("--records-dir <dir>", "Base directory holding hook records", ".compaction/hooks")
132
+ .option("--since <iso>", "Only include records recorded at or after this ISO timestamp (time window).")
133
+ .option("--json", "Print the raw content-free aggregate JSON instead of the table.")
134
+ .action(async (options) => {
135
+ const records = await loadHookUsageRecords(options.recordsDir ?? ".compaction/hooks");
136
+ const aggregate = aggregateHookUsageRecords(records, options.since ? { since: options.since } : {});
137
+ if (options.json) {
138
+ console.log(JSON.stringify(aggregate, null, 2));
139
+ return;
140
+ }
141
+ console.log(chalk.cyan("compaction hooks status"));
142
+ console.log(" Local, content-free source status from Claude Code Stop-hook records. No content; no savings claim.");
143
+ console.log(printHookUsageAggregate(aggregate));
144
+ });
145
+ }
146
+ const dash = chalk.gray("—");
147
+ function tok(value) {
148
+ return value === null ? chalk.gray("unavailable") : String(value);
149
+ }
150
+ /** Render the content-free source-status aggregate as a readable block (provider-reported vs unavailable). */
151
+ export function printHookUsageAggregate(aggregate) {
152
+ const lines = [];
153
+ lines.push(` records: ${aggregate.dedupedRecords} (deduped from ${aggregate.totalRecords})${aggregate.since ? ` · since ${aggregate.since}` : ""}`);
154
+ if (aggregate.tools.length === 0) {
155
+ lines.push(chalk.gray(" No hook records yet. Install the hook and let a Claude Code turn finish:"));
156
+ lines.push(chalk.gray(" compaction hooks install → (run Claude Code) → compaction hooks status"));
157
+ return lines.join("\n");
158
+ }
159
+ for (const t of aggregate.tools) {
160
+ // Headline never overstates: full provider-reported only when EVERY event is; mixed says "partial".
161
+ const status = t.providerReportedEvents === t.events && t.events > 0
162
+ ? chalk.green("live · provider-reported")
163
+ : t.providerReportedEvents > 0
164
+ ? chalk.yellow("live · provider-reported (partial)")
165
+ : t.unavailableEvents === t.events
166
+ ? chalk.gray("usage unavailable")
167
+ : chalk.yellow(t.tokenSources.join(" / ") || "recorded");
168
+ lines.push("");
169
+ lines.push(` ${chalk.bold(t.tool)} — ${status}`);
170
+ lines.push(` events: ${t.events} sessions: ${t.sessions}`);
171
+ lines.push(` input tokens: ${tok(t.inputTokens)}`);
172
+ lines.push(` output tokens: ${tok(t.outputTokens)}${t.outputRecorded < t.events ? chalk.gray(` (${t.events - t.outputRecorded} record(s) without output)`) : ""}`);
173
+ lines.push(` reasoning: ${chalk.gray("unavailable (Claude Code session usage does not report it separately)")}`);
174
+ lines.push(` token source: ${t.tokenSources.length ? t.tokenSources.join(" / ") : dash}`);
175
+ lines.push(` provider/model:${t.providers.length || t.models.length ? ` ${[t.providers.join(", "), t.models.join(", ")].filter(Boolean).join(" · ")}` : ` ${dash}`}`);
176
+ lines.push(` provider-reported / unavailable: ${t.providerReportedEvents} / ${t.unavailableEvents}`);
177
+ lines.push(` window: ${t.firstRecordedAt ?? dash} → ${t.lastRecordedAt ?? dash}`);
178
+ }
179
+ return lines.join("\n");
180
+ }
181
+ //# sourceMappingURL=hooks.js.map
@@ -5,8 +5,10 @@ function printSupportedSources() {
5
5
  console.log(chalk.cyan("compaction import sources"));
6
6
  for (const adapter of listTraceAdapters()) {
7
7
  console.log(`- ${adapter.id}: ${adapter.displayName}`);
8
+ console.log(` readiness: ${adapter.readiness}`);
8
9
  }
9
- console.log("Live provider/runtime integrations remain future work; codex-exec-jsonl reads only local export files.");
10
+ console.log("All sources are local file import (no live integration). Readiness above is an honest integration-readiness " +
11
+ "label (AGENTS.md Level 0–6) + validation status — not a stronger integration claim than the evidence supports.");
10
12
  }
11
13
  export function registerImportCommand(program) {
12
14
  program