greprag 5.40.1 → 5.42.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 (80) hide show
  1. package/dist/archive/glyph-font.d.ts +22 -0
  2. package/dist/archive/glyph-font.js +72 -0
  3. package/dist/archive/glyph-font.js.map +1 -0
  4. package/dist/archive/page-renderer.d.ts +67 -0
  5. package/dist/archive/page-renderer.js +197 -0
  6. package/dist/archive/page-renderer.js.map +1 -0
  7. package/dist/archive/png-codec.d.ts +21 -0
  8. package/dist/archive/png-codec.js +197 -0
  9. package/dist/archive/png-codec.js.map +1 -0
  10. package/dist/archive/transcript-extract.d.ts +39 -0
  11. package/dist/archive/transcript-extract.js +187 -0
  12. package/dist/archive/transcript-extract.js.map +1 -0
  13. package/dist/ccr-store.d.ts +49 -0
  14. package/dist/ccr-store.js +171 -0
  15. package/dist/ccr-store.js.map +1 -0
  16. package/dist/commands/archive.d.ts +81 -0
  17. package/dist/commands/archive.js +358 -0
  18. package/dist/commands/archive.js.map +1 -0
  19. package/dist/commands/context-governor.d.ts +82 -0
  20. package/dist/commands/context-governor.js +344 -0
  21. package/dist/commands/context-governor.js.map +1 -0
  22. package/dist/commands/crush-stats.d.ts +36 -0
  23. package/dist/commands/crush-stats.js +142 -0
  24. package/dist/commands/crush-stats.js.map +1 -0
  25. package/dist/commands/crush.d.ts +24 -0
  26. package/dist/commands/crush.js +183 -0
  27. package/dist/commands/crush.js.map +1 -0
  28. package/dist/commands/email.js +158 -9
  29. package/dist/commands/email.js.map +1 -1
  30. package/dist/commands/inbox-watch-supervisor.js +8 -65
  31. package/dist/commands/inbox-watch-supervisor.js.map +1 -1
  32. package/dist/commands/init.js +93 -0
  33. package/dist/commands/init.js.map +1 -1
  34. package/dist/commands/pipe-wrap.d.ts +75 -0
  35. package/dist/commands/pipe-wrap.js +255 -0
  36. package/dist/commands/pipe-wrap.js.map +1 -0
  37. package/dist/commands/watcher-registry.d.ts +1 -8
  38. package/dist/commands/watcher-registry.js +10 -47
  39. package/dist/commands/watcher-registry.js.map +1 -1
  40. package/dist/crush/adaptive-sizer.d.ts +40 -0
  41. package/dist/crush/adaptive-sizer.js +156 -0
  42. package/dist/crush/adaptive-sizer.js.map +1 -0
  43. package/dist/crush/crush-keywords.d.ts +40 -0
  44. package/dist/crush/crush-keywords.js +110 -0
  45. package/dist/crush/crush-keywords.js.map +1 -0
  46. package/dist/crush/crush-types.d.ts +38 -0
  47. package/dist/crush/crush-types.js +36 -0
  48. package/dist/crush/crush-types.js.map +1 -0
  49. package/dist/crush/index.d.ts +16 -0
  50. package/dist/crush/index.js +48 -0
  51. package/dist/crush/index.js.map +1 -0
  52. package/dist/crush/json-crusher.d.ts +38 -0
  53. package/dist/crush/json-crusher.js +250 -0
  54. package/dist/crush/json-crusher.js.map +1 -0
  55. package/dist/crush/json-detectors.d.ts +48 -0
  56. package/dist/crush/json-detectors.js +211 -0
  57. package/dist/crush/json-detectors.js.map +1 -0
  58. package/dist/crush/log-compressor.d.ts +60 -0
  59. package/dist/crush/log-compressor.js +394 -0
  60. package/dist/crush/log-compressor.js.map +1 -0
  61. package/dist/crush/search-compressor.d.ts +60 -0
  62. package/dist/crush/search-compressor.js +210 -0
  63. package/dist/crush/search-compressor.js.map +1 -0
  64. package/dist/email-pull.d.ts +25 -3
  65. package/dist/email-pull.js +25 -5
  66. package/dist/email-pull.js.map +1 -1
  67. package/dist/email-send.d.ts +4 -0
  68. package/dist/email-send.js +2 -0
  69. package/dist/email-send.js.map +1 -1
  70. package/dist/hook.js +49 -1
  71. package/dist/hook.js.map +1 -1
  72. package/dist/index.js +487 -449
  73. package/dist/index.js.map +1 -1
  74. package/dist/session-id.js +7 -12
  75. package/dist/session-id.js.map +1 -1
  76. package/package.json +2 -2
  77. package/skill/greprag/SKILL.md +2 -2
  78. package/skill/greprag/docs/email.md +28 -4
  79. package/skill/mechanic/SKILL.md +1 -1
  80. package/skill/templates/chip-spawn.md +4 -2
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ /**
3
+ * MIRRORED FILE — canonical source: packages/core/src/crush/json-crusher.ts.
4
+ * Byte-identical copy lives at packages/cli/src/crush/json-crusher.ts (the CLI is a
5
+ * zero-dep artifact and cannot import @greprag/core). KEEP IN SYNC —
6
+ * the sync guard in tests/test-crush.cjs fails the build on drift.
7
+ *
8
+ * JSON-array crusher — TypeScript port of the SIMPLE core of Headroom's
9
+ * smart_crusher: statistical sampling + error preservation. Classifies
10
+ * each array, keeps errors / structural outliers / rare statuses /
11
+ * first+last blocks, and replaces the dropped bulk with a count marker
12
+ * string (`"[... N items omitted ...]"`).
13
+ *
14
+ * The full Headroom orchestration (TOIN semantics, relevance planning,
15
+ * lossless compaction, change points) is intentionally not ported.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.DEFAULT_JSON_CONFIG = void 0;
19
+ exports.computeKSplit = computeKSplit;
20
+ exports.crushJson = crushJson;
21
+ const adaptive_sizer_1 = require("./adaptive-sizer");
22
+ const crush_types_1 = require("./crush-types");
23
+ const json_detectors_1 = require("./json-detectors");
24
+ exports.DEFAULT_JSON_CONFIG = {
25
+ minItemsToAnalyze: 10,
26
+ maxItemsAfterCrush: 15,
27
+ firstFraction: 0.3,
28
+ lastFraction: 0.15,
29
+ bias: 1.0,
30
+ };
31
+ /** First/last slot split for a keep-budget. Never exceeds kTotal. */
32
+ function computeKSplit(kTotal, firstFraction, lastFraction) {
33
+ if (kTotal <= 1)
34
+ return { first: kTotal, last: 0 };
35
+ const first = Math.max(1, Math.round(kTotal * firstFraction));
36
+ const last = Math.max(1, Math.round(kTotal * lastFraction));
37
+ if (first + last > kTotal)
38
+ return { first: Math.max(1, kTotal - 1), last: kTotal > 1 ? 1 : 0 };
39
+ return { first, last };
40
+ }
41
+ /** Find a common-ish id field whose values run sequentially, for marker enrichment. */
42
+ function sequentialIdField(items) {
43
+ const first = items[0];
44
+ if (typeof first !== 'object' || first === null || Array.isArray(first))
45
+ return null;
46
+ for (const key of Object.keys(first)) {
47
+ if (!/^(id|.*_id|index|seq|n)$/i.test(key))
48
+ continue;
49
+ const values = items.map((it) => typeof it === 'object' && it !== null && !Array.isArray(it) ? it[key] ?? null : null);
50
+ if ((0, json_detectors_1.detectSequentialPattern)(values))
51
+ return key;
52
+ }
53
+ return null;
54
+ }
55
+ function omittedMarker(droppedCount, items, droppedIndices) {
56
+ const idField = sequentialIdField(items);
57
+ if (idField && droppedIndices.length > 0) {
58
+ const firstDropped = items[droppedIndices[0]];
59
+ const lastDropped = items[droppedIndices[droppedIndices.length - 1]];
60
+ const idOf = (v) => typeof v === 'object' && v !== null && !Array.isArray(v) ? v[idField] ?? null : null;
61
+ const lo = idOf(firstDropped);
62
+ const hi = idOf(lastDropped);
63
+ if (lo !== null && hi !== null) {
64
+ return `[... ${droppedCount} items omitted (${idField} ${lo}..${hi}) ...]`;
65
+ }
66
+ }
67
+ return `[... ${droppedCount} items omitted ...]`;
68
+ }
69
+ /** Crush one array of objects: errors + outliers always kept; first/last + fill to budget. */
70
+ function crushDictArray(items, config) {
71
+ const n = items.length;
72
+ const serialized = items.map((it) => JSON.stringify(it));
73
+ const budget = (0, adaptive_sizer_1.computeOptimalK)(serialized, config.bias, 5, config.maxItemsAfterCrush);
74
+ const keep = new Set();
75
+ // Errors are never dropped (preservation guarantee).
76
+ for (const idx of (0, json_detectors_1.detectErrorItems)(items))
77
+ keep.add(idx);
78
+ // Structural outliers (rare fields + rare statuses).
79
+ for (const idx of (0, json_detectors_1.detectStructuralOutliers)(items))
80
+ keep.add(idx);
81
+ // First/last blocks from the remaining budget.
82
+ const remaining = Math.max(0, budget - keep.size);
83
+ if (remaining > 0) {
84
+ const { first, last } = computeKSplit(remaining, config.firstFraction, config.lastFraction);
85
+ for (let i = 0; i < Math.min(first, n); i++)
86
+ keep.add(i);
87
+ for (let i = Math.max(0, n - last); i < n; i++)
88
+ keep.add(i);
89
+ // Fill any leftover slots with an even sample of the middle.
90
+ let leftover = budget - keep.size;
91
+ if (leftover > 0) {
92
+ const step = Math.max(1, Math.floor(n / (leftover + 1)));
93
+ for (let i = step; i < n && leftover > 0; i += step) {
94
+ if (!keep.has(i)) {
95
+ keep.add(i);
96
+ leftover--;
97
+ }
98
+ }
99
+ }
100
+ }
101
+ if (keep.size >= n) {
102
+ return { items: [...items], originalCount: n, keptCount: n, crushed: false };
103
+ }
104
+ const keptIndices = [...keep].sort((a, b) => a - b);
105
+ const droppedIndices = [];
106
+ for (let i = 0; i < n; i++) {
107
+ if (!keep.has(i))
108
+ droppedIndices.push(i);
109
+ }
110
+ // Survivors in original order, with one marker at the first gap.
111
+ const out = [];
112
+ let markerPlaced = false;
113
+ for (let i = 0; i < n; i++) {
114
+ if (keep.has(i)) {
115
+ out.push(items[i]);
116
+ }
117
+ else if (!markerPlaced) {
118
+ out.push(omittedMarker(droppedIndices.length, items, droppedIndices));
119
+ markerPlaced = true;
120
+ }
121
+ }
122
+ return { items: out, originalCount: n, keptCount: keptIndices.length, crushed: true };
123
+ }
124
+ /** Crush a scalar (string/number) array: keep first/last + statistical outliers. */
125
+ function crushScalarArray(items, config) {
126
+ const n = items.length;
127
+ const serialized = items.map((it) => JSON.stringify(it));
128
+ const budget = (0, adaptive_sizer_1.computeOptimalK)(serialized, config.bias, 5, config.maxItemsAfterCrush);
129
+ if (budget >= n) {
130
+ return { items: [...items], originalCount: n, keptCount: n, crushed: false };
131
+ }
132
+ const keep = new Set();
133
+ // Numeric outliers: beyond 2 standard deviations from the mean.
134
+ const numbers = items.every((it) => typeof it === 'number') ? items : null;
135
+ if (numbers) {
136
+ const mean = numbers.reduce((s, v) => s + v, 0) / n;
137
+ const variance = numbers.reduce((s, v) => s + (v - mean) ** 2, 0) / n;
138
+ const std = Math.sqrt(variance);
139
+ if (std > 0) {
140
+ for (let i = 0; i < n; i++) {
141
+ if (Math.abs(numbers[i] - mean) > 2 * std)
142
+ keep.add(i);
143
+ }
144
+ }
145
+ }
146
+ // String error items.
147
+ if (!numbers) {
148
+ for (let i = 0; i < n; i++) {
149
+ const it = items[i];
150
+ if (typeof it === 'string') {
151
+ const lower = it.toLowerCase();
152
+ if (/(error|exception|failed|failure|fatal|panic|timeout)/.test(lower))
153
+ keep.add(i);
154
+ }
155
+ }
156
+ }
157
+ const remaining = Math.max(0, budget - keep.size);
158
+ if (remaining > 0) {
159
+ const { first, last } = computeKSplit(remaining, config.firstFraction, config.lastFraction);
160
+ for (let i = 0; i < Math.min(first, n); i++)
161
+ keep.add(i);
162
+ for (let i = Math.max(0, n - last); i < n; i++)
163
+ keep.add(i);
164
+ }
165
+ if (keep.size >= n) {
166
+ return { items: [...items], originalCount: n, keptCount: n, crushed: false };
167
+ }
168
+ const out = [];
169
+ let dropped = 0;
170
+ let markerAt = -1;
171
+ for (let i = 0; i < n; i++) {
172
+ if (keep.has(i)) {
173
+ out.push(items[i]);
174
+ }
175
+ else {
176
+ dropped++;
177
+ if (markerAt === -1) {
178
+ markerAt = out.length;
179
+ out.push(''); // placeholder, replaced below
180
+ }
181
+ }
182
+ }
183
+ if (markerAt >= 0)
184
+ out[markerAt] = `[... ${dropped} items omitted ...]`;
185
+ return { items: out, originalCount: n, keptCount: n - dropped, crushed: true };
186
+ }
187
+ /** Recursively walk a JSON value, crushing qualifying arrays in place. */
188
+ function walkAndCrush(value, config, totals) {
189
+ if (Array.isArray(value)) {
190
+ const recursed = value.map((v) => walkAndCrush(v, config, totals));
191
+ if (recursed.length < config.minItemsToAnalyze)
192
+ return recursed;
193
+ const kind = (0, json_detectors_1.classifyArray)(recursed);
194
+ let outcome = null;
195
+ if (kind === 'dict_array')
196
+ outcome = crushDictArray(recursed, config);
197
+ else if (kind === 'string_array' || kind === 'number_array')
198
+ outcome = crushScalarArray(recursed, config);
199
+ if (outcome && outcome.crushed) {
200
+ totals.original += outcome.originalCount;
201
+ totals.kept += outcome.keptCount;
202
+ totals.crushedArrays++;
203
+ return outcome.items;
204
+ }
205
+ totals.original += recursed.length;
206
+ totals.kept += recursed.length;
207
+ return recursed;
208
+ }
209
+ if (typeof value === 'object' && value !== null) {
210
+ const out = {};
211
+ for (const [k, v] of Object.entries(value)) {
212
+ out[k] = walkAndCrush(v, config, totals);
213
+ }
214
+ return out;
215
+ }
216
+ return value;
217
+ }
218
+ /**
219
+ * Compress JSON containing large arrays. Non-JSON input, or JSON with
220
+ * no qualifying arrays, passes through unchanged.
221
+ */
222
+ function crushJson(content, config = {}) {
223
+ const cfg = { ...exports.DEFAULT_JSON_CONFIG, ...config };
224
+ let parsed;
225
+ try {
226
+ parsed = JSON.parse(content);
227
+ }
228
+ catch {
229
+ return (0, crush_types_1.makePassthrough)(content, 'json', 'not valid JSON');
230
+ }
231
+ const totals = { original: 0, kept: 0, crushedArrays: 0 };
232
+ const crushedValue = walkAndCrush(parsed, cfg, totals);
233
+ if (totals.crushedArrays === 0) {
234
+ return (0, crush_types_1.makePassthrough)(content, 'json', 'no qualifying arrays', totals.original);
235
+ }
236
+ const output = JSON.stringify(crushedValue);
237
+ return {
238
+ output,
239
+ stats: {
240
+ sourceType: 'json',
241
+ originalChars: content.length,
242
+ compressedChars: output.length,
243
+ compressionRatio: output.length / Math.max(content.length, 1),
244
+ originalUnits: totals.original,
245
+ keptUnits: totals.kept,
246
+ passthrough: false,
247
+ },
248
+ };
249
+ }
250
+ //# sourceMappingURL=json-crusher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-crusher.js","sourceRoot":"","sources":["../../src/crush/json-crusher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * MIRRORED FILE — canonical source: packages/core/src/crush/json-detectors.ts.
3
+ * Byte-identical copy lives at packages/cli/src/crush/json-detectors.ts (the CLI is a
4
+ * zero-dep artifact and cannot import @greprag/core). KEEP IN SYNC —
5
+ * the sync guard in tests/test-crush.cjs fails the build on drift.
6
+ *
7
+ * Array classification + outlier/sequence detectors for the JSON
8
+ * crusher — port of the simple core of Headroom's smart_crusher
9
+ * (classifier.rs, outliers.rs, statistics.rs).
10
+ *
11
+ * Carries Headroom's documented fixes:
12
+ * - Zero-padded string IDs ("001", "002", ...) are NOT sequential
13
+ * numerics — if every parseable value originated as a string, the
14
+ * field is categorical and the padding matters.
15
+ * - Rare-status detection must not short-circuit at >10 distinct
16
+ * values: cardinality cap raised to 50 with a Pareto check (smallest
17
+ * K values covering >=80%; rare iff K <= 5), so datasets with dozens
18
+ * of error codes keep their rare-error preservation.
19
+ */
20
+ export type JsonValue = null | boolean | number | string | JsonValue[] | {
21
+ [key: string]: JsonValue;
22
+ };
23
+ export type ArrayType = 'dict_array' | 'string_array' | 'number_array' | 'bool_array' | 'nested_array' | 'mixed_array' | 'empty';
24
+ /** Classify a JSON array by element types (walks every element). */
25
+ export declare function classifyArray(items: readonly JsonValue[]): ArrayType;
26
+ /**
27
+ * Detect whether values form a sequential numeric pattern (IDs: 1,2,3…).
28
+ *
29
+ * Zero-padding fix: string values that parse as integers are counted,
30
+ * but if EVERY numeric value originated as a string the field is
31
+ * refused as sequential (padded codes are categorical).
32
+ */
33
+ export declare function detectSequentialPattern(values: readonly JsonValue[]): boolean;
34
+ /**
35
+ * Detect items with rare values in status-like categorical fields
36
+ * (Pareto check, cardinality 2..=50). Returns item indices.
37
+ */
38
+ export declare function detectRareStatusValues(items: readonly JsonValue[], commonFields: ReadonlySet<string>): number[];
39
+ /**
40
+ * Detect structural outliers: items carrying a rare field (<20% of the
41
+ * array) plus rare-status items. Returns sorted, deduped indices.
42
+ */
43
+ export declare function detectStructuralOutliers(items: readonly JsonValue[]): number[];
44
+ /**
45
+ * Detect items containing error keywords — these are NEVER dropped.
46
+ * Scans each object item's full serialization, case-insensitively.
47
+ */
48
+ export declare function detectErrorItems(items: readonly JsonValue[]): number[];
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ /**
3
+ * MIRRORED FILE — canonical source: packages/core/src/crush/json-detectors.ts.
4
+ * Byte-identical copy lives at packages/cli/src/crush/json-detectors.ts (the CLI is a
5
+ * zero-dep artifact and cannot import @greprag/core). KEEP IN SYNC —
6
+ * the sync guard in tests/test-crush.cjs fails the build on drift.
7
+ *
8
+ * Array classification + outlier/sequence detectors for the JSON
9
+ * crusher — port of the simple core of Headroom's smart_crusher
10
+ * (classifier.rs, outliers.rs, statistics.rs).
11
+ *
12
+ * Carries Headroom's documented fixes:
13
+ * - Zero-padded string IDs ("001", "002", ...) are NOT sequential
14
+ * numerics — if every parseable value originated as a string, the
15
+ * field is categorical and the padding matters.
16
+ * - Rare-status detection must not short-circuit at >10 distinct
17
+ * values: cardinality cap raised to 50 with a Pareto check (smallest
18
+ * K values covering >=80%; rare iff K <= 5), so datasets with dozens
19
+ * of error codes keep their rare-error preservation.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.classifyArray = classifyArray;
23
+ exports.detectSequentialPattern = detectSequentialPattern;
24
+ exports.detectRareStatusValues = detectRareStatusValues;
25
+ exports.detectStructuralOutliers = detectStructuralOutliers;
26
+ exports.detectErrorItems = detectErrorItems;
27
+ const crush_keywords_1 = require("./crush-keywords");
28
+ /** Classify a JSON array by element types (walks every element). */
29
+ function classifyArray(items) {
30
+ if (items.length === 0)
31
+ return 'empty';
32
+ let hasBool = false, hasNumber = false, hasString = false, hasObject = false, hasArray = false, hasNull = false;
33
+ for (const item of items) {
34
+ if (item === null)
35
+ hasNull = true;
36
+ else if (typeof item === 'boolean')
37
+ hasBool = true;
38
+ else if (typeof item === 'number')
39
+ hasNumber = true;
40
+ else if (typeof item === 'string')
41
+ hasString = true;
42
+ else if (Array.isArray(item))
43
+ hasArray = true;
44
+ else
45
+ hasObject = true;
46
+ }
47
+ if (hasBool && !hasNumber && !hasString && !hasObject && !hasArray && !hasNull)
48
+ return 'bool_array';
49
+ if (hasObject && !hasBool && !hasNumber && !hasString && !hasArray && !hasNull)
50
+ return 'dict_array';
51
+ if (hasString && !hasBool && !hasNumber && !hasObject && !hasArray && !hasNull)
52
+ return 'string_array';
53
+ if (hasNumber && !hasBool && !hasString && !hasObject && !hasArray && !hasNull)
54
+ return 'number_array';
55
+ if (hasArray && !hasBool && !hasNumber && !hasString && !hasObject && !hasNull)
56
+ return 'nested_array';
57
+ return 'mixed_array';
58
+ }
59
+ function isPlainObject(v) {
60
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
61
+ }
62
+ function stringifyScalar(v) {
63
+ if (typeof v === 'string')
64
+ return v;
65
+ return JSON.stringify(v);
66
+ }
67
+ /**
68
+ * Detect whether values form a sequential numeric pattern (IDs: 1,2,3…).
69
+ *
70
+ * Zero-padding fix: string values that parse as integers are counted,
71
+ * but if EVERY numeric value originated as a string the field is
72
+ * refused as sequential (padded codes are categorical).
73
+ */
74
+ function detectSequentialPattern(values) {
75
+ if (values.length < 5)
76
+ return false;
77
+ const nums = [];
78
+ let hadNonStringNumeric = false;
79
+ for (const v of values) {
80
+ if (typeof v === 'number') {
81
+ nums.push(v);
82
+ hadNonStringNumeric = true;
83
+ }
84
+ else if (typeof v === 'string' && /^[+-]?\d+$/.test(v.trim())) {
85
+ nums.push(parseInt(v.trim(), 10));
86
+ // Deliberately NOT setting hadNonStringNumeric (the fix).
87
+ }
88
+ }
89
+ if (nums.length < 5)
90
+ return false;
91
+ if (!hadNonStringNumeric)
92
+ return false;
93
+ const sorted = [...nums].sort((a, b) => a - b);
94
+ const diffs = [];
95
+ for (let i = 1; i < sorted.length; i++)
96
+ diffs.push(sorted[i] - sorted[i - 1]);
97
+ if (diffs.length === 0)
98
+ return false;
99
+ const avg = diffs.reduce((s, d) => s + d, 0) / diffs.length;
100
+ if (avg < 0.5 || avg > 2.0)
101
+ return false;
102
+ const consistent = diffs.filter((d) => d >= 0.5 && d <= 2.0).length;
103
+ return consistent / diffs.length > 0.8;
104
+ }
105
+ /**
106
+ * Detect items with rare values in status-like categorical fields
107
+ * (Pareto check, cardinality 2..=50). Returns item indices.
108
+ */
109
+ function detectRareStatusValues(items, commonFields) {
110
+ const outliers = [];
111
+ const sortedFields = [...commonFields].sort();
112
+ for (const field of sortedFields) {
113
+ const values = [];
114
+ for (const item of items) {
115
+ if (isPlainObject(item) && field in item)
116
+ values.push(item[field]);
117
+ }
118
+ const uniqueValues = new Set();
119
+ for (const v of values) {
120
+ if (v !== null)
121
+ uniqueValues.add(stringifyScalar(v));
122
+ }
123
+ // Cardinality cap: 2..=50 (the >10 short-circuit fix).
124
+ if (uniqueValues.size < 2 || uniqueValues.size > 50)
125
+ continue;
126
+ const counts = new Map();
127
+ for (const v of values) {
128
+ const key = v === null ? '__none__' : stringifyScalar(v);
129
+ counts.set(key, (counts.get(key) ?? 0) + 1);
130
+ }
131
+ if (counts.size === 0)
132
+ continue;
133
+ // Pareto: smallest K values covering >=80% of items.
134
+ const sortedCounts = [...counts.entries()].sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1));
135
+ const threshold = Math.ceil(values.length * 0.8);
136
+ let cumulative = 0;
137
+ const topK = new Set();
138
+ for (const [value, count] of sortedCounts) {
139
+ cumulative += count;
140
+ topK.add(value);
141
+ if (cumulative >= threshold)
142
+ break;
143
+ }
144
+ // Above K=5 the distribution is too uniform to call anything rare.
145
+ if (topK.size > 5)
146
+ continue;
147
+ for (let i = 0; i < items.length; i++) {
148
+ const item = items[i];
149
+ if (!isPlainObject(item) || !(field in item))
150
+ continue;
151
+ const v = item[field];
152
+ const key = v === null ? '__none__' : stringifyScalar(v);
153
+ if (!topK.has(key))
154
+ outliers.push(i);
155
+ }
156
+ }
157
+ return outliers;
158
+ }
159
+ /**
160
+ * Detect structural outliers: items carrying a rare field (<20% of the
161
+ * array) plus rare-status items. Returns sorted, deduped indices.
162
+ */
163
+ function detectStructuralOutliers(items) {
164
+ if (items.length < 5)
165
+ return [];
166
+ const fieldCounts = new Map();
167
+ for (const item of items) {
168
+ if (!isPlainObject(item))
169
+ continue;
170
+ for (const key of Object.keys(item)) {
171
+ fieldCounts.set(key, (fieldCounts.get(key) ?? 0) + 1);
172
+ }
173
+ }
174
+ const n = items.length;
175
+ const commonFields = new Set();
176
+ const rareFields = new Set();
177
+ for (const [field, count] of fieldCounts) {
178
+ if (count >= n * 0.8)
179
+ commonFields.add(field);
180
+ if (count < n * 0.2)
181
+ rareFields.add(field);
182
+ }
183
+ const outliers = new Set();
184
+ for (let i = 0; i < items.length; i++) {
185
+ const item = items[i];
186
+ if (!isPlainObject(item))
187
+ continue;
188
+ if (Object.keys(item).some((k) => rareFields.has(k)))
189
+ outliers.add(i);
190
+ }
191
+ for (const idx of detectRareStatusValues(items, commonFields))
192
+ outliers.add(idx);
193
+ return [...outliers].sort((a, b) => a - b);
194
+ }
195
+ /**
196
+ * Detect items containing error keywords — these are NEVER dropped.
197
+ * Scans each object item's full serialization, case-insensitively.
198
+ */
199
+ function detectErrorItems(items) {
200
+ const out = [];
201
+ for (let i = 0; i < items.length; i++) {
202
+ const item = items[i];
203
+ if (!isPlainObject(item))
204
+ continue;
205
+ const serialized = JSON.stringify(item).toLowerCase();
206
+ if (crush_keywords_1.JSON_ERROR_KEYWORDS.some((kw) => serialized.includes(kw)))
207
+ out.push(i);
208
+ }
209
+ return out;
210
+ }
211
+ //# sourceMappingURL=json-detectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-detectors.js","sourceRoot":"","sources":["../../src/crush/json-detectors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * MIRRORED FILE — canonical source: packages/core/src/crush/log-compressor.ts.
3
+ * Byte-identical copy lives at packages/cli/src/crush/log-compressor.ts (the CLI is a
4
+ * zero-dep artifact and cannot import @greprag/core). KEEP IN SYNC —
5
+ * the sync guard in tests/test-crush.cjs fails the build on drift.
6
+ *
7
+ * Log/build-output compressor — TypeScript port of Headroom's
8
+ * transforms/log_compressor.rs. Compresses build and test output
9
+ * (pytest/npm/cargo/jest/make/generic): keep errors, stack traces and
10
+ * summary lines + small context windows, drop the passing/INFO bulk.
11
+ * Typical compression: 10-50x.
12
+ *
13
+ * Carries Headroom's documented fixes:
14
+ * - Stack-trace state machine survives blank lines inside chained
15
+ * Python tracebacks (the original terminated on any blank line and
16
+ * dropped the second half of "During handling of the above
17
+ * exception" traces).
18
+ * - Dedupe preserves the message prefix (everything before the first
19
+ * `:` or `=`), so two distinct errors that share a trailing
20
+ * address/id shape stay distinct survivors.
21
+ */
22
+ import { CrushResult } from './crush-types';
23
+ export type LogFormat = 'pytest' | 'npm' | 'cargo' | 'jest' | 'make' | 'generic';
24
+ export type LogLevel = 'error' | 'fail' | 'warn' | 'info' | 'debug' | 'trace' | 'unknown';
25
+ export interface LogLine {
26
+ lineNumber: number;
27
+ content: string;
28
+ level: LogLevel;
29
+ isStackTrace: boolean;
30
+ isSummary: boolean;
31
+ score: number;
32
+ }
33
+ export interface LogCompressorConfig {
34
+ maxErrors: number;
35
+ errorContextLines: number;
36
+ keepFirstError: boolean;
37
+ keepLastError: boolean;
38
+ maxStackTraces: number;
39
+ stackTraceMaxLines: number;
40
+ maxWarnings: number;
41
+ dedupeWarnings: boolean;
42
+ keepSummaryLines: boolean;
43
+ maxTotalLines: number;
44
+ /** Inputs with fewer lines than this pass through verbatim. */
45
+ minLinesToCompress: number;
46
+ bias: number;
47
+ }
48
+ export declare const DEFAULT_LOG_CONFIG: LogCompressorConfig;
49
+ export declare function detectFormat(lines: readonly string[]): LogFormat;
50
+ export declare function classifyLevel(line: string): LogLevel;
51
+ export declare function isSummaryLine(line: string): boolean;
52
+ export declare function parseLogLines(lines: readonly string[], config: LogCompressorConfig): LogLine[];
53
+ /**
54
+ * Conservative dedupe key: the message prefix (before the first `:` or
55
+ * `=`) survives verbatim; only the trailing variable region is
56
+ * normalized (hex addresses, digits, paths).
57
+ */
58
+ export declare function normalizeForDedupe(content: string): string;
59
+ /** Compress build/test log output. Short inputs pass through verbatim. */
60
+ export declare function crushLog(content: string, config?: Partial<LogCompressorConfig>): CrushResult;