@unerr-ai/unerr 0.2.0 → 0.2.2
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/README.md +6 -0
- package/dist/cli.js +37236 -35793
- package/package.json +6 -1
- package/dist/behaviors/agent-llm-bridge.js +0 -166
- package/dist/behaviors/architecture-guard.js +0 -256
- package/dist/behaviors/auto-doc.js +0 -247
- package/dist/behaviors/cascade-guard.js +0 -289
- package/dist/behaviors/change-narrative.js +0 -270
- package/dist/behaviors/convention-drift.js +0 -290
- package/dist/behaviors/framework.js +0 -235
- package/dist/behaviors/guard-formatter.js +0 -44
- package/dist/behaviors/incomplete-work.js +0 -270
- package/dist/behaviors/loop-breaker.js +0 -300
- package/dist/behaviors/session-continuity.js +0 -208
- package/dist/commands/branches.js +0 -97
- package/dist/commands/check-commit.js +0 -225
- package/dist/commands/compress-output.js +0 -64
- package/dist/commands/config-verify.js +0 -243
- package/dist/commands/daemon.js +0 -905
- package/dist/commands/dashboard.js +0 -52
- package/dist/commands/debug.js +0 -200
- package/dist/commands/enrich.js +0 -184
- package/dist/commands/exec.js +0 -233
- package/dist/commands/gain.js +0 -156
- package/dist/commands/hook.js +0 -88
- package/dist/commands/index.js +0 -88
- package/dist/commands/init.js +0 -74
- package/dist/commands/install.js +0 -505
- package/dist/commands/learn.js +0 -116
- package/dist/commands/manifest.js +0 -193
- package/dist/commands/rewind.js +0 -103
- package/dist/commands/serve.js +0 -19
- package/dist/commands/setup-wizard.js +0 -414
- package/dist/commands/skills.js +0 -64
- package/dist/commands/stats.js +0 -20
- package/dist/commands/status.js +0 -654
- package/dist/commands/timeline.js +0 -139
- package/dist/commands/uninstall.js +0 -230
- package/dist/components/App.js +0 -109
- package/dist/components/Banner.js +0 -12
- package/dist/components/ConfirmPrompt.js +0 -25
- package/dist/components/DriftSummary.js +0 -23
- package/dist/components/GradeBadge.js +0 -15
- package/dist/components/HealthCard.js +0 -18
- package/dist/components/InkSpinner.js +0 -22
- package/dist/components/InputBox.js +0 -17
- package/dist/components/KeyValue.js +0 -13
- package/dist/components/MessageList.js +0 -14
- package/dist/components/ProgressBar.js +0 -26
- package/dist/components/Section.js +0 -16
- package/dist/components/SessionSummaryCard.js +0 -73
- package/dist/components/StartupDisplay.js +0 -24
- package/dist/components/StatusDashboard.js +0 -57
- package/dist/components/StatusLine.js +0 -8
- package/dist/components/StepLine.js +0 -22
- package/dist/components/Theme.js +0 -20
- package/dist/components/ToolProgress.js +0 -8
- package/dist/components/ViolationList.js +0 -21
- package/dist/components/render.js +0 -13
- package/dist/config/agent-registry.js +0 -237
- package/dist/config/claude-settings-hooks.js +0 -304
- package/dist/config/hook-installer.js +0 -65
- package/dist/config/instruction-writer.js +0 -388
- package/dist/config/mcp-config-writer.js +0 -266
- package/dist/config/settings.js +0 -174
- package/dist/config/tool-detector.js +0 -42
- package/dist/config/value-surfacing.js +0 -119
- package/dist/core/context-assembly.js +0 -108
- package/dist/core/conversation.js +0 -33
- package/dist/core/local-chat-provider.js +0 -475
- package/dist/core/provider-factory.js +0 -55
- package/dist/core/providers.js +0 -90
- package/dist/core/query-engine.js +0 -174
- package/dist/daemon/api.js +0 -312
- package/dist/daemon/autostart.js +0 -119
- package/dist/daemon/bootstrap.js +0 -39
- package/dist/daemon/client.js +0 -164
- package/dist/daemon/detect-ci.js +0 -81
- package/dist/daemon/platform-linux.js +0 -146
- package/dist/daemon/platform-macos.js +0 -134
- package/dist/daemon/platform-windows.js +0 -116
- package/dist/daemon/process-manager.js +0 -299
- package/dist/daemon/protocol.js +0 -23
- package/dist/daemon/registry.js +0 -270
- package/dist/daemon/settings-schema.js +0 -72
- package/dist/daemon/system-health.js +0 -134
- package/dist/daemon/version-checker.js +0 -262
- package/dist/daemon/warm-start.js +0 -223
- package/dist/entrypoints/cli.js +0 -1043
- package/dist/entrypoints/daemon.js +0 -380
- package/dist/entrypoints/repl.js +0 -147
- package/dist/hooks/adapters/claude-code.js +0 -90
- package/dist/hooks/adapters/cline.js +0 -100
- package/dist/hooks/adapters/cursor.js +0 -98
- package/dist/hooks/hook-dedup.js +0 -79
- package/dist/hooks/hook-runner.js +0 -113
- package/dist/hooks/navigation-hooks.js +0 -175
- package/dist/hooks/prompt-hooks.js +0 -63
- package/dist/hooks/shell-hooks.js +0 -47
- package/dist/ignore.js +0 -111
- package/dist/intelligence/approach-suggester.js +0 -61
- package/dist/intelligence/ast-extractor.js +0 -2615
- package/dist/intelligence/ast-worker.js +0 -34
- package/dist/intelligence/background-indexer.js +0 -121
- package/dist/intelligence/blast-radius.js +0 -200
- package/dist/intelligence/community-detection.js +0 -691
- package/dist/intelligence/community-detector.js +0 -184
- package/dist/intelligence/computation-scheduler.js +0 -75
- package/dist/intelligence/confidence-propagation.js +0 -47
- package/dist/intelligence/convention-detector.js +0 -242
- package/dist/intelligence/convention-learner.js +0 -205
- package/dist/intelligence/convention-matcher.js +0 -205
- package/dist/intelligence/cozo-schema.js +0 -376
- package/dist/intelligence/decision-point-detector.js +0 -90
- package/dist/intelligence/deep-dive-tools.js +0 -586
- package/dist/intelligence/durability-scorer.js +0 -84
- package/dist/intelligence/exploration-cost.js +0 -204
- package/dist/intelligence/exploration-pattern-tracker.js +0 -61
- package/dist/intelligence/fact-generator.js +0 -322
- package/dist/intelligence/facts-schema.js +0 -90
- package/dist/intelligence/file-intelligence.js +0 -59
- package/dist/intelligence/graph-holder.js +0 -220
- package/dist/intelligence/graph-temporal-joiner.js +0 -238
- package/dist/intelligence/health-grade.js +0 -423
- package/dist/intelligence/health-grader.js +0 -200
- package/dist/intelligence/health-map-data.js +0 -259
- package/dist/intelligence/import-symbols.js +0 -136
- package/dist/intelligence/incremental-indexer.js +0 -658
- package/dist/intelligence/indexer/centrality.js +0 -62
- package/dist/intelligence/indexer/cfg-context.js +0 -95
- package/dist/intelligence/indexer/confidence.js +0 -34
- package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
- package/dist/intelligence/indexer/edge-repair.js +0 -89
- package/dist/intelligence/indexer/entity-key.js +0 -17
- package/dist/intelligence/indexer/export-map.js +0 -132
- package/dist/intelligence/indexer/git-cochange.js +0 -128
- package/dist/intelligence/indexer/graph-patch.js +0 -147
- package/dist/intelligence/indexer/incremental.js +0 -78
- package/dist/intelligence/indexer/ingest.js +0 -160
- package/dist/intelligence/indexer/language-detect.js +0 -226
- package/dist/intelligence/indexer/metadata.js +0 -63
- package/dist/intelligence/indexer/mutation-tracker.js +0 -79
- package/dist/intelligence/indexer/orchestrator.js +0 -155
- package/dist/intelligence/indexer/plugin-interface.js +0 -31
- package/dist/intelligence/indexer/plugins/csharp.js +0 -440
- package/dist/intelligence/indexer/plugins/go.js +0 -335
- package/dist/intelligence/indexer/plugins/java.js +0 -370
- package/dist/intelligence/indexer/plugins/python.js +0 -358
- package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
- package/dist/intelligence/indexer/plugins/ruby.js +0 -290
- package/dist/intelligence/indexer/plugins/rust.js +0 -484
- package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
- package/dist/intelligence/indexer/plugins/typescript.js +0 -456
- package/dist/intelligence/indexer/resource-monitor.js +0 -93
- package/dist/intelligence/indexer/scip/decoder.js +0 -253
- package/dist/intelligence/indexer/scip/detector.js +0 -232
- package/dist/intelligence/indexer/scip/downloader.js +0 -427
- package/dist/intelligence/indexer/scip/fallback.js +0 -34
- package/dist/intelligence/indexer/scip/merger.js +0 -109
- package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
- package/dist/intelligence/indexer/scip/runner.js +0 -98
- package/dist/intelligence/indexer/snapshot.js +0 -66
- package/dist/intelligence/indexer/test-detector.js +0 -196
- package/dist/intelligence/indexer/watch-integration.js +0 -61
- package/dist/intelligence/indexer/worker.js +0 -85
- package/dist/intelligence/local-convention-detector.js +0 -437
- package/dist/intelligence/local-embeddings.js +0 -190
- package/dist/intelligence/local-graph.js +0 -1946
- package/dist/intelligence/local-indexer.js +0 -1575
- package/dist/intelligence/local-llm.js +0 -163
- package/dist/intelligence/local-rule-generator.js +0 -154
- package/dist/intelligence/local-snapshot.js +0 -213
- package/dist/intelligence/negative-knowledge.js +0 -103
- package/dist/intelligence/persistent-db.js +0 -85
- package/dist/intelligence/query-router.js +0 -2556
- package/dist/intelligence/risk-classifier.js +0 -116
- package/dist/intelligence/rule-evaluator.js +0 -380
- package/dist/intelligence/rule-generator.js +0 -49
- package/dist/intelligence/search-index.js +0 -173
- package/dist/intelligence/semantic/docstring-extractor.js +0 -67
- package/dist/intelligence/semantic/embedding-store.js +0 -52
- package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
- package/dist/intelligence/semantic/git-message-miner.js +0 -114
- package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
- package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
- package/dist/intelligence/semantic/node2vec-walks.js +0 -103
- package/dist/intelligence/semantic/path-domain-inference.js +0 -112
- package/dist/intelligence/semantic/similarity-engine.js +0 -60
- package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
- package/dist/intelligence/session-brief-builder.js +0 -159
- package/dist/intelligence/session-context.js +0 -221
- package/dist/intelligence/session-health-monitor.js +0 -211
- package/dist/intelligence/session-narrative.js +0 -197
- package/dist/intelligence/session-pattern-analyzer.js +0 -218
- package/dist/intelligence/signal-scorer.js +0 -390
- package/dist/intelligence/signal-show-store.js +0 -182
- package/dist/intelligence/smart-truncate.js +0 -158
- package/dist/intelligence/subgraph-cache.js +0 -88
- package/dist/intelligence/temporal-facts.js +0 -494
- package/dist/intelligence/token-estimator.js +0 -100
- package/dist/intelligence/tool-injector.js +0 -87
- package/dist/intelligence/tree-sitter-loader.js +0 -71
- package/dist/intelligence/worker-pool.js +0 -116
- package/dist/proxy/arg-validator.js +0 -79
- package/dist/proxy/auto-bootstrap.js +0 -167
- package/dist/proxy/bridge.js +0 -147
- package/dist/proxy/budget-enforcer.js +0 -70
- package/dist/proxy/compression-quality-monitor.js +0 -160
- package/dist/proxy/compression-stats.js +0 -51
- package/dist/proxy/context-rot-detector.js +0 -137
- package/dist/proxy/drift-detector.js +0 -139
- package/dist/proxy/efficiency-tracker.js +0 -79
- package/dist/proxy/fact-ranking.js +0 -154
- package/dist/proxy/format-encoder.js +0 -266
- package/dist/proxy/http-transport.js +0 -90
- package/dist/proxy/lifecycle-actor.js +0 -55
- package/dist/proxy/lifecycle-machine.js +0 -187
- package/dist/proxy/log-tailer.js +0 -265
- package/dist/proxy/model-pricing.js +0 -98
- package/dist/proxy/network-firewall.js +0 -141
- package/dist/proxy/nudge-state.js +0 -93
- package/dist/proxy/output-compressor.js +0 -185
- package/dist/proxy/pid-lock.js +0 -291
- package/dist/proxy/proxy-context.js +0 -11
- package/dist/proxy/proxy.js +0 -2633
- package/dist/proxy/response-enrichment.js +0 -32
- package/dist/proxy/response-envelope.js +0 -313
- package/dist/proxy/session-dedup.js +0 -82
- package/dist/proxy/session-legend.js +0 -30
- package/dist/proxy/session-persistence.js +0 -210
- package/dist/proxy/session-resume.js +0 -94
- package/dist/proxy/session-stats.js +0 -513
- package/dist/proxy/shell-classifier.js +0 -1346
- package/dist/proxy/shell-compression-log.js +0 -93
- package/dist/proxy/shell-compressor.js +0 -390
- package/dist/proxy/shell-graph-boost.js +0 -202
- package/dist/proxy/shell-monitor-map.js +0 -18
- package/dist/proxy/shell-stats.js +0 -54
- package/dist/proxy/shell-strategies/cloud.js +0 -215
- package/dist/proxy/shell-strategies/diff.js +0 -159
- package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
- package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
- package/dist/proxy/shell-strategies/git-status.js +0 -177
- package/dist/proxy/shell-strategies/key-value.js +0 -193
- package/dist/proxy/shell-strategies/log-text.js +0 -154
- package/dist/proxy/shell-strategies/omni.js +0 -188
- package/dist/proxy/shell-strategies/progress.js +0 -55
- package/dist/proxy/shell-strategies/redact.js +0 -76
- package/dist/proxy/shell-strategies/structured.js +0 -241
- package/dist/proxy/shell-strategies/tabular.js +0 -243
- package/dist/proxy/shell-strategies/test-results-types.js +0 -13
- package/dist/proxy/shell-strategies/test-results.js +0 -784
- package/dist/proxy/shell-strategies/tree-paths.js +0 -144
- package/dist/proxy/shell-strategies/yaml.js +0 -182
- package/dist/proxy/shell-tee.js +0 -111
- package/dist/proxy/signal-dedup.js +0 -171
- package/dist/proxy/startup-renderer.js +0 -158
- package/dist/proxy/task-token-display.js +0 -38
- package/dist/proxy/token-counter.js +0 -61
- package/dist/proxy/tool-clusters.js +0 -273
- package/dist/proxy/tool-definitions.js +0 -525
- package/dist/proxy/transport-mux.js +0 -229
- package/dist/proxy/wire-cap.js +0 -268
- package/dist/rules/developer.mozilla.org.json +0 -9
- package/dist/rules/github.com.json +0 -21
- package/dist/schemas/api/skills.js +0 -19
- package/dist/schemas/common/errors.js +0 -7
- package/dist/schemas/common/headers.js +0 -5
- package/dist/schemas/entities/edge.js +0 -25
- package/dist/schemas/entities/entity.js +0 -22
- package/dist/schemas/entities/rule.js +0 -18
- package/dist/schemas/index.js +0 -14
- package/dist/server/event-bus.js +0 -59
- package/dist/server/http.js +0 -156
- package/dist/server/middleware.js +0 -70
- package/dist/server/routes/drift.js +0 -97
- package/dist/server/routes/intelligence.js +0 -1217
- package/dist/server/routes/reasoning-quality.js +0 -444
- package/dist/server/routes/session.js +0 -86
- package/dist/server/routes/stream.js +0 -120
- package/dist/server/routes/system.js +0 -73
- package/dist/server/routes/temporal.js +0 -170
- package/dist/server/routes/timeline.js +0 -232
- package/dist/server/routes/token-flow.js +0 -403
- package/dist/skills/effectiveness-tracker.js +0 -93
- package/dist/skills/local-pack.js +0 -380
- package/dist/skills/resolver.js +0 -495
- package/dist/state-detector.js +0 -83
- package/dist/timeline/intent-detector.js +0 -263
- package/dist/timeline/loop-miner.js +0 -140
- package/dist/timeline/open-threads.js +0 -49
- package/dist/timeline/signal-reinforcer.js +0 -62
- package/dist/timeline/timeline-bootstrap.js +0 -151
- package/dist/timeline/timeline-store.js +0 -618
- package/dist/tools/coding/bash.js +0 -49
- package/dist/tools/coding/file-edit.js +0 -72
- package/dist/tools/coding/file-outline.js +0 -227
- package/dist/tools/coding/file-read-protocol.js +0 -425
- package/dist/tools/coding/file-read.js +0 -35
- package/dist/tools/coding/file-write.js +0 -43
- package/dist/tools/coding/glob-tool.js +0 -109
- package/dist/tools/coding/grep.js +0 -162
- package/dist/tools/coding/index.js +0 -27
- package/dist/tools/intelligence/index.js +0 -269
- package/dist/tools/intelligence/record-fact.js +0 -48
- package/dist/tools/intelligence/timeline-markers.js +0 -130
- package/dist/tools/registry.js +0 -47
- package/dist/tools/types.js +0 -8
- package/dist/tracking/auto-snapshot-triggers.js +0 -246
- package/dist/tracking/branch-context.js +0 -115
- package/dist/tracking/branch-snapshot.js +0 -217
- package/dist/tracking/causal-bridge.js +0 -317
- package/dist/tracking/circuit-breaker.js +0 -147
- package/dist/tracking/commit-watcher.js +0 -114
- package/dist/tracking/context-ledger.js +0 -119
- package/dist/tracking/correction-detector.js +0 -324
- package/dist/tracking/drift-tracker.js +0 -874
- package/dist/tracking/durability-tracker.js +0 -94
- package/dist/tracking/entity-rewind.js +0 -200
- package/dist/tracking/file-hash-state.js +0 -114
- package/dist/tracking/git-attribution.js +0 -132
- package/dist/tracking/git-trailers.js +0 -171
- package/dist/tracking/intelligence-counter.js +0 -46
- package/dist/tracking/intent-correlator.js +0 -202
- package/dist/tracking/intent-encoder.js +0 -52
- package/dist/tracking/intent-token-tracker.js +0 -159
- package/dist/tracking/ledger-archiver.js +0 -94
- package/dist/tracking/ledger-chains.js +0 -245
- package/dist/tracking/metrics-store.js +0 -361
- package/dist/tracking/native-watcher.js +0 -131
- package/dist/tracking/offline-rewind.js +0 -295
- package/dist/tracking/pending-violations.js +0 -74
- package/dist/tracking/persistence-effectiveness.js +0 -167
- package/dist/tracking/prompt-durability.js +0 -202
- package/dist/tracking/quality-signals.js +0 -213
- package/dist/tracking/redactor.js +0 -73
- package/dist/tracking/rewind-engine.js +0 -161
- package/dist/tracking/session-history.js +0 -128
- package/dist/tracking/session-receipt.js +0 -88
- package/dist/tracking/session-summary-writer.js +0 -157
- package/dist/tracking/shadow-ledger.js +0 -321
- package/dist/tracking/stash-manager.js +0 -258
- package/dist/tracking/timeline-fork.js +0 -213
- package/dist/tracking/timeline.js +0 -69
- package/dist/tracking/token-flow.js +0 -276
- package/dist/tracking/turn-segmenter.js +0 -122
- package/dist/tracking/weekly-accumulator.js +0 -179
- package/dist/tracking/working-snapshots.js +0 -188
- package/dist/tracking/workspace-manifest.js +0 -176
- package/dist/transport/http.js +0 -102
- package/dist/utils/counterfactual.js +0 -65
- package/dist/utils/deep-link.js +0 -34
- package/dist/utils/detect.js +0 -193
- package/dist/utils/exec.js +0 -73
- package/dist/utils/file-logger.js +0 -87
- package/dist/utils/format-error.js +0 -29
- package/dist/utils/git.js +0 -181
- package/dist/utils/log.js +0 -57
- package/dist/utils/logger.js +0 -35
- package/dist/utils/mcp-content-json.js +0 -8
- package/dist/utils/session-logger.js +0 -154
- package/dist/utils/startup-log.js +0 -512
- package/dist/utils/ui.js +0 -56
|
@@ -1,796 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Strategy T7 — format-aware error diagnostic compression.
|
|
3
|
-
* Groups by error code (tsc, eslint, biome) or deduplicates stack traces (node).
|
|
4
|
-
*/
|
|
5
|
-
function detectErrorFormat(command, lines) {
|
|
6
|
-
if (command) {
|
|
7
|
-
const cmd = command.toLowerCase();
|
|
8
|
-
if (cmd.includes("tsc") || cmd.includes("typescript"))
|
|
9
|
-
return "tsc";
|
|
10
|
-
if (cmd.includes("eslint"))
|
|
11
|
-
return "eslint";
|
|
12
|
-
if (cmd.includes("biome"))
|
|
13
|
-
return "biome";
|
|
14
|
-
if (/\b(gcc|g\+\+|clang|clang\+\+)\b/.test(cmd))
|
|
15
|
-
return "gcc_clang";
|
|
16
|
-
if (/\b(rustc|cargo clippy|cargo check|cargo build)\b/.test(cmd))
|
|
17
|
-
return "rustc";
|
|
18
|
-
if (/\b(go vet|golangci-lint|staticcheck)\b/.test(cmd))
|
|
19
|
-
return "go";
|
|
20
|
-
if (/\b(python|python3|mypy|pyright|pylint|flake8|ruff)\b/.test(cmd))
|
|
21
|
-
return "python_tb";
|
|
22
|
-
if (/\b(javac|kotlinc)\b/.test(cmd))
|
|
23
|
-
return "javac";
|
|
24
|
-
}
|
|
25
|
-
const sample = lines.slice(0, 30).join("\n");
|
|
26
|
-
if (/error TS\d{4}:/.test(sample))
|
|
27
|
-
return "tsc";
|
|
28
|
-
if (/^\s+\d+:\d+\s+(error|warning)\s+.+\s{2,}\S+/m.test(sample))
|
|
29
|
-
return "eslint";
|
|
30
|
-
if (/^\s*×/.test(sample) || /\bdiagnostics?\b/i.test(sample))
|
|
31
|
-
return "biome";
|
|
32
|
-
// gcc/clang: file.c:10:5: error: ...
|
|
33
|
-
if (/^.+:\d+:\d+:\s+(error|warning|note):/m.test(sample))
|
|
34
|
-
return "gcc_clang";
|
|
35
|
-
// rustc: error[E0308]: mismatched types
|
|
36
|
-
if (/^error\[E\d{4}\]:/m.test(sample))
|
|
37
|
-
return "rustc";
|
|
38
|
-
// Go: file.go:10:5: ...
|
|
39
|
-
if (/^.+\.go:\d+:\d+:.*$/m.test(sample) && !/error TS/.test(sample))
|
|
40
|
-
return "go";
|
|
41
|
-
// Python traceback
|
|
42
|
-
if (/^Traceback \(most recent call last\):/m.test(sample) ||
|
|
43
|
-
/^ {2}File ".+", line \d+/m.test(sample))
|
|
44
|
-
return "python_tb";
|
|
45
|
-
// javac: File.java:10: error: ...
|
|
46
|
-
if (/^.+\.java:\d+: error:/m.test(sample))
|
|
47
|
-
return "javac";
|
|
48
|
-
if (/^\s*at\s+/.test(sample) ||
|
|
49
|
-
/^(Error|TypeError|RangeError|ReferenceError|SyntaxError):/m.test(sample))
|
|
50
|
-
return "node_stack";
|
|
51
|
-
// shellcheck: "In file.sh line N:"
|
|
52
|
-
if (/^In .+ line \d+:/m.test(sample))
|
|
53
|
-
return "shellcheck";
|
|
54
|
-
// Generic line diagnostics: file:line:col: message (rubocop, stylelint, vale, etc.)
|
|
55
|
-
if (/^.+:\d+(?::\d+)?:\s*(?:error|warning|info|note|convention|refactor)\s*:?\s*.+$/m.test(sample))
|
|
56
|
-
return "line_diagnostic";
|
|
57
|
-
return "generic";
|
|
58
|
-
}
|
|
59
|
-
function compressTscErrors(lines) {
|
|
60
|
-
const TSC_ERROR = /^(.+)\((\d+),(\d+)\): error (TS\d{4}): (.+)$/;
|
|
61
|
-
const SUMMARY = /^Found (\d+) errors?/;
|
|
62
|
-
const groups = new Map();
|
|
63
|
-
let summaryLine = "";
|
|
64
|
-
for (const line of lines) {
|
|
65
|
-
const m = TSC_ERROR.exec(line);
|
|
66
|
-
if (m) {
|
|
67
|
-
const [, filePath, , , code, message] = m;
|
|
68
|
-
const existing = groups.get(code);
|
|
69
|
-
if (existing) {
|
|
70
|
-
existing.count++;
|
|
71
|
-
existing.files.add(filePath);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
groups.set(code, {
|
|
75
|
-
code: code,
|
|
76
|
-
message: message,
|
|
77
|
-
firstOccurrence: line,
|
|
78
|
-
count: 1,
|
|
79
|
-
files: new Set([filePath]),
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
const s = SUMMARY.exec(line);
|
|
85
|
-
if (s)
|
|
86
|
-
summaryLine = line;
|
|
87
|
-
}
|
|
88
|
-
if (groups.size === 0)
|
|
89
|
-
return "";
|
|
90
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
91
|
-
if (summaryLine)
|
|
92
|
-
parts.push(summaryLine);
|
|
93
|
-
const sorted = [...groups.values()].sort((a, b) => b.count - a.count);
|
|
94
|
-
for (const group of sorted) {
|
|
95
|
-
parts.push("");
|
|
96
|
-
parts.push(group.firstOccurrence);
|
|
97
|
-
if (group.count > 1) {
|
|
98
|
-
parts.push(` └─ ${group.code}: ${group.count - 1} more in ${group.files.size} file(s)`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return parts.join("\n");
|
|
102
|
-
}
|
|
103
|
-
function compressEslintErrors(lines) {
|
|
104
|
-
const FILE_HEADER = /^(\/[^\s]+\.(ts|js|tsx|jsx|mts|mjs|cjs|cts))$/;
|
|
105
|
-
const ERROR_LINE = /^\s+(\d+):(\d+)\s+(error|warning)\s+(.+?)\s{2,}(\S+)\s*$/;
|
|
106
|
-
const SUMMARY = /^✖ (\d+) problems?/;
|
|
107
|
-
const ruleGroups = new Map();
|
|
108
|
-
let currentFile = "";
|
|
109
|
-
let summaryLine = "";
|
|
110
|
-
for (const line of lines) {
|
|
111
|
-
const fh = FILE_HEADER.exec(line);
|
|
112
|
-
if (fh) {
|
|
113
|
-
currentFile = fh[1];
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
const el = ERROR_LINE.exec(line);
|
|
117
|
-
if (el) {
|
|
118
|
-
const [, lineNum, , , message, rule] = el;
|
|
119
|
-
const existing = ruleGroups.get(rule);
|
|
120
|
-
if (existing) {
|
|
121
|
-
existing.count++;
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
ruleGroups.set(rule, {
|
|
125
|
-
message: message?.trim() ?? "",
|
|
126
|
-
count: 1,
|
|
127
|
-
firstFile: currentFile,
|
|
128
|
-
firstLine: lineNum,
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
const s = SUMMARY.exec(line);
|
|
134
|
-
if (s)
|
|
135
|
-
summaryLine = line;
|
|
136
|
-
}
|
|
137
|
-
if (ruleGroups.size === 0)
|
|
138
|
-
return "";
|
|
139
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
140
|
-
if (summaryLine)
|
|
141
|
-
parts.push(summaryLine);
|
|
142
|
-
const sorted = [...ruleGroups.entries()].sort((a, b) => b[1].count - a[1].count);
|
|
143
|
-
for (const [rule, group] of sorted) {
|
|
144
|
-
if (group.count === 1) {
|
|
145
|
-
parts.push(` ${group.firstFile}:${group.firstLine} ${group.message} ${rule}`);
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
parts.push(` ${rule}: ${group.message} (${group.count} occurrences)`);
|
|
149
|
-
parts.push(` first: ${group.firstFile}:${group.firstLine}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return parts.join("\n");
|
|
153
|
-
}
|
|
154
|
-
function filterStackFrames(frames) {
|
|
155
|
-
return frames.filter((f, i) => {
|
|
156
|
-
if (i === 0)
|
|
157
|
-
return true;
|
|
158
|
-
if (/node:internal/.test(f))
|
|
159
|
-
return false;
|
|
160
|
-
if (/node_modules/.test(f)) {
|
|
161
|
-
const isFirstNM = i === 0 || !/node_modules/.test(frames[i - 1] ?? "");
|
|
162
|
-
const isLastNM = i === frames.length - 1 || !/node_modules/.test(frames[i + 1] ?? "");
|
|
163
|
-
return isFirstNM || isLastNM;
|
|
164
|
-
}
|
|
165
|
-
return true;
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
function compressNodeStack(lines) {
|
|
169
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
170
|
-
const seenFingerprints = new Map();
|
|
171
|
-
let currentStack = [];
|
|
172
|
-
let currentError = "";
|
|
173
|
-
const flushStack = () => {
|
|
174
|
-
if (currentStack.length === 0)
|
|
175
|
-
return;
|
|
176
|
-
const filtered = filterStackFrames(currentStack);
|
|
177
|
-
const fingerprint = filtered.join("\n");
|
|
178
|
-
const existing = seenFingerprints.get(fingerprint) ?? 0;
|
|
179
|
-
seenFingerprints.set(fingerprint, existing + 1);
|
|
180
|
-
if (existing === 0) {
|
|
181
|
-
if (currentError)
|
|
182
|
-
parts.push(currentError);
|
|
183
|
-
parts.push(...filtered);
|
|
184
|
-
}
|
|
185
|
-
currentStack = [];
|
|
186
|
-
currentError = "";
|
|
187
|
-
};
|
|
188
|
-
for (const line of lines) {
|
|
189
|
-
const isFrame = /^\s+at\s/.test(line);
|
|
190
|
-
if (!isFrame && currentStack.length > 0) {
|
|
191
|
-
flushStack();
|
|
192
|
-
}
|
|
193
|
-
if (isFrame) {
|
|
194
|
-
currentStack.push(line);
|
|
195
|
-
}
|
|
196
|
-
else if (/^(Error|TypeError|RangeError|ReferenceError|SyntaxError|Caused by):/.test(line)) {
|
|
197
|
-
currentError = line;
|
|
198
|
-
parts.push(line);
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
if (line.trim())
|
|
202
|
-
parts.push(line);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
flushStack();
|
|
206
|
-
for (const [, count] of seenFingerprints) {
|
|
207
|
-
if (count > 1) {
|
|
208
|
-
parts.push(` (identical stack repeated ×${count})`);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return parts.join("\n");
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* gcc/clang: file.c:10:5: error: ... — group by error message, dedup across files.
|
|
215
|
-
* Also handles: warning, note (notes are dropped unless attached to first occurrence).
|
|
216
|
-
*/
|
|
217
|
-
function compressGccClangErrors(lines) {
|
|
218
|
-
// Pattern: file:line:col: severity: message
|
|
219
|
-
const DIAG_RE = /^(.+):(\d+):(\d+):\s+(error|warning|note):\s+(.+)$/;
|
|
220
|
-
const groups = new Map();
|
|
221
|
-
let summaryErrors = 0;
|
|
222
|
-
let summaryWarnings = 0;
|
|
223
|
-
for (const line of lines) {
|
|
224
|
-
const m = DIAG_RE.exec(line);
|
|
225
|
-
if (m) {
|
|
226
|
-
const [, filePath, , , severity, message] = m;
|
|
227
|
-
if (severity === "note")
|
|
228
|
-
continue; // drop notes
|
|
229
|
-
if (severity === "error")
|
|
230
|
-
summaryErrors++;
|
|
231
|
-
else
|
|
232
|
-
summaryWarnings++;
|
|
233
|
-
const key = `${severity}:${message}`;
|
|
234
|
-
const existing = groups.get(key);
|
|
235
|
-
if (existing) {
|
|
236
|
-
existing.count++;
|
|
237
|
-
existing.files.add(filePath);
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
groups.set(key, {
|
|
241
|
-
code: severity,
|
|
242
|
-
message: message,
|
|
243
|
-
firstOccurrence: line,
|
|
244
|
-
count: 1,
|
|
245
|
-
files: new Set([filePath]),
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
if (groups.size === 0)
|
|
251
|
-
return "";
|
|
252
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
253
|
-
parts.push(`${summaryErrors} error(s), ${summaryWarnings} warning(s)`);
|
|
254
|
-
const sorted = [...groups.values()].sort((a, b) => b.count - a.count);
|
|
255
|
-
for (const group of sorted) {
|
|
256
|
-
parts.push("");
|
|
257
|
-
parts.push(group.firstOccurrence);
|
|
258
|
-
if (group.count > 1) {
|
|
259
|
-
parts.push(` └─ ${group.count - 1} more in ${group.files.size} file(s)`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return parts.join("\n");
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* rustc / cargo clippy: error[E0308]: message — group by error code.
|
|
266
|
-
*/
|
|
267
|
-
function compressRustcErrors(lines) {
|
|
268
|
-
// error[E0308]: mismatched types
|
|
269
|
-
const RUSTC_ERROR = /^(error|warning)\[([A-Z]\d{4})\]:\s+(.+)$/;
|
|
270
|
-
// --> file.rs:10:5
|
|
271
|
-
const LOCATION = /^\s*-->\s+(.+):(\d+):(\d+)$/;
|
|
272
|
-
const SUMMARY = /^(error|warning): .+ generated (\d+) warnings?/;
|
|
273
|
-
const ABORT = /^error: aborting due to (\d+)/;
|
|
274
|
-
const groups = new Map();
|
|
275
|
-
let currentCode = "";
|
|
276
|
-
let summaryLine = "";
|
|
277
|
-
for (const line of lines) {
|
|
278
|
-
const m = RUSTC_ERROR.exec(line);
|
|
279
|
-
if (m) {
|
|
280
|
-
const [, , code, message] = m;
|
|
281
|
-
currentCode = code;
|
|
282
|
-
const existing = groups.get(code);
|
|
283
|
-
if (existing) {
|
|
284
|
-
existing.count++;
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
groups.set(code, {
|
|
288
|
-
code: code,
|
|
289
|
-
message: message,
|
|
290
|
-
firstOccurrence: line,
|
|
291
|
-
count: 1,
|
|
292
|
-
files: new Set(),
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
const loc = LOCATION.exec(line);
|
|
298
|
-
if (loc && currentCode) {
|
|
299
|
-
groups.get(currentCode)?.files.add(loc[1]);
|
|
300
|
-
}
|
|
301
|
-
if (SUMMARY.test(line) || ABORT.test(line)) {
|
|
302
|
-
summaryLine = line;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (groups.size === 0)
|
|
306
|
-
return "";
|
|
307
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
308
|
-
if (summaryLine)
|
|
309
|
-
parts.push(summaryLine);
|
|
310
|
-
const sorted = [...groups.values()].sort((a, b) => b.count - a.count);
|
|
311
|
-
for (const group of sorted) {
|
|
312
|
-
parts.push("");
|
|
313
|
-
parts.push(group.firstOccurrence);
|
|
314
|
-
if (group.files.size > 0) {
|
|
315
|
-
const fileList = [...group.files].slice(0, 5).join(", ");
|
|
316
|
-
const more = group.files.size > 5 ? ` +${group.files.size - 5} more` : "";
|
|
317
|
-
parts.push(` └─ in: ${fileList}${more}`);
|
|
318
|
-
}
|
|
319
|
-
if (group.count > 1) {
|
|
320
|
-
parts.push(` └─ ${group.count} total occurrences`);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return parts.join("\n");
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Go: file.go:10:5: message — group by message, dedup across files.
|
|
327
|
-
* Handles go vet, golangci-lint, staticcheck output.
|
|
328
|
-
*/
|
|
329
|
-
function compressGoErrors(lines) {
|
|
330
|
-
// file.go:10:5: message OR file.go:10: message
|
|
331
|
-
const GO_DIAG = /^(.+\.go):(\d+)(?::\d+)?:\s+(.+)$/;
|
|
332
|
-
// golangci-lint: file.go:10:5: message (linter)
|
|
333
|
-
const GOLANGCI = /^(.+\.go):(\d+)(?::\d+)?:\s+(.+?)\s+\((\w+)\)$/;
|
|
334
|
-
const groups = new Map();
|
|
335
|
-
for (const line of lines) {
|
|
336
|
-
const gl = GOLANGCI.exec(line);
|
|
337
|
-
if (gl) {
|
|
338
|
-
const [, filePath, , message, linter] = gl;
|
|
339
|
-
const key = `${linter}:${message}`;
|
|
340
|
-
const existing = groups.get(key);
|
|
341
|
-
if (existing) {
|
|
342
|
-
existing.count++;
|
|
343
|
-
existing.files.add(filePath);
|
|
344
|
-
}
|
|
345
|
-
else {
|
|
346
|
-
groups.set(key, {
|
|
347
|
-
code: linter,
|
|
348
|
-
message: message,
|
|
349
|
-
firstOccurrence: line,
|
|
350
|
-
count: 1,
|
|
351
|
-
files: new Set([filePath]),
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
const m = GO_DIAG.exec(line);
|
|
357
|
-
if (m) {
|
|
358
|
-
const [, filePath, , message] = m;
|
|
359
|
-
const existing = groups.get(message);
|
|
360
|
-
if (existing) {
|
|
361
|
-
existing.count++;
|
|
362
|
-
existing.files.add(filePath);
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
groups.set(message, {
|
|
366
|
-
code: "",
|
|
367
|
-
message: message,
|
|
368
|
-
firstOccurrence: line,
|
|
369
|
-
count: 1,
|
|
370
|
-
files: new Set([filePath]),
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
if (groups.size === 0)
|
|
376
|
-
return "";
|
|
377
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
378
|
-
const sorted = [...groups.values()].sort((a, b) => b.count - a.count);
|
|
379
|
-
for (const group of sorted) {
|
|
380
|
-
parts.push("");
|
|
381
|
-
parts.push(group.firstOccurrence);
|
|
382
|
-
if (group.count > 1) {
|
|
383
|
-
parts.push(` └─ ${group.count - 1} more in ${group.files.size} file(s)`);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
return parts.join("\n");
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Python tracebacks: dedup identical stack traces, keep assertion details.
|
|
390
|
-
* Also handles mypy/pyright/pylint/ruff/flake8 line-based diagnostics.
|
|
391
|
-
*/
|
|
392
|
-
function compressPythonErrors(lines) {
|
|
393
|
-
// mypy/pyright: file.py:10: error: Message [error-code]
|
|
394
|
-
const MYPY_RE = /^(.+\.py):(\d+):\s+(error|warning|note):\s+(.+?)(?:\s+\[(.+)\])?$/;
|
|
395
|
-
// ruff/flake8: file.py:10:5: E501 line too long
|
|
396
|
-
const RUFF_RE = /^(.+\.py):(\d+):\d+:\s+([A-Z]\d+)\s+(.+)$/;
|
|
397
|
-
// pylint: file.py:10:0: C0114: Missing module docstring (missing-module-docstring)
|
|
398
|
-
const PYLINT_RE = /^(.+\.py):(\d+):\d+:\s+([A-Z]\d{4}):\s+(.+?)(?:\s+\((.+)\))?$/;
|
|
399
|
-
// Try line-based diagnostics first (mypy, ruff, flake8, pylint)
|
|
400
|
-
const diagGroups = new Map();
|
|
401
|
-
let diagCount = 0;
|
|
402
|
-
for (const line of lines) {
|
|
403
|
-
let code = "";
|
|
404
|
-
let message = "";
|
|
405
|
-
let filePath = "";
|
|
406
|
-
const mp = MYPY_RE.exec(line);
|
|
407
|
-
if (mp) {
|
|
408
|
-
filePath = mp[1] ?? "";
|
|
409
|
-
message = mp[4] ?? "";
|
|
410
|
-
code = mp[5] ?? "error";
|
|
411
|
-
}
|
|
412
|
-
const rp = !mp ? RUFF_RE.exec(line) : null;
|
|
413
|
-
if (rp) {
|
|
414
|
-
filePath = rp[1] ?? "";
|
|
415
|
-
code = rp[3] ?? "";
|
|
416
|
-
message = rp[4] ?? "";
|
|
417
|
-
}
|
|
418
|
-
const pp = !mp && !rp ? PYLINT_RE.exec(line) : null;
|
|
419
|
-
if (pp) {
|
|
420
|
-
filePath = pp[1] ?? "";
|
|
421
|
-
code = pp[3] ?? "";
|
|
422
|
-
message = pp[4] ?? "";
|
|
423
|
-
}
|
|
424
|
-
if (code && message) {
|
|
425
|
-
diagCount++;
|
|
426
|
-
const key = `${code}:${message}`;
|
|
427
|
-
const existing = diagGroups.get(key);
|
|
428
|
-
if (existing) {
|
|
429
|
-
existing.count++;
|
|
430
|
-
existing.files.add(filePath);
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
diagGroups.set(key, {
|
|
434
|
-
code,
|
|
435
|
-
message,
|
|
436
|
-
firstOccurrence: line,
|
|
437
|
-
count: 1,
|
|
438
|
-
files: new Set([filePath]),
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
if (diagGroups.size > 0) {
|
|
444
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
445
|
-
parts.push(`${diagCount} diagnostic(s), ${diagGroups.size} unique`);
|
|
446
|
-
const sorted = [...diagGroups.values()].sort((a, b) => b.count - a.count);
|
|
447
|
-
for (const group of sorted.slice(0, 30)) {
|
|
448
|
-
parts.push("");
|
|
449
|
-
parts.push(group.firstOccurrence);
|
|
450
|
-
if (group.count > 1) {
|
|
451
|
-
parts.push(` └─ ${group.code}: ${group.count - 1} more in ${group.files.size} file(s)`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
if (sorted.length > 30) {
|
|
455
|
-
parts.push(`\n… and ${sorted.length - 30} more diagnostic types`);
|
|
456
|
-
}
|
|
457
|
-
return parts.join("\n");
|
|
458
|
-
}
|
|
459
|
-
// Fall back to traceback compression (similar to node_stack)
|
|
460
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
461
|
-
const tracebacks = [];
|
|
462
|
-
let currentFrames = [];
|
|
463
|
-
let currentError = "";
|
|
464
|
-
let inTraceback = false;
|
|
465
|
-
for (const line of lines) {
|
|
466
|
-
if (/^Traceback \(most recent call last\):/.test(line)) {
|
|
467
|
-
if (currentFrames.length > 0) {
|
|
468
|
-
tracebacks.push({ error: currentError, frames: currentFrames });
|
|
469
|
-
}
|
|
470
|
-
currentFrames = [];
|
|
471
|
-
currentError = "";
|
|
472
|
-
inTraceback = true;
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
if (inTraceback && /^ {2}File ".+", line \d+/.test(line)) {
|
|
476
|
-
currentFrames.push(line);
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
if (inTraceback && /^\w+Error:|^\w+Exception:/.test(line)) {
|
|
480
|
-
currentError = line;
|
|
481
|
-
inTraceback = false;
|
|
482
|
-
continue;
|
|
483
|
-
}
|
|
484
|
-
if (!inTraceback && line.trim()) {
|
|
485
|
-
parts.push(line);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
if (currentFrames.length > 0 || currentError) {
|
|
489
|
-
tracebacks.push({ error: currentError, frames: currentFrames });
|
|
490
|
-
}
|
|
491
|
-
// Dedup identical tracebacks
|
|
492
|
-
const seen = new Map();
|
|
493
|
-
for (const tb of tracebacks) {
|
|
494
|
-
const fingerprint = `${tb.frames.join("\n")}\n${tb.error}`;
|
|
495
|
-
const prev = seen.get(fingerprint) ?? 0;
|
|
496
|
-
seen.set(fingerprint, prev + 1);
|
|
497
|
-
if (prev === 0) {
|
|
498
|
-
parts.push("");
|
|
499
|
-
parts.push("Traceback (most recent call last):");
|
|
500
|
-
// Keep first + last 3 frames max
|
|
501
|
-
const frames = tb.frames;
|
|
502
|
-
if (frames.length > 6) {
|
|
503
|
-
parts.push(...frames.slice(0, 3));
|
|
504
|
-
parts.push(` ... ${frames.length - 6} frames omitted ...`);
|
|
505
|
-
parts.push(...frames.slice(-3));
|
|
506
|
-
}
|
|
507
|
-
else {
|
|
508
|
-
parts.push(...frames);
|
|
509
|
-
}
|
|
510
|
-
if (tb.error)
|
|
511
|
-
parts.push(tb.error);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
for (const [, count] of seen) {
|
|
515
|
-
if (count > 1)
|
|
516
|
-
parts.push(` (identical traceback repeated ×${count})`);
|
|
517
|
-
}
|
|
518
|
-
return parts.length > 1 ? parts.join("\n") : "";
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* javac: File.java:10: error: message — group by message.
|
|
522
|
-
*/
|
|
523
|
-
function compressJavacErrors(lines) {
|
|
524
|
-
// File.java:10: error: message
|
|
525
|
-
const JAVAC_RE = /^(.+\.(?:java|kt|scala)):(\d+):\s+(error|warning):\s+(.+)$/;
|
|
526
|
-
const SUMMARY = /^(\d+) errors?$/;
|
|
527
|
-
const groups = new Map();
|
|
528
|
-
let summaryLine = "";
|
|
529
|
-
for (const line of lines) {
|
|
530
|
-
const m = JAVAC_RE.exec(line);
|
|
531
|
-
if (m) {
|
|
532
|
-
const [, filePath, , severity, message] = m;
|
|
533
|
-
const key = `${severity}:${message}`;
|
|
534
|
-
const existing = groups.get(key);
|
|
535
|
-
if (existing) {
|
|
536
|
-
existing.count++;
|
|
537
|
-
existing.files.add(filePath);
|
|
538
|
-
}
|
|
539
|
-
else {
|
|
540
|
-
groups.set(key, {
|
|
541
|
-
code: severity,
|
|
542
|
-
message: message,
|
|
543
|
-
firstOccurrence: line,
|
|
544
|
-
count: 1,
|
|
545
|
-
files: new Set([filePath]),
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
continue;
|
|
549
|
-
}
|
|
550
|
-
if (SUMMARY.test(line))
|
|
551
|
-
summaryLine = line;
|
|
552
|
-
}
|
|
553
|
-
if (groups.size === 0)
|
|
554
|
-
return "";
|
|
555
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
556
|
-
if (summaryLine)
|
|
557
|
-
parts.push(summaryLine);
|
|
558
|
-
const sorted = [...groups.values()].sort((a, b) => b.count - a.count);
|
|
559
|
-
for (const group of sorted) {
|
|
560
|
-
parts.push("");
|
|
561
|
-
parts.push(group.firstOccurrence);
|
|
562
|
-
if (group.count > 1) {
|
|
563
|
-
parts.push(` └─ ${group.count - 1} more in ${group.files.size} file(s)`);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
return parts.join("\n");
|
|
567
|
-
}
|
|
568
|
-
/** Compress shellcheck output: group by SC code, show first example of each. */
|
|
569
|
-
function compressShellcheck(lines) {
|
|
570
|
-
// Shellcheck format: 3-line blocks:
|
|
571
|
-
// In file.sh line N:
|
|
572
|
-
// <code snippet>
|
|
573
|
-
// ^-- SCXXXX (severity): message
|
|
574
|
-
const SC_HEADER = /^In (.+) line (\d+):$/;
|
|
575
|
-
const SC_CODE = /\^--\s*(SC\d{4})\s*\((\w+)\):\s*(.+)$/;
|
|
576
|
-
const groups = new Map();
|
|
577
|
-
let i = 0;
|
|
578
|
-
while (i < lines.length) {
|
|
579
|
-
const headerMatch = SC_HEADER.exec(lines[i]);
|
|
580
|
-
if (!headerMatch) {
|
|
581
|
-
i++;
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
584
|
-
const file = headerMatch[1];
|
|
585
|
-
const blockLines = [lines[i]];
|
|
586
|
-
i++;
|
|
587
|
-
// Collect lines until next header or end (max 5 lines per block)
|
|
588
|
-
let codeFound = "";
|
|
589
|
-
let severity = "";
|
|
590
|
-
let message = "";
|
|
591
|
-
while (i < lines.length && blockLines.length < 6) {
|
|
592
|
-
const line = lines[i];
|
|
593
|
-
if (SC_HEADER.test(line))
|
|
594
|
-
break;
|
|
595
|
-
blockLines.push(line);
|
|
596
|
-
const codeMatch = SC_CODE.exec(line);
|
|
597
|
-
if (codeMatch) {
|
|
598
|
-
codeFound = codeMatch[1];
|
|
599
|
-
severity = codeMatch[2];
|
|
600
|
-
message = codeMatch[3];
|
|
601
|
-
}
|
|
602
|
-
i++;
|
|
603
|
-
}
|
|
604
|
-
if (!codeFound)
|
|
605
|
-
continue;
|
|
606
|
-
const existing = groups.get(codeFound);
|
|
607
|
-
if (existing) {
|
|
608
|
-
existing.count++;
|
|
609
|
-
existing.files.add(file);
|
|
610
|
-
}
|
|
611
|
-
else {
|
|
612
|
-
groups.set(codeFound, {
|
|
613
|
-
code: codeFound,
|
|
614
|
-
severity,
|
|
615
|
-
message,
|
|
616
|
-
count: 1,
|
|
617
|
-
files: new Set([file]),
|
|
618
|
-
firstBlock: blockLines,
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
if (groups.size === 0)
|
|
623
|
-
return "";
|
|
624
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
625
|
-
const total = [...groups.values()].reduce((s, g) => s + g.count, 0);
|
|
626
|
-
parts.push(`(${total} issues, ${groups.size} unique codes)`);
|
|
627
|
-
const sorted = [...groups.values()].sort((a, b) => b.count - a.count);
|
|
628
|
-
for (const g of sorted) {
|
|
629
|
-
parts.push("");
|
|
630
|
-
parts.push(`${g.code} (${g.severity}): ${g.message} [×${g.count} in ${g.files.size} file${g.files.size > 1 ? "s" : ""}]`);
|
|
631
|
-
// Show first example block indented
|
|
632
|
-
for (const bl of g.firstBlock) {
|
|
633
|
-
parts.push(` ${bl}`);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
return parts.join("\n");
|
|
637
|
-
}
|
|
638
|
-
/** Compress generic line diagnostics (rubocop, stylelint, vale, markdownlint, etc.) */
|
|
639
|
-
function compressLineDiagnostics(lines) {
|
|
640
|
-
// Pattern: file:line:col: severity: message OR file:line: [code] message
|
|
641
|
-
const DIAG_RE = /^(.+?):(\d+)(?::(\d+))?:\s*(?:(error|warning|info|note|convention|refactor)\s*:?\s*)?(.+)$/;
|
|
642
|
-
const groups = new Map();
|
|
643
|
-
const nonDiagLines = [];
|
|
644
|
-
for (const line of lines) {
|
|
645
|
-
const m = DIAG_RE.exec(line);
|
|
646
|
-
if (!m) {
|
|
647
|
-
if (line.trim())
|
|
648
|
-
nonDiagLines.push(line);
|
|
649
|
-
continue;
|
|
650
|
-
}
|
|
651
|
-
const [, filePath, , , severity, message] = m;
|
|
652
|
-
// Normalize message: strip trailing whitespace and file-specific references
|
|
653
|
-
const normMsg = message
|
|
654
|
-
?.trim()
|
|
655
|
-
.replace(/`[^`]+`/g, "`<id>`")
|
|
656
|
-
.replace(/'\S+'/, "'<id>'");
|
|
657
|
-
const key = `${severity ?? "warning"}:${normMsg}`;
|
|
658
|
-
const existing = groups.get(key);
|
|
659
|
-
if (existing) {
|
|
660
|
-
existing.count++;
|
|
661
|
-
existing.files.add(filePath);
|
|
662
|
-
}
|
|
663
|
-
else {
|
|
664
|
-
groups.set(key, {
|
|
665
|
-
message: message?.trim() ?? "",
|
|
666
|
-
severity: severity ?? "warning",
|
|
667
|
-
count: 1,
|
|
668
|
-
files: new Set([filePath]),
|
|
669
|
-
firstLine: line,
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
if (groups.size === 0)
|
|
674
|
-
return "";
|
|
675
|
-
const parts = ["_shell_fmt:error_diagnostic"];
|
|
676
|
-
const total = [...groups.values()].reduce((s, g) => s + g.count, 0);
|
|
677
|
-
parts.push(`(${total} diagnostics, ${groups.size} unique messages)`);
|
|
678
|
-
const sorted = [...groups.values()].sort((a, b) => b.count - a.count);
|
|
679
|
-
for (const g of sorted.slice(0, 30)) {
|
|
680
|
-
const fileInfo = g.files.size > 3 ? `${g.files.size} files` : [...g.files].join(", ");
|
|
681
|
-
parts.push(g.count > 1
|
|
682
|
-
? `${g.severity}: ${g.message} [×${g.count} in ${fileInfo}]`
|
|
683
|
-
: g.firstLine);
|
|
684
|
-
}
|
|
685
|
-
if (sorted.length > 30) {
|
|
686
|
-
parts.push(`… ${sorted.length - 30} more unique messages`);
|
|
687
|
-
}
|
|
688
|
-
// Keep summary lines (non-diagnostic) at the end
|
|
689
|
-
const summaryLines = nonDiagLines.filter((l) => /\d+\s*(error|warning|problem|issue|offense)/i.test(l) ||
|
|
690
|
-
/^(✖|✗|Found|Total:)/i.test(l));
|
|
691
|
-
if (summaryLines.length > 0) {
|
|
692
|
-
parts.push("");
|
|
693
|
-
for (const s of summaryLines.slice(0, 5))
|
|
694
|
-
parts.push(s);
|
|
695
|
-
}
|
|
696
|
-
return parts.join("\n");
|
|
697
|
-
}
|
|
698
|
-
/** Legacy fallback: dedup consecutive identical frames + truncation. */
|
|
699
|
-
function compressFallback(lines) {
|
|
700
|
-
const out = [];
|
|
701
|
-
let prevFrame = "";
|
|
702
|
-
let repeatCount = 0;
|
|
703
|
-
const flushDup = () => {
|
|
704
|
-
if (repeatCount > 0) {
|
|
705
|
-
out.push(`…repeated_frame ×${repeatCount}`);
|
|
706
|
-
repeatCount = 0;
|
|
707
|
-
}
|
|
708
|
-
};
|
|
709
|
-
for (const line of lines) {
|
|
710
|
-
const trimmed = line.trimEnd();
|
|
711
|
-
const isFrame = /^\s*at\s/.test(trimmed);
|
|
712
|
-
if (isFrame && trimmed === prevFrame) {
|
|
713
|
-
repeatCount++;
|
|
714
|
-
continue;
|
|
715
|
-
}
|
|
716
|
-
flushDup();
|
|
717
|
-
prevFrame = isFrame ? trimmed : "";
|
|
718
|
-
out.push(trimmed);
|
|
719
|
-
}
|
|
720
|
-
flushDup();
|
|
721
|
-
const joined = out.join("\n");
|
|
722
|
-
const clipped = joined.length > 10_000
|
|
723
|
-
? `${joined.slice(0, 6000)}\n…error_mid_omitted…\n${joined.slice(-3000)}`
|
|
724
|
-
: joined;
|
|
725
|
-
return `_shell_fmt:error_diagnostic\n${clipped}`;
|
|
726
|
-
}
|
|
727
|
-
/**
|
|
728
|
-
* R1 — success short-circuit for linters/compilers. If the output has no
|
|
729
|
-
* "error" / "warning" / "FAIL" markers AND matches a clean-run pattern,
|
|
730
|
-
* collapse to a one-line summary.
|
|
731
|
-
*/
|
|
732
|
-
const DIAG_ERROR_RE = /\b(error|warning|FAIL(?:ED)?|panic)\b/i;
|
|
733
|
-
const DIAG_SUCCESS_PATTERNS = [
|
|
734
|
-
/\b0 (?:errors?|problems?)\b/i,
|
|
735
|
-
/\bno (?:errors|problems|issues|violations|warnings) (?:found|reported)\b/i,
|
|
736
|
-
/\ball checks pass(?:ed)?\b/i,
|
|
737
|
-
/\bnothing to report\b/i,
|
|
738
|
-
/\bclean\b/i,
|
|
739
|
-
];
|
|
740
|
-
function tryDiagShortCircuit(raw, command) {
|
|
741
|
-
if (DIAG_ERROR_RE.test(raw))
|
|
742
|
-
return null;
|
|
743
|
-
for (const pat of DIAG_SUCCESS_PATTERNS) {
|
|
744
|
-
const m = raw.match(pat);
|
|
745
|
-
if (m) {
|
|
746
|
-
const label = command?.split(/\s+/)[0] ?? "lint";
|
|
747
|
-
return `_shell_fmt:error_diagnostic\n${label} ok — ${m[0]}`;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
return null;
|
|
751
|
-
}
|
|
752
|
-
export function compressErrorDiagnostic(raw, command) {
|
|
753
|
-
const short = tryDiagShortCircuit(raw, command);
|
|
754
|
-
if (short)
|
|
755
|
-
return short;
|
|
756
|
-
const lines = raw.split("\n");
|
|
757
|
-
const format = detectErrorFormat(command, lines);
|
|
758
|
-
let result = "";
|
|
759
|
-
switch (format) {
|
|
760
|
-
case "tsc":
|
|
761
|
-
result = compressTscErrors(lines);
|
|
762
|
-
break;
|
|
763
|
-
case "eslint":
|
|
764
|
-
result = compressEslintErrors(lines);
|
|
765
|
-
break;
|
|
766
|
-
case "node_stack":
|
|
767
|
-
result = compressNodeStack(lines);
|
|
768
|
-
break;
|
|
769
|
-
case "gcc_clang":
|
|
770
|
-
result = compressGccClangErrors(lines);
|
|
771
|
-
break;
|
|
772
|
-
case "rustc":
|
|
773
|
-
result = compressRustcErrors(lines);
|
|
774
|
-
break;
|
|
775
|
-
case "go":
|
|
776
|
-
result = compressGoErrors(lines);
|
|
777
|
-
break;
|
|
778
|
-
case "python_tb":
|
|
779
|
-
result = compressPythonErrors(lines);
|
|
780
|
-
break;
|
|
781
|
-
case "javac":
|
|
782
|
-
result = compressJavacErrors(lines);
|
|
783
|
-
break;
|
|
784
|
-
case "shellcheck":
|
|
785
|
-
result = compressShellcheck(lines);
|
|
786
|
-
break;
|
|
787
|
-
case "line_diagnostic":
|
|
788
|
-
result = compressLineDiagnostics(lines);
|
|
789
|
-
break;
|
|
790
|
-
case "biome":
|
|
791
|
-
case "generic":
|
|
792
|
-
break;
|
|
793
|
-
}
|
|
794
|
-
// Fall back if format-specific parser returned nothing
|
|
795
|
-
return result || compressFallback(lines);
|
|
796
|
-
}
|