@unerr-ai/unerr 0.1.6 → 0.1.7

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 (218) hide show
  1. package/README.md +70 -194
  2. package/dist/cli.js +39149 -36991
  3. package/package.json +9 -2
  4. package/dist/__tests__/architecture-guard.test.js +0 -122
  5. package/dist/__tests__/arg-validator.test.js +0 -205
  6. package/dist/__tests__/ast-extractor.test.js +0 -203
  7. package/dist/__tests__/auto-bootstrap.test.js +0 -280
  8. package/dist/__tests__/background-indexer.test.js +0 -228
  9. package/dist/__tests__/blast-radius-engine.test.js +0 -200
  10. package/dist/__tests__/bridge-isolation.test.js +0 -37
  11. package/dist/__tests__/budget-enforcer.test.js +0 -53
  12. package/dist/__tests__/cfg-test-detection-perf.test.js +0 -82
  13. package/dist/__tests__/change-narrative.test.js +0 -190
  14. package/dist/__tests__/check-commit.test.js +0 -258
  15. package/dist/__tests__/checksum.test.js +0 -34
  16. package/dist/__tests__/commit-watcher.test.js +0 -154
  17. package/dist/__tests__/community-detection.test.js +0 -179
  18. package/dist/__tests__/community-tools.test.js +0 -299
  19. package/dist/__tests__/components.test.js +0 -449
  20. package/dist/__tests__/compression-log.test.js +0 -174
  21. package/dist/__tests__/compression-quality-monitor.test.js +0 -40
  22. package/dist/__tests__/config-healer.test.js +0 -165
  23. package/dist/__tests__/context-ledger.test.js +0 -58
  24. package/dist/__tests__/convention-detector.test.js +0 -99
  25. package/dist/__tests__/convention-learner.test.js +0 -86
  26. package/dist/__tests__/correction-detector.test.js +0 -330
  27. package/dist/__tests__/daemon-autostart-install.test.js +0 -283
  28. package/dist/__tests__/daemon-bridge.test.js +0 -222
  29. package/dist/__tests__/daemon-dashboard.test.js +0 -202
  30. package/dist/__tests__/daemon-registry.test.js +0 -240
  31. package/dist/__tests__/daemon-supervisor.test.js +0 -318
  32. package/dist/__tests__/daemon-version-check.test.js +0 -275
  33. package/dist/__tests__/decision-point-detector.test.js +0 -98
  34. package/dist/__tests__/deep-link.test.js +0 -143
  35. package/dist/__tests__/disallowed-tools.test.js +0 -115
  36. package/dist/__tests__/drift-tracker.test.js +0 -582
  37. package/dist/__tests__/durability-scorer.test.js +0 -152
  38. package/dist/__tests__/efficiency-tracker.test.js +0 -65
  39. package/dist/__tests__/enrich.test.js +0 -144
  40. package/dist/__tests__/entity-rewind.test.js +0 -248
  41. package/dist/__tests__/ephemeral.test.js +0 -111
  42. package/dist/__tests__/exploration-cost.test.js +0 -93
  43. package/dist/__tests__/fact-generator.test.js +0 -197
  44. package/dist/__tests__/file-l0-graph.test.js +0 -244
  45. package/dist/__tests__/file-logger.test.js +0 -82
  46. package/dist/__tests__/file-outline.test.js +0 -141
  47. package/dist/__tests__/file-read-protocol.test.js +0 -188
  48. package/dist/__tests__/format-encoder.test.js +0 -233
  49. package/dist/__tests__/git-attribution.test.js +0 -259
  50. package/dist/__tests__/graph-temporal-joiner.test.js +0 -219
  51. package/dist/__tests__/health-grade-enhanced.test.js +0 -138
  52. package/dist/__tests__/health-map-data.test.js +0 -173
  53. package/dist/__tests__/helpers/mcp-harness.js +0 -45
  54. package/dist/__tests__/helpers/mcp-harness.test.js +0 -68
  55. package/dist/__tests__/hook-dedup.test.js +0 -112
  56. package/dist/__tests__/hook-runner.test.js +0 -253
  57. package/dist/__tests__/indexer-cfg.test.js +0 -185
  58. package/dist/__tests__/indexer-cross-file.test.js +0 -172
  59. package/dist/__tests__/indexer-extraction.test.js +0 -245
  60. package/dist/__tests__/indexer-incremental.test.js +0 -232
  61. package/dist/__tests__/indexer-language-expansion.test.js +0 -165
  62. package/dist/__tests__/init-push.test.js +0 -131
  63. package/dist/__tests__/instruction-writer.test.js +0 -179
  64. package/dist/__tests__/intelligence-integration.test.js +0 -217
  65. package/dist/__tests__/intent-correlator.test.js +0 -175
  66. package/dist/__tests__/intent-detector.test.js +0 -235
  67. package/dist/__tests__/intent-encoder.test.js +0 -167
  68. package/dist/__tests__/java-build-tool-detection.test.js +0 -174
  69. package/dist/__tests__/layer3-sprint-q.test.js +0 -160
  70. package/dist/__tests__/layer3-sprint-r.test.js +0 -91
  71. package/dist/__tests__/layer3-sprint-s.test.js +0 -183
  72. package/dist/__tests__/layer3-sprint-t.test.js +0 -201
  73. package/dist/__tests__/layer3-sprint-u.test.js +0 -174
  74. package/dist/__tests__/layer4-sprint-ba2.test.js +0 -354
  75. package/dist/__tests__/layer4-sprint-ba4.test.js +0 -84
  76. package/dist/__tests__/layer4-sprint-vs.test.js +0 -105
  77. package/dist/__tests__/ledger-chains.test.js +0 -162
  78. package/dist/__tests__/lifecycle-machine.test.js +0 -226
  79. package/dist/__tests__/local-chat-provider.test.js +0 -170
  80. package/dist/__tests__/local-convention-detector.test.js +0 -308
  81. package/dist/__tests__/local-embeddings.test.js +0 -422
  82. package/dist/__tests__/local-graph.test.js +0 -540
  83. package/dist/__tests__/local-indexer.test.js +0 -228
  84. package/dist/__tests__/local-intelligence-l3.test.js +0 -332
  85. package/dist/__tests__/local-llm.test.js +0 -253
  86. package/dist/__tests__/local-mode-offline.test.js +0 -187
  87. package/dist/__tests__/local-mode-stats.test.js +0 -273
  88. package/dist/__tests__/local-mode-tui.test.js +0 -343
  89. package/dist/__tests__/local-parse.test.js +0 -199
  90. package/dist/__tests__/log-tailer.test.js +0 -208
  91. package/dist/__tests__/loop-breaker.test.js +0 -276
  92. package/dist/__tests__/loop-miner.test.js +0 -226
  93. package/dist/__tests__/mcp-config.test.js +0 -126
  94. package/dist/__tests__/mcp-content-json.test.js +0 -10
  95. package/dist/__tests__/mcp-envelope.test.js +0 -124
  96. package/dist/__tests__/metrics-store.test.js +0 -223
  97. package/dist/__tests__/native-watcher.test.js +0 -191
  98. package/dist/__tests__/navigation-hooks-agent-aware.test.js +0 -145
  99. package/dist/__tests__/negative-knowledge.test.js +0 -116
  100. package/dist/__tests__/network-boundary.test.js +0 -190
  101. package/dist/__tests__/network-firewall.test.js +0 -112
  102. package/dist/__tests__/nudge-invariants.test.js +0 -160
  103. package/dist/__tests__/nudge-v2.test.js +0 -225
  104. package/dist/__tests__/offline-rewind.test.js +0 -251
  105. package/dist/__tests__/open-threads.test.js +0 -89
  106. package/dist/__tests__/output-compressor.test.js +0 -93
  107. package/dist/__tests__/pending-violations.test.js +0 -112
  108. package/dist/__tests__/persistence-effectiveness.test.js +0 -143
  109. package/dist/__tests__/provider-factory.test.js +0 -42
  110. package/dist/__tests__/providers.test.js +0 -24
  111. package/dist/__tests__/proxy.test.js +0 -314
  112. package/dist/__tests__/query-router.test.js +0 -1018
  113. package/dist/__tests__/reasoning-quality-route.test.js +0 -138
  114. package/dist/__tests__/redactor.test.js +0 -120
  115. package/dist/__tests__/resource-monitor.test.js +0 -57
  116. package/dist/__tests__/response-envelope.test.js +0 -100
  117. package/dist/__tests__/risk-classifier.test.js +0 -101
  118. package/dist/__tests__/risk-signal-scope.test.js +0 -75
  119. package/dist/__tests__/rule-evaluator.test.js +0 -280
  120. package/dist/__tests__/scip-decoder.test.js +0 -49
  121. package/dist/__tests__/scip-downloader.test.js +0 -201
  122. package/dist/__tests__/scip-merger.test.js +0 -103
  123. package/dist/__tests__/search-index.test.js +0 -422
  124. package/dist/__tests__/semantic-enrichment.test.js +0 -360
  125. package/dist/__tests__/session-brief-builder.test.js +0 -187
  126. package/dist/__tests__/session-context.test.js +0 -221
  127. package/dist/__tests__/session-continuity.test.js +0 -144
  128. package/dist/__tests__/session-dedup.test.js +0 -74
  129. package/dist/__tests__/session-event-wiring.test.js +0 -206
  130. package/dist/__tests__/session-events.test.js +0 -149
  131. package/dist/__tests__/session-legend.test.js +0 -20
  132. package/dist/__tests__/session-persistence.test.js +0 -131
  133. package/dist/__tests__/session-resume-block.test.js +0 -107
  134. package/dist/__tests__/session-resume.test.js +0 -97
  135. package/dist/__tests__/session-summary-writer.test.js +0 -134
  136. package/dist/__tests__/shadow-ledger.test.js +0 -203
  137. package/dist/__tests__/shell-classifier.test.js +0 -151
  138. package/dist/__tests__/shell-compression-floor.test.js +0 -189
  139. package/dist/__tests__/shell-compression-v2.test.js +0 -339
  140. package/dist/__tests__/shell-compressor.test.js +0 -35
  141. package/dist/__tests__/shell-hooks.test.js +0 -128
  142. package/dist/__tests__/shell-strategies.test.js +0 -644
  143. package/dist/__tests__/shell-tee.test.js +0 -133
  144. package/dist/__tests__/signal-dedup.test.js +0 -158
  145. package/dist/__tests__/signal-reinforcer.test.js +0 -77
  146. package/dist/__tests__/signal-scorer.test.js +0 -251
  147. package/dist/__tests__/signal-show-store.test.js +0 -108
  148. package/dist/__tests__/smart-truncate.test.js +0 -215
  149. package/dist/__tests__/snapshot-v2.test.js +0 -113
  150. package/dist/__tests__/sprint-l1-local-mode.test.js +0 -130
  151. package/dist/__tests__/sprint-l10-boot.test.js +0 -220
  152. package/dist/__tests__/sprint-l9-offline-commands.test.js +0 -189
  153. package/dist/__tests__/sprint-q-persistent-context.test.js +0 -198
  154. package/dist/__tests__/sprint-s1-wiring.test.js +0 -215
  155. package/dist/__tests__/sprint-s2-wiring.test.js +0 -256
  156. package/dist/__tests__/sprint-s3-wiring.test.js +0 -195
  157. package/dist/__tests__/sprint-s4-wiring.test.js +0 -213
  158. package/dist/__tests__/sprint-s6-hooks.test.js +0 -222
  159. package/dist/__tests__/sprint-s7-persistent.test.js +0 -263
  160. package/dist/__tests__/sprint-s8-value.test.js +0 -167
  161. package/dist/__tests__/sprint-s9-behavioral.test.js +0 -179
  162. package/dist/__tests__/sprint3-intelligence.test.js +0 -297
  163. package/dist/__tests__/sprint5-mcp-server.test.js +0 -136
  164. package/dist/__tests__/startup-display.test.js +0 -302
  165. package/dist/__tests__/startup-log-file.test.js +0 -97
  166. package/dist/__tests__/stash-manager.test.js +0 -229
  167. package/dist/__tests__/state-detector.test.js +0 -92
  168. package/dist/__tests__/status-dashboard.test.js +0 -142
  169. package/dist/__tests__/temporal-facts.test.js +0 -292
  170. package/dist/__tests__/temporal-routes.test.js +0 -142
  171. package/dist/__tests__/test-detector.test.js +0 -174
  172. package/dist/__tests__/theme.test.js +0 -72
  173. package/dist/__tests__/timeline-agents.test.js +0 -122
  174. package/dist/__tests__/timeline-bootstrap.test.js +0 -176
  175. package/dist/__tests__/timeline-filters.test.js +0 -193
  176. package/dist/__tests__/timeline-markers.test.js +0 -151
  177. package/dist/__tests__/timeline-routes.test.js +0 -156
  178. package/dist/__tests__/timeline-store.test.js +0 -171
  179. package/dist/__tests__/token-counter.test.js +0 -86
  180. package/dist/__tests__/token-estimator.test.js +0 -96
  181. package/dist/__tests__/token-flow-api.test.js +0 -239
  182. package/dist/__tests__/token-flow-instrumentation.test.js +0 -437
  183. package/dist/__tests__/token-flow-persistence.test.js +0 -356
  184. package/dist/__tests__/token-flow-routes.test.js +0 -199
  185. package/dist/__tests__/token-flow.test.js +0 -695
  186. package/dist/__tests__/tool-clusters.test.js +0 -177
  187. package/dist/__tests__/transport-mux.test.js +0 -283
  188. package/dist/__tests__/turn-segmenter.test.js +0 -166
  189. package/dist/__tests__/uninstall.test.js +0 -141
  190. package/dist/__tests__/warm-start-policy.test.js +0 -271
  191. package/dist/__tests__/wire-cap-nudge.test.js +0 -77
  192. package/dist/__tests__/worker-pool.test.js +0 -101
  193. package/dist/ui/assets/index-7gl3mIuY.css +0 -1
  194. package/dist/ui/assets/index-CX4FCWGT.js +0 -10
  195. package/dist/ui/assets/rolldown-runtime-S-ySWqyJ.js +0 -1
  196. package/dist/ui/assets/vis-network-NIJHUFI3.js +0 -908
  197. package/dist/ui/fonts/jetbrains-mono-latin-400-normal.woff +0 -0
  198. package/dist/ui/icon-wordmark.png +0 -0
  199. package/dist/ui/icon-wordmark.svg +0 -30
  200. package/dist/ui/icon.png +0 -0
  201. package/dist/ui/icon.svg +0 -25
  202. package/dist/ui/index.html +0 -15
  203. package/dist/ui/prototype-sandbox/index.html +0 -257
  204. package/dist/ui/screenshots/activity.png +0 -0
  205. package/dist/ui/screenshots/code-base-intelligence.png +0 -0
  206. package/dist/ui/screenshots/dashboard.png +0 -0
  207. package/dist/ui/screenshots/project-memory.png +0 -0
  208. package/dist/ui/screenshots/reasoning-quality.png +0 -0
  209. package/dist/ui/screenshots/reasoning-session.png +0 -0
  210. package/dist/ui/screenshots/token-session.png +0 -0
  211. package/dist/ui/screenshots/token-trace-main.png +0 -0
  212. package/dist/ui/screenshots/token-turn.png +0 -0
  213. package/dist/ui/unerr-wordmark.png +0 -0
  214. package/dist/ui/unerr-wordmark.svg +0 -9
  215. package/dist/ui/unerr.png +0 -0
  216. package/dist/ui/unerr.svg +0 -25
  217. package/dist/ui/web-app-manifest-192x192.png +0 -0
  218. package/dist/ui/web-app-manifest-512x512.png +0 -0
@@ -1,644 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { createCompressionQualityMonitor } from "../proxy/compression-quality-monitor.js";
3
- import { compressShellOutput } from "../proxy/shell-compressor.js";
4
- import { compressDiff } from "../proxy/shell-strategies/diff.js";
5
- import { compressErrorDiagnostic } from "../proxy/shell-strategies/error-diagnostic.js";
6
- import { compressKeyValue } from "../proxy/shell-strategies/key-value.js";
7
- import { compressLogText } from "../proxy/shell-strategies/log-text.js";
8
- import { compressOmni } from "../proxy/shell-strategies/omni.js";
9
- import { compressProgress } from "../proxy/shell-strategies/progress.js";
10
- import { compressStructured } from "../proxy/shell-strategies/structured.js";
11
- import { compressTabular } from "../proxy/shell-strategies/tabular.js";
12
- import { compressTestResults } from "../proxy/shell-strategies/test-results.js";
13
- import { compressTreePaths } from "../proxy/shell-strategies/tree-paths.js";
14
- describe("compressTabular", () => {
15
- it("converts ps-style columns to pipe grid", () => {
16
- const raw = "USER PID CMD\nroot 1 init\nroot 42 bash";
17
- const out = compressTabular(raw);
18
- expect(out).toContain("_shell_fmt:tabular");
19
- expect(out).toContain("root|1|init");
20
- });
21
- it("detects header row and uses real column names", () => {
22
- const raw = "USER PID CMD\nroot 1 init";
23
- const out = compressTabular(raw);
24
- expect(out).toContain("USER|PID|CMD");
25
- expect(out).not.toContain("c0|c1");
26
- });
27
- it("prunes columns for docker ps with profile", () => {
28
- const raw = [
29
- "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES",
30
- "abc123 nginx nginx 2d ago Up 2d 80/tcp web",
31
- ].join("\n");
32
- const out = compressTabular(raw, "docker ps");
33
- expect(out).toContain("NAMES");
34
- expect(out).toContain("STATUS");
35
- // CREATED and COMMAND should be pruned
36
- expect(out).not.toContain("CREATED");
37
- });
38
- it("falls back to c0/c1 for headerless output", () => {
39
- const raw = "foo bar\nbaz qux";
40
- const out = compressTabular(raw);
41
- expect(out).toContain("c0|c1");
42
- });
43
- it("safety valve triggers when compression is too aggressive", () => {
44
- // Create output where tabular parsing would strip most content
45
- const lines = [
46
- "HEADER1 HEADER2 HEADER3 HEADER4",
47
- ...Array.from({ length: 20 }, (_, i) => `value${i} data${i} info${i} extra-long-description-with-lots-of-detail-${i}-that-should-not-be-lost`),
48
- ].join("\n");
49
- const out = compressTabular(lines);
50
- // Should still contain _shell_fmt:tabular
51
- expect(out).toContain("_shell_fmt:tabular");
52
- // If safety valve triggered, all original content should be present
53
- // (gentle fallback just strips blank lines)
54
- expect(out.length).toBeGreaterThan(lines.length * 0.3);
55
- });
56
- it("ps aux preserves COMMAND column with spaces via splitWithLastRest", () => {
57
- const raw = [
58
- "USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND",
59
- "root 1 0.0 0.1 169236 13128 ? Ss Jan01 0:05 /sbin/init splash",
60
- "www-data 100 0.5 1.2 500000 25000 ? S 09:00 1:30 /usr/sbin/apache2 -k start",
61
- ].join("\n");
62
- const out = compressTabular(raw, "ps aux");
63
- expect(out).toContain("_shell_fmt:tabular");
64
- // COMMAND column should be preserved with spaces
65
- expect(out).toContain("/sbin/init splash");
66
- expect(out).toContain("/usr/sbin/apache2 -k start");
67
- });
68
- it("docker ps preserves NAMES column", () => {
69
- const raw = [
70
- "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES",
71
- 'abc123def456 nginx "nginx -g …" 2 days ago Up 2 days 80/tcp my-web-server',
72
- ].join("\n");
73
- const out = compressTabular(raw, "docker ps");
74
- expect(out).toContain("NAMES");
75
- expect(out).toContain("my-web-server");
76
- });
77
- it("lsof preserves NAME column with file paths", () => {
78
- const raw = [
79
- "COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME",
80
- "node 123 dev cwd DIR 1,5 4096 2 /home/dev/project",
81
- "node 123 dev txt REG 1,5 50000 100 /usr/bin/node",
82
- ].join("\n");
83
- const out = compressTabular(raw, "lsof");
84
- expect(out).toContain("NAME");
85
- expect(out).toContain("/home/dev/project");
86
- });
87
- it("systemctl list-units preserves DESCRIPTION column", () => {
88
- const raw = [
89
- "UNIT LOAD ACTIVE SUB DESCRIPTION",
90
- "docker.service loaded active running Docker Application Container Engine",
91
- "ssh.service loaded active running OpenBSD Secure Shell server",
92
- ].join("\n");
93
- const out = compressTabular(raw, "systemctl list-units");
94
- expect(out).toContain("DESCRIPTION");
95
- expect(out).toContain("Docker Application Container Engine");
96
- });
97
- });
98
- describe("compressLogText", () => {
99
- it("passes through short output without format header", () => {
100
- const out = compressLogText("line1\nline2\nline3");
101
- expect(out).not.toContain("_shell_fmt:");
102
- expect(out).toContain("line1");
103
- });
104
- it("deduplicates repeated log lines with ×N", () => {
105
- const lines = [
106
- ...Array.from({ length: 50 }, () => "Compiling module v1.0"),
107
- ...Array.from({ length: 40 }, (_, i) => `step ${i}`),
108
- ];
109
- const out = compressLogText(lines.join("\n"));
110
- expect(out).toContain("[×50]");
111
- expect(out).toContain("unique patterns");
112
- });
113
- it("preserves error lines verbatim", () => {
114
- const lines = [
115
- ...Array.from({ length: 90 }, () => "INFO: processing"),
116
- "ERROR: build failed",
117
- "FATAL: out of memory",
118
- ];
119
- const out = compressLogText(lines.join("\n"));
120
- expect(out).toContain("ERROR: build failed");
121
- expect(out).toContain("FATAL: out of memory");
122
- });
123
- it("normalizes timestamps for pattern matching", () => {
124
- const lines = Array.from({ length: 100 }, (_, i) => `2024-01-01T00:00:${String(i).padStart(2, "0")}Z request handled`);
125
- const out = compressLogText(lines.join("\n"));
126
- expect(out).toContain("[×100]");
127
- expect(out).toContain("unique patterns");
128
- });
129
- });
130
- describe("compressTestResults", () => {
131
- it("collapses many passing lines (generic fallback)", () => {
132
- const raw = `${Array.from({ length: 20 }, () => "PASS ok").join("\n")}\nFAIL boom`;
133
- const out = compressTestResults(raw);
134
- expect(out).toContain("collapsed");
135
- expect(out).toContain("FAIL boom");
136
- });
137
- it("vitest all-pass: single summary line", () => {
138
- const input = [
139
- " RUN v3.2.4 /project",
140
- " ✓ src/a.test.ts > test1 1ms",
141
- " ✓ src/a.test.ts > test2 2ms",
142
- " ✓ src/b.test.ts > test3 1ms",
143
- "",
144
- " Test Files 2 passed (2)",
145
- " Tests 3 passed (3)",
146
- " Duration 1.5s",
147
- ].join("\n");
148
- const out = compressTestResults(input, "vitest run");
149
- expect(out).toContain("3 passed");
150
- expect(out).not.toContain("✓");
151
- expect(out.split("\n").length).toBeLessThan(6);
152
- });
153
- it("vitest with failures: summary + failure blocks", () => {
154
- const input = [
155
- " ✓ src/a.test.ts > pass1 1ms",
156
- " ✓ src/a.test.ts > pass2 2ms",
157
- "⎯⎯⎯ Failed Tests 1 ⎯⎯⎯",
158
- " FAIL src/b.test.ts > broken test",
159
- "AssertionError: expected 1 to be 2",
160
- " ❯ src/b.test.ts:10:5",
161
- "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯",
162
- " Test Files 1 failed | 1 passed (2)",
163
- " Tests 1 failed | 2 passed (3)",
164
- " Duration 2.5s",
165
- ].join("\n");
166
- const out = compressTestResults(input, "vitest run");
167
- expect(out).toContain("1 failed");
168
- expect(out).toContain("2 passed");
169
- expect(out).toContain("FAIL");
170
- expect(out).toContain("AssertionError");
171
- expect(out).not.toContain("✓");
172
- });
173
- it("vitest strips [unerr] noise lines", () => {
174
- const input = [
175
- " ✓ test 1ms",
176
- "[unerr] ⚠ Prevented: Loop",
177
- " ✓ test2 2ms",
178
- " Test Files 1 passed (1)",
179
- " Tests 2 passed (2)",
180
- ].join("\n");
181
- const out = compressTestResults(input, "vitest run");
182
- expect(out).not.toContain("[unerr]");
183
- });
184
- it("vitest exitCode=0 emits minimal summary", () => {
185
- const input = [
186
- " ✓ src/a.test.ts > test1 1ms",
187
- " Test Files 1 passed (1)",
188
- " Tests 5 passed (5)",
189
- " Duration 1.0s",
190
- ].join("\n");
191
- const out = compressTestResults(input, "vitest run", 0);
192
- expect(out).toContain("5 passed");
193
- expect(out.split("\n").length).toBeLessThanOrEqual(2);
194
- });
195
- it("pytest: extracts summary and failures", () => {
196
- const input = [
197
- "============================= test session starts ==============================",
198
- "collected 10 items",
199
- "test_foo.py::test_ok PASSED",
200
- "test_foo.py::test_fail FAILED",
201
- "=================================== FAILURES ===================================",
202
- "_________________________________ test_fail __________________________________",
203
- " assert 1 == 2",
204
- "AssertionError",
205
- "========================= 9 passed, 1 failed in 2.5s ==========================",
206
- ].join("\n");
207
- const out = compressTestResults(input, "pytest");
208
- expect(out).toContain("9 passed");
209
- expect(out).toContain("1 failed");
210
- expect(out).toContain("test_fail");
211
- });
212
- it("cargo test: extracts summary", () => {
213
- const input = [
214
- "test parser::test_basic ... ok",
215
- "test parser::test_edge ... ok",
216
- "test parser::test_fail ... FAILED",
217
- "failures:",
218
- "---- parser::test_fail stdout ----",
219
- "thread panicked at 'assertion failed'",
220
- "test result: ok. 2 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.2s",
221
- ].join("\n");
222
- const out = compressTestResults(input, "cargo test");
223
- expect(out).toContain("2 passed");
224
- expect(out).toContain("1 failed");
225
- expect(out).toContain("test_fail");
226
- });
227
- it("go test: extracts summary", () => {
228
- const input = [
229
- "--- PASS: TestFoo (0.00s)",
230
- "--- PASS: TestBar (0.01s)",
231
- "--- FAIL: TestBaz (0.02s)",
232
- " expected X got Y",
233
- "FAIL\tgithub.com/foo/bar\t0.045s",
234
- ].join("\n");
235
- const out = compressTestResults(input, "go test ./...");
236
- expect(out).toContain("2 passed");
237
- expect(out).toContain("1 failed");
238
- expect(out).toContain("TestBaz");
239
- });
240
- });
241
- describe("compressProgress", () => {
242
- it("truncates long npm-style output", () => {
243
- const raw = Array.from({ length: 80 }, (_, i) => `step ${i}`).join("\n");
244
- const out = compressProgress(raw);
245
- expect(out).toContain("omitted");
246
- expect(out.split("\n").length).toBeLessThan(40);
247
- });
248
- it("extracts summary line when present", () => {
249
- const raw = [
250
- ...Array.from({ length: 50 }, (_, i) => `Downloading dep ${i}`),
251
- "added 42 packages in 3s",
252
- ].join("\n");
253
- const out = compressProgress(raw);
254
- expect(out).toContain("added 42 packages");
255
- // Should be very compact — just header + summary
256
- expect(out.split("\n").length).toBeLessThanOrEqual(3);
257
- });
258
- });
259
- describe("compressStructured (FE-F T2)", () => {
260
- it("minifies JSON objects", () => {
261
- const raw = '{\n "a": 1,\n "b": [2, 3]\n}';
262
- const out = compressStructured(raw);
263
- expect(out).toContain("_shell_fmt:structured_json");
264
- expect(out).toContain('"a":1');
265
- });
266
- it("tags unstructured blobs", () => {
267
- const out = compressStructured("hello world\n\n\nextra");
268
- expect(out).toContain("_shell_fmt:structured_text");
269
- });
270
- it("depth-limits large JSON", () => {
271
- // Create JSON large enough to exceed MEDIUM_JSON (8000 chars)
272
- const items = Array.from({ length: 200 }, (_, i) => ({
273
- id: i,
274
- name: `item-${i}`,
275
- data: { nested: true },
276
- }));
277
- const obj = { results: items, meta: { total: 200 } };
278
- const raw = JSON.stringify(obj, null, 2);
279
- expect(raw.length).toBeGreaterThan(8000);
280
- const out = compressStructured(raw);
281
- expect(out).toContain("_shell_fmt:structured_json");
282
- expect(out).toContain("more items");
283
- });
284
- it("prunes large string values with noise keys", () => {
285
- // Create JSON large enough to exceed SMALL_JSON (2000 chars) so pruning kicks in
286
- const obj = {
287
- name: "test",
288
- certificate: "x".repeat(2000),
289
- value: 42,
290
- extra: Array.from({ length: 10 }, (_, i) => `field-${i}`),
291
- };
292
- const raw = JSON.stringify(obj, null, 2);
293
- const out = compressStructured(raw);
294
- expect(out).toContain("pruned");
295
- expect(out).not.toContain("x".repeat(200));
296
- });
297
- });
298
- describe("compressDiff (FE-F T4)", () => {
299
- it("emits stats header and preserves body shape", () => {
300
- const raw = `diff --git a/a.ts b/a.ts
301
- @@ -1,2 +1,3 @@
302
- export function foo() {
303
- + line
304
- }
305
- `;
306
- const out = compressDiff(raw);
307
- expect(out).toContain("_shell_diff:");
308
- expect(out).toContain("files=");
309
- });
310
- it("annotates high-risk symbols when risk map provided", () => {
311
- const risks = new Map([
312
- ["login", { name: "login", risk_level: "high", fan_in: 12 }],
313
- ]);
314
- const raw = "@@ function login()";
315
- const out = compressDiff(raw, risks);
316
- expect(out).toContain("[HIGH-RISK:login");
317
- });
318
- });
319
- describe("compressTreePaths (FE-F T5)", () => {
320
- it("emits tree_paths header with rollup summary on large inputs", () => {
321
- // R7 rewrite: <40 lines passes through unchanged; ≥40 lines hits the rollup path
322
- const lines = [];
323
- for (let i = 0; i < 50; i++)
324
- lines.push(`src/foo/file_${i}.ts`);
325
- const out = compressTreePaths(lines.join("\n"));
326
- expect(out).toContain("_shell_fmt:tree_paths");
327
- expect(out).toContain("rolled up");
328
- expect(out).toContain("src/foo/");
329
- });
330
- it("collapses node_modules directory in large inputs", () => {
331
- const lines = ["src/index.ts", "src/utils.ts"];
332
- for (let i = 0; i < 50; i++)
333
- lines.push(`node_modules/lodash/${i}.js`);
334
- const out = compressTreePaths(lines.join("\n"));
335
- expect(out).toContain("node_modules/");
336
- expect(out).toContain("[collapsed]");
337
- });
338
- it("passes small inputs through unchanged", () => {
339
- const raw = "src/foo/a.ts\nsrc/foo/b.ts\nsrc/foo/c.ts";
340
- expect(compressTreePaths(raw)).toBe(raw);
341
- });
342
- });
343
- describe("compressKeyValue (FE-F T6)", () => {
344
- it("drops noisy keys and shortens PATH", () => {
345
- const raw = "PATH=/a:/b:/c:/d:/e\nHOME=/Users/x\nOLDPWD=/tmp\nFOO=bar";
346
- const out = compressKeyValue(raw);
347
- expect(out).toContain("_shell_fmt:key_value");
348
- expect(out).not.toContain("OLDPWD");
349
- expect(out).toContain("HOME=");
350
- });
351
- it("drops terminal and locale noise keys", () => {
352
- const raw = "TERM=xterm-256color\nLC_ALL=en_US.UTF-8\nGOPATH=/go\nFOO=bar";
353
- const out = compressKeyValue(raw);
354
- expect(out).not.toContain("TERM=");
355
- expect(out).not.toContain("LC_ALL=");
356
- expect(out).toContain("GOPATH=");
357
- expect(out).toContain("FOO=bar");
358
- });
359
- it("truncates long values", () => {
360
- const raw = `LONG_VAR=${"x".repeat(500)}`;
361
- const out = compressKeyValue(raw);
362
- expect(out).toContain("500 chars");
363
- expect(out).not.toContain("x".repeat(200));
364
- });
365
- // B21 regression: `git branch --show-current` (mapped to key_value by the
366
- // classifier on the `git branch` prefix) produces a single bare word.
367
- // No key=value or key:value rows means no compression possible — emitting
368
- // the 20-byte header on a 5-byte payload is strictly net-negative.
369
- it("passthrough on output with no parseable key:value rows (B21)", () => {
370
- const raw = "main\n";
371
- const out = compressKeyValue(raw);
372
- expect(out).toBe(raw);
373
- expect(out).not.toContain("_shell_fmt:");
374
- });
375
- it("passthrough on multi-line plain text with no key:value rows", () => {
376
- const raw = "one\ntwo\nthree\n";
377
- const out = compressKeyValue(raw);
378
- expect(out).toBe(raw);
379
- expect(out).not.toContain("_shell_fmt:");
380
- });
381
- it("still emits header when at least one key=value row exists", () => {
382
- const raw = "FOO=bar\nplain line";
383
- const out = compressKeyValue(raw);
384
- expect(out).toContain("_shell_fmt:key_value");
385
- expect(out).toContain("FOO=bar");
386
- });
387
- it("still emits header for colon-format with parseable rows", () => {
388
- const raw = "Name: my-pod\nNamespace: default\nStatus: Running";
389
- const out = compressKeyValue(raw);
390
- expect(out).toContain("_shell_fmt:key_value");
391
- expect(out).toContain("Name: my-pod");
392
- });
393
- });
394
- describe("compressErrorDiagnostic (FE-F T7)", () => {
395
- it("node_stack: deduplicates identical stack frames", () => {
396
- const raw = `Error: boom
397
- at x (f.ts:1)
398
- at x (f.ts:1)
399
- at x (f.ts:1)
400
- at y (g.ts:2)`;
401
- const out = compressErrorDiagnostic(raw);
402
- expect(out).toContain("_shell_fmt:error_diagnostic");
403
- expect(out).toContain("Error: boom");
404
- expect(out).toContain("at x (f.ts:1)");
405
- expect(out).toContain("at y (g.ts:2)");
406
- });
407
- it("tsc: groups errors by TS code", () => {
408
- const raw = [
409
- "src/a.ts(10,5): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.",
410
- "src/b.ts(20,3): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.",
411
- "src/c.ts(5,1): error TS2322: Type 'undefined' is not assignable to type 'string'.",
412
- "Found 3 errors in 3 files.",
413
- ].join("\n");
414
- const out = compressErrorDiagnostic(raw, "tsc --noEmit");
415
- expect(out).toContain("TS2345");
416
- expect(out).toContain("1 more in 2 file(s)");
417
- expect(out).toContain("Found 3 errors");
418
- });
419
- it("eslint: groups by rule", () => {
420
- const raw = [
421
- "/path/to/file.ts",
422
- " 10:5 error Unexpected console statement no-console",
423
- " 23:1 error Unexpected console statement no-console",
424
- "/path/to/other.ts",
425
- " 5:3 warning Missing return type @typescript-eslint/explicit-function-return-type",
426
- "",
427
- "✖ 3 problems (2 errors, 1 warning)",
428
- ].join("\n");
429
- const out = compressErrorDiagnostic(raw, "eslint .");
430
- expect(out).toContain("no-console");
431
- expect(out).toContain("2 occurrences");
432
- expect(out).toContain("✖ 3 problems");
433
- });
434
- it("node stack: filters node:internal frames", () => {
435
- const raw = [
436
- "Error: connection refused",
437
- " at connect (src/db.ts:10:5)",
438
- " at node:internal/process/task_queues:1:2",
439
- " at node:internal/process/task_queues:3:4",
440
- " at main (src/index.ts:5:3)",
441
- ].join("\n");
442
- const out = compressErrorDiagnostic(raw);
443
- expect(out).toContain("connection refused");
444
- expect(out).toContain("src/db.ts");
445
- expect(out).not.toContain("node:internal");
446
- });
447
- });
448
- describe("compressShellOutput integration", () => {
449
- it("compresses docker ps output", async () => {
450
- const txt = "CONTAINER ID IMAGE COMMAND\nabc123 nginx nginx -g";
451
- const r = await compressShellOutput("docker ps", txt, {
452
- persistStats: false,
453
- });
454
- expect(r.text).toContain("_shell_fmt");
455
- expect(r.classification.category).toBe("tabular");
456
- });
457
- it("applies omni compression for low-confidence output", async () => {
458
- // Small input passes through bare (no header) — verify only that the omni
459
- // path was chosen via classification metadata, not via the absent format tag.
460
- const r = await compressShellOutput("unknown", "x", {
461
- persistStats: false,
462
- });
463
- expect(r.classification.confidence).toBeLessThan(0.7);
464
- expect(r.text).toContain("x");
465
- expect(r.text).not.toContain("_shell_fmt:");
466
- });
467
- it("records shell categories on quality monitor when provided", async () => {
468
- const mon = createCompressionQualityMonitor();
469
- await compressShellOutput("docker ps", "CONTAINER ID IMAGE\nabc123 nginx", { persistStats: false, qualityMonitor: mon });
470
- expect(mon.getRetention("shell_tabular")).toBeGreaterThanOrEqual(0.4);
471
- });
472
- });
473
- describe("compressOmni", () => {
474
- it("passes small output through without format header", () => {
475
- const out = compressOmni("hello\nworld");
476
- expect(out).not.toContain("_shell_fmt:");
477
- expect(out).toContain("hello");
478
- expect(out).toContain("world");
479
- });
480
- it("Tier 1: collapses 3+ blank lines to 1", () => {
481
- // Need enough lines to exceed SMALL_THRESHOLD (40)
482
- const filler = Array.from({ length: 45 }, (_, i) => `line_${i}_unique`);
483
- filler.splice(20, 0, "", "", "", "", "");
484
- const out = compressOmni(filler.join("\n"));
485
- expect(out).not.toContain("\n\n\n");
486
- expect(out).toContain("line_0_unique");
487
- });
488
- it("Tier 2: deduplicates 3+ consecutive identical lines", () => {
489
- const lines = Array.from({ length: 100 }, () => "Compiling crate v1.0");
490
- const out = compressOmni(lines.join("\n"));
491
- expect(out).toContain("[×100]");
492
- expect(out.split("\n").length).toBeLessThan(10);
493
- });
494
- it("Tier 2: keeps runs shorter than 3 intact", () => {
495
- const lines = ["alpha", "alpha", "bravo", "bravo", "charlie"];
496
- // Filler lines must be unique even after pattern normalization
497
- const filler = Array.from({ length: 80 }, (_, i) => `unique_${String.fromCharCode(65 + (i % 26))}${String.fromCharCode(97 + Math.floor(i / 26))}_line`);
498
- const out = compressOmni([...filler, ...lines].join("\n"));
499
- // Consecutive dedup threshold is 3, so runs of 2 should stay
500
- expect(out).toContain("alpha");
501
- expect(out).toContain("bravo");
502
- });
503
- it("Tier 2: skips output under 80 lines", () => {
504
- const lines = Array.from({ length: 30 }, () => "same");
505
- expect(compressOmni(lines.join("\n"))).not.toContain("[×");
506
- });
507
- it("Tier 3: truncates >200 lines with head + tail", () => {
508
- // Each line must be unique even after normalization to avoid pattern-dedup
509
- const lines = Array.from({ length: 500 }, (_, i) => `processing file_${String.fromCharCode(65 + (i % 26))}_module_${String.fromCharCode(97 + (i % 26))}`);
510
- const out = compressOmni(lines.join("\n"));
511
- // Pattern-dedup or truncation should reduce the output
512
- expect(out.split("\n").length).toBeLessThan(400);
513
- });
514
- it("Tier 3: preserves diagnostic lines from middle", () => {
515
- // Use truly unique lines to prevent pattern-dedup from collapsing
516
- const lines = Array.from({ length: 500 }, (_, i) => {
517
- if (i === 200)
518
- return "ERROR: build failed";
519
- if (i === 300)
520
- return "FATAL: out of memory";
521
- return `processing_unique_module_${String.fromCharCode(65 + (i % 26))}${String.fromCharCode(97 + ((i * 7) % 26))}_v${i}`;
522
- });
523
- const out = compressOmni(lines.join("\n"));
524
- expect(out).toContain("ERROR: build failed");
525
- expect(out).toContain("FATAL: out of memory");
526
- });
527
- it("character cap: limits very wide output", () => {
528
- const lines = Array.from({ length: 100 }, () => "x".repeat(10_000));
529
- const out = compressOmni(lines.join("\n"));
530
- expect(out.length).toBeLessThan(60_000);
531
- });
532
- it("shows line savings in header for large compression", () => {
533
- const lines = Array.from({ length: 200 }, () => "Downloading...");
534
- const out = compressOmni(lines.join("\n"));
535
- expect(out).toMatch(/_shell_fmt:omni \(\d+→\d+ lines\)/);
536
- });
537
- // ── Hardening: edge cases that must never crash ──
538
- it("handles empty string", () => {
539
- const out = compressOmni("");
540
- expect(out).toBeDefined();
541
- expect(out).not.toContain("_shell_fmt:");
542
- });
543
- it("handles single line", () => {
544
- const out = compressOmni("just one line");
545
- expect(out).toContain("just one line");
546
- expect(out).not.toContain("_shell_fmt:");
547
- });
548
- it("handles only blank lines", () => {
549
- const out = compressOmni("\n\n\n\n\n\n\n\n\n\n");
550
- expect(out).not.toContain("_shell_fmt:");
551
- });
552
- it("handles only whitespace lines", () => {
553
- const lines = Array.from({ length: 50 }, () => " \t ");
554
- const out = compressOmni(lines.join("\n"));
555
- expect(out).toContain("_shell_fmt:omni");
556
- });
557
- it("handles Windows CRLF line endings", () => {
558
- const lines = Array.from({ length: 50 }, (_, i) => `line ${i}`);
559
- const out = compressOmni(lines.join("\r\n"));
560
- expect(out).toContain("_shell_fmt:omni");
561
- expect(out).not.toContain("\r");
562
- });
563
- it("handles mixed CRLF and LF endings", () => {
564
- const lines = ["line1\r\n", "line2\n", "line3\r\n", "line4\n"];
565
- const out = compressOmni(lines.join("").repeat(15));
566
- expect(out).toContain("_shell_fmt:omni");
567
- });
568
- it("handles binary-like content with null bytes gracefully", () => {
569
- const lines = Array.from({ length: 50 }, (_, i) => `data\x00\x01\x02 row ${i}`);
570
- const out = compressOmni(lines.join("\n"));
571
- expect(out).toContain("_shell_fmt:omni");
572
- });
573
- it("handles very long single line (minified JS)", () => {
574
- const line = `var a=${"x".repeat(100_000)};`;
575
- const out = compressOmni(line);
576
- // Single line is under SMALL_THRESHOLD so no header is added, but capChars
577
- // still trims wide input; the omission marker confirms truncation happened.
578
- expect(out).toContain("chars omitted");
579
- expect(out.length).toBeLessThan(60_000);
580
- });
581
- it("handles lines with only ANSI escape codes", () => {
582
- const lines = Array.from({ length: 50 }, () => "\x1b[31m\x1b[0m");
583
- const out = compressOmni(lines.join("\n"));
584
- expect(out).toContain("_shell_fmt:omni");
585
- });
586
- it("handles mixed diagnostic and normal lines at boundaries", () => {
587
- // First and last lines are diagnostic
588
- const lines = [
589
- "ERROR: start",
590
- ...Array.from({ length: 248 }, (_, i) => `normal line ${i}`),
591
- "FATAL: end",
592
- ];
593
- const out = compressOmni(lines.join("\n"));
594
- expect(out).toContain("ERROR: start");
595
- expect(out).toContain("FATAL: end");
596
- });
597
- it("handles all-diagnostic lines", () => {
598
- const lines = Array.from({ length: 60 }, (_, i) => `ERROR: failure ${i}`);
599
- const out = compressOmni(lines.join("\n"));
600
- expect(out).toContain("_shell_fmt:omni");
601
- expect(out).toContain("ERROR: failure");
602
- });
603
- it("pattern dedup handles UUIDs, timestamps, IPs", () => {
604
- const lines = Array.from({ length: 60 }, (_, i) => `2024-01-15T10:00:${String(i).padStart(2, "0")}Z [192.168.1.${i}] Processing batch abc${String(i).padStart(4, "0")}def`);
605
- const out = compressOmni(lines.join("\n"));
606
- expect(out).toContain("_shell_fmt:omni");
607
- // Pattern dedup should collapse these
608
- expect(out.split("\n").length).toBeLessThan(40);
609
- });
610
- it("handles Unicode content (CJK, emoji)", () => {
611
- const lines = Array.from({ length: 50 }, (_, i) => `处理文件 ${i}: 成功 ✓`);
612
- const out = compressOmni(lines.join("\n"));
613
- expect(out).toContain("_shell_fmt:omni");
614
- expect(out).toContain("处理文件");
615
- });
616
- it("handles tab-separated output (TSV-like)", () => {
617
- const lines = Array.from({ length: 50 }, (_, i) => `col1_${i}\tcol2_${i}\tcol3_${i}`);
618
- const out = compressOmni(lines.join("\n"));
619
- expect(out).toContain("_shell_fmt:omni");
620
- });
621
- it("handles cargo build output with repeating compile lines", () => {
622
- const lines = [
623
- ...Array.from({ length: 80 }, (_, i) => ` Compiling dep-${i} v0.${i}.0`),
624
- " Compiling my-project v1.0.0",
625
- " Finished release [optimized] target(s) in 45.2s",
626
- ];
627
- const out = compressOmni(lines.join("\n"));
628
- expect(out).toContain("_shell_fmt:omni");
629
- expect(out.split("\n").length).toBeLessThan(50);
630
- });
631
- it("handles webpack/vite build output", () => {
632
- const lines = [
633
- "vite v5.0.0 building for production...",
634
- ...Array.from({ length: 100 }, (_, i) => `transforming (${i + 1}) src/components/Component${i}.tsx`),
635
- "✓ 100 modules transformed.",
636
- "dist/assets/index-abc123.js 125.4 kB │ gzip: 42.1 kB",
637
- "✓ built in 3.21s",
638
- ];
639
- const out = compressOmni(lines.join("\n"));
640
- expect(out).toContain("_shell_fmt:omni");
641
- // Pattern dedup should compress the repeating transform lines
642
- expect(out.split("\n").length).toBeLessThan(lines.length);
643
- });
644
- });