@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.
- package/README.md +70 -194
- package/dist/cli.js +39149 -36991
- package/package.json +9 -2
- package/dist/__tests__/architecture-guard.test.js +0 -122
- package/dist/__tests__/arg-validator.test.js +0 -205
- package/dist/__tests__/ast-extractor.test.js +0 -203
- package/dist/__tests__/auto-bootstrap.test.js +0 -280
- package/dist/__tests__/background-indexer.test.js +0 -228
- package/dist/__tests__/blast-radius-engine.test.js +0 -200
- package/dist/__tests__/bridge-isolation.test.js +0 -37
- package/dist/__tests__/budget-enforcer.test.js +0 -53
- package/dist/__tests__/cfg-test-detection-perf.test.js +0 -82
- package/dist/__tests__/change-narrative.test.js +0 -190
- package/dist/__tests__/check-commit.test.js +0 -258
- package/dist/__tests__/checksum.test.js +0 -34
- package/dist/__tests__/commit-watcher.test.js +0 -154
- package/dist/__tests__/community-detection.test.js +0 -179
- package/dist/__tests__/community-tools.test.js +0 -299
- package/dist/__tests__/components.test.js +0 -449
- package/dist/__tests__/compression-log.test.js +0 -174
- package/dist/__tests__/compression-quality-monitor.test.js +0 -40
- package/dist/__tests__/config-healer.test.js +0 -165
- package/dist/__tests__/context-ledger.test.js +0 -58
- package/dist/__tests__/convention-detector.test.js +0 -99
- package/dist/__tests__/convention-learner.test.js +0 -86
- package/dist/__tests__/correction-detector.test.js +0 -330
- package/dist/__tests__/daemon-autostart-install.test.js +0 -283
- package/dist/__tests__/daemon-bridge.test.js +0 -222
- package/dist/__tests__/daemon-dashboard.test.js +0 -202
- package/dist/__tests__/daemon-registry.test.js +0 -240
- package/dist/__tests__/daemon-supervisor.test.js +0 -318
- package/dist/__tests__/daemon-version-check.test.js +0 -275
- package/dist/__tests__/decision-point-detector.test.js +0 -98
- package/dist/__tests__/deep-link.test.js +0 -143
- package/dist/__tests__/disallowed-tools.test.js +0 -115
- package/dist/__tests__/drift-tracker.test.js +0 -582
- package/dist/__tests__/durability-scorer.test.js +0 -152
- package/dist/__tests__/efficiency-tracker.test.js +0 -65
- package/dist/__tests__/enrich.test.js +0 -144
- package/dist/__tests__/entity-rewind.test.js +0 -248
- package/dist/__tests__/ephemeral.test.js +0 -111
- package/dist/__tests__/exploration-cost.test.js +0 -93
- package/dist/__tests__/fact-generator.test.js +0 -197
- package/dist/__tests__/file-l0-graph.test.js +0 -244
- package/dist/__tests__/file-logger.test.js +0 -82
- package/dist/__tests__/file-outline.test.js +0 -141
- package/dist/__tests__/file-read-protocol.test.js +0 -188
- package/dist/__tests__/format-encoder.test.js +0 -233
- package/dist/__tests__/git-attribution.test.js +0 -259
- package/dist/__tests__/graph-temporal-joiner.test.js +0 -219
- package/dist/__tests__/health-grade-enhanced.test.js +0 -138
- package/dist/__tests__/health-map-data.test.js +0 -173
- package/dist/__tests__/helpers/mcp-harness.js +0 -45
- package/dist/__tests__/helpers/mcp-harness.test.js +0 -68
- package/dist/__tests__/hook-dedup.test.js +0 -112
- package/dist/__tests__/hook-runner.test.js +0 -253
- package/dist/__tests__/indexer-cfg.test.js +0 -185
- package/dist/__tests__/indexer-cross-file.test.js +0 -172
- package/dist/__tests__/indexer-extraction.test.js +0 -245
- package/dist/__tests__/indexer-incremental.test.js +0 -232
- package/dist/__tests__/indexer-language-expansion.test.js +0 -165
- package/dist/__tests__/init-push.test.js +0 -131
- package/dist/__tests__/instruction-writer.test.js +0 -179
- package/dist/__tests__/intelligence-integration.test.js +0 -217
- package/dist/__tests__/intent-correlator.test.js +0 -175
- package/dist/__tests__/intent-detector.test.js +0 -235
- package/dist/__tests__/intent-encoder.test.js +0 -167
- package/dist/__tests__/java-build-tool-detection.test.js +0 -174
- package/dist/__tests__/layer3-sprint-q.test.js +0 -160
- package/dist/__tests__/layer3-sprint-r.test.js +0 -91
- package/dist/__tests__/layer3-sprint-s.test.js +0 -183
- package/dist/__tests__/layer3-sprint-t.test.js +0 -201
- package/dist/__tests__/layer3-sprint-u.test.js +0 -174
- package/dist/__tests__/layer4-sprint-ba2.test.js +0 -354
- package/dist/__tests__/layer4-sprint-ba4.test.js +0 -84
- package/dist/__tests__/layer4-sprint-vs.test.js +0 -105
- package/dist/__tests__/ledger-chains.test.js +0 -162
- package/dist/__tests__/lifecycle-machine.test.js +0 -226
- package/dist/__tests__/local-chat-provider.test.js +0 -170
- package/dist/__tests__/local-convention-detector.test.js +0 -308
- package/dist/__tests__/local-embeddings.test.js +0 -422
- package/dist/__tests__/local-graph.test.js +0 -540
- package/dist/__tests__/local-indexer.test.js +0 -228
- package/dist/__tests__/local-intelligence-l3.test.js +0 -332
- package/dist/__tests__/local-llm.test.js +0 -253
- package/dist/__tests__/local-mode-offline.test.js +0 -187
- package/dist/__tests__/local-mode-stats.test.js +0 -273
- package/dist/__tests__/local-mode-tui.test.js +0 -343
- package/dist/__tests__/local-parse.test.js +0 -199
- package/dist/__tests__/log-tailer.test.js +0 -208
- package/dist/__tests__/loop-breaker.test.js +0 -276
- package/dist/__tests__/loop-miner.test.js +0 -226
- package/dist/__tests__/mcp-config.test.js +0 -126
- package/dist/__tests__/mcp-content-json.test.js +0 -10
- package/dist/__tests__/mcp-envelope.test.js +0 -124
- package/dist/__tests__/metrics-store.test.js +0 -223
- package/dist/__tests__/native-watcher.test.js +0 -191
- package/dist/__tests__/navigation-hooks-agent-aware.test.js +0 -145
- package/dist/__tests__/negative-knowledge.test.js +0 -116
- package/dist/__tests__/network-boundary.test.js +0 -190
- package/dist/__tests__/network-firewall.test.js +0 -112
- package/dist/__tests__/nudge-invariants.test.js +0 -160
- package/dist/__tests__/nudge-v2.test.js +0 -225
- package/dist/__tests__/offline-rewind.test.js +0 -251
- package/dist/__tests__/open-threads.test.js +0 -89
- package/dist/__tests__/output-compressor.test.js +0 -93
- package/dist/__tests__/pending-violations.test.js +0 -112
- package/dist/__tests__/persistence-effectiveness.test.js +0 -143
- package/dist/__tests__/provider-factory.test.js +0 -42
- package/dist/__tests__/providers.test.js +0 -24
- package/dist/__tests__/proxy.test.js +0 -314
- package/dist/__tests__/query-router.test.js +0 -1018
- package/dist/__tests__/reasoning-quality-route.test.js +0 -138
- package/dist/__tests__/redactor.test.js +0 -120
- package/dist/__tests__/resource-monitor.test.js +0 -57
- package/dist/__tests__/response-envelope.test.js +0 -100
- package/dist/__tests__/risk-classifier.test.js +0 -101
- package/dist/__tests__/risk-signal-scope.test.js +0 -75
- package/dist/__tests__/rule-evaluator.test.js +0 -280
- package/dist/__tests__/scip-decoder.test.js +0 -49
- package/dist/__tests__/scip-downloader.test.js +0 -201
- package/dist/__tests__/scip-merger.test.js +0 -103
- package/dist/__tests__/search-index.test.js +0 -422
- package/dist/__tests__/semantic-enrichment.test.js +0 -360
- package/dist/__tests__/session-brief-builder.test.js +0 -187
- package/dist/__tests__/session-context.test.js +0 -221
- package/dist/__tests__/session-continuity.test.js +0 -144
- package/dist/__tests__/session-dedup.test.js +0 -74
- package/dist/__tests__/session-event-wiring.test.js +0 -206
- package/dist/__tests__/session-events.test.js +0 -149
- package/dist/__tests__/session-legend.test.js +0 -20
- package/dist/__tests__/session-persistence.test.js +0 -131
- package/dist/__tests__/session-resume-block.test.js +0 -107
- package/dist/__tests__/session-resume.test.js +0 -97
- package/dist/__tests__/session-summary-writer.test.js +0 -134
- package/dist/__tests__/shadow-ledger.test.js +0 -203
- package/dist/__tests__/shell-classifier.test.js +0 -151
- package/dist/__tests__/shell-compression-floor.test.js +0 -189
- package/dist/__tests__/shell-compression-v2.test.js +0 -339
- package/dist/__tests__/shell-compressor.test.js +0 -35
- package/dist/__tests__/shell-hooks.test.js +0 -128
- package/dist/__tests__/shell-strategies.test.js +0 -644
- package/dist/__tests__/shell-tee.test.js +0 -133
- package/dist/__tests__/signal-dedup.test.js +0 -158
- package/dist/__tests__/signal-reinforcer.test.js +0 -77
- package/dist/__tests__/signal-scorer.test.js +0 -251
- package/dist/__tests__/signal-show-store.test.js +0 -108
- package/dist/__tests__/smart-truncate.test.js +0 -215
- package/dist/__tests__/snapshot-v2.test.js +0 -113
- package/dist/__tests__/sprint-l1-local-mode.test.js +0 -130
- package/dist/__tests__/sprint-l10-boot.test.js +0 -220
- package/dist/__tests__/sprint-l9-offline-commands.test.js +0 -189
- package/dist/__tests__/sprint-q-persistent-context.test.js +0 -198
- package/dist/__tests__/sprint-s1-wiring.test.js +0 -215
- package/dist/__tests__/sprint-s2-wiring.test.js +0 -256
- package/dist/__tests__/sprint-s3-wiring.test.js +0 -195
- package/dist/__tests__/sprint-s4-wiring.test.js +0 -213
- package/dist/__tests__/sprint-s6-hooks.test.js +0 -222
- package/dist/__tests__/sprint-s7-persistent.test.js +0 -263
- package/dist/__tests__/sprint-s8-value.test.js +0 -167
- package/dist/__tests__/sprint-s9-behavioral.test.js +0 -179
- package/dist/__tests__/sprint3-intelligence.test.js +0 -297
- package/dist/__tests__/sprint5-mcp-server.test.js +0 -136
- package/dist/__tests__/startup-display.test.js +0 -302
- package/dist/__tests__/startup-log-file.test.js +0 -97
- package/dist/__tests__/stash-manager.test.js +0 -229
- package/dist/__tests__/state-detector.test.js +0 -92
- package/dist/__tests__/status-dashboard.test.js +0 -142
- package/dist/__tests__/temporal-facts.test.js +0 -292
- package/dist/__tests__/temporal-routes.test.js +0 -142
- package/dist/__tests__/test-detector.test.js +0 -174
- package/dist/__tests__/theme.test.js +0 -72
- package/dist/__tests__/timeline-agents.test.js +0 -122
- package/dist/__tests__/timeline-bootstrap.test.js +0 -176
- package/dist/__tests__/timeline-filters.test.js +0 -193
- package/dist/__tests__/timeline-markers.test.js +0 -151
- package/dist/__tests__/timeline-routes.test.js +0 -156
- package/dist/__tests__/timeline-store.test.js +0 -171
- package/dist/__tests__/token-counter.test.js +0 -86
- package/dist/__tests__/token-estimator.test.js +0 -96
- package/dist/__tests__/token-flow-api.test.js +0 -239
- package/dist/__tests__/token-flow-instrumentation.test.js +0 -437
- package/dist/__tests__/token-flow-persistence.test.js +0 -356
- package/dist/__tests__/token-flow-routes.test.js +0 -199
- package/dist/__tests__/token-flow.test.js +0 -695
- package/dist/__tests__/tool-clusters.test.js +0 -177
- package/dist/__tests__/transport-mux.test.js +0 -283
- package/dist/__tests__/turn-segmenter.test.js +0 -166
- package/dist/__tests__/uninstall.test.js +0 -141
- package/dist/__tests__/warm-start-policy.test.js +0 -271
- package/dist/__tests__/wire-cap-nudge.test.js +0 -77
- package/dist/__tests__/worker-pool.test.js +0 -101
- package/dist/ui/assets/index-7gl3mIuY.css +0 -1
- package/dist/ui/assets/index-CX4FCWGT.js +0 -10
- package/dist/ui/assets/rolldown-runtime-S-ySWqyJ.js +0 -1
- package/dist/ui/assets/vis-network-NIJHUFI3.js +0 -908
- package/dist/ui/fonts/jetbrains-mono-latin-400-normal.woff +0 -0
- package/dist/ui/icon-wordmark.png +0 -0
- package/dist/ui/icon-wordmark.svg +0 -30
- package/dist/ui/icon.png +0 -0
- package/dist/ui/icon.svg +0 -25
- package/dist/ui/index.html +0 -15
- package/dist/ui/prototype-sandbox/index.html +0 -257
- package/dist/ui/screenshots/activity.png +0 -0
- package/dist/ui/screenshots/code-base-intelligence.png +0 -0
- package/dist/ui/screenshots/dashboard.png +0 -0
- package/dist/ui/screenshots/project-memory.png +0 -0
- package/dist/ui/screenshots/reasoning-quality.png +0 -0
- package/dist/ui/screenshots/reasoning-session.png +0 -0
- package/dist/ui/screenshots/token-session.png +0 -0
- package/dist/ui/screenshots/token-trace-main.png +0 -0
- package/dist/ui/screenshots/token-turn.png +0 -0
- package/dist/ui/unerr-wordmark.png +0 -0
- package/dist/ui/unerr-wordmark.svg +0 -9
- package/dist/ui/unerr.png +0 -0
- package/dist/ui/unerr.svg +0 -25
- package/dist/ui/web-app-manifest-192x192.png +0 -0
- package/dist/ui/web-app-manifest-512x512.png +0 -0
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createDurabilityScorer } from "../intelligence/durability-scorer.js";
|
|
3
|
-
describe("createDurabilityScorer", () => {
|
|
4
|
-
it("computes durability for entities", () => {
|
|
5
|
-
const scorer = createDurabilityScorer();
|
|
6
|
-
const entries = [
|
|
7
|
-
{
|
|
8
|
-
id: "e1",
|
|
9
|
-
ts: "2026-04-28T10:00:00Z",
|
|
10
|
-
tool: "sync_local_diff",
|
|
11
|
-
args_summary: { files: ["src/auth.ts"] },
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
id: "e2",
|
|
15
|
-
ts: "2026-04-29T10:00:00Z",
|
|
16
|
-
tool: "sync_local_diff",
|
|
17
|
-
args_summary: { files: ["src/auth.ts"] },
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
id: "e3",
|
|
21
|
-
ts: "2026-04-30T10:00:00Z",
|
|
22
|
-
tool: "sync_local_diff",
|
|
23
|
-
args_summary: { files: ["src/auth.ts"] },
|
|
24
|
-
},
|
|
25
|
-
];
|
|
26
|
-
const scores = scorer.computeScores(entries);
|
|
27
|
-
const score = scores.get("src/auth.ts");
|
|
28
|
-
expect(score).toBeDefined();
|
|
29
|
-
expect(score?.modificationCount).toBe(3);
|
|
30
|
-
expect(score?.score).toBeGreaterThan(0);
|
|
31
|
-
expect(score?.score).toBeLessThanOrEqual(1);
|
|
32
|
-
});
|
|
33
|
-
it("entity modified 3x in 3 sessions with 1 day survival has low durability", () => {
|
|
34
|
-
const scorer = createDurabilityScorer();
|
|
35
|
-
const day = 24 * 60 * 60 * 1000;
|
|
36
|
-
const now = Date.now();
|
|
37
|
-
const entries = [
|
|
38
|
-
{
|
|
39
|
-
id: "e1",
|
|
40
|
-
ts: new Date(now - 3 * day).toISOString(),
|
|
41
|
-
tool: "sync_local_diff",
|
|
42
|
-
args_summary: { files: ["src/flaky.ts"] },
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
id: "e2",
|
|
46
|
-
ts: new Date(now - 2 * day).toISOString(),
|
|
47
|
-
tool: "sync_local_diff",
|
|
48
|
-
args_summary: { files: ["src/flaky.ts"] },
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
id: "e3",
|
|
52
|
-
ts: new Date(now - 1 * day).toISOString(),
|
|
53
|
-
tool: "sync_local_diff",
|
|
54
|
-
args_summary: { files: ["src/flaky.ts"] },
|
|
55
|
-
},
|
|
56
|
-
];
|
|
57
|
-
scorer.computeScores(entries);
|
|
58
|
-
const score = scorer.getScore("src/flaky.ts");
|
|
59
|
-
expect(score).toBeDefined();
|
|
60
|
-
expect(score?.score).toBeLessThan(0.7);
|
|
61
|
-
});
|
|
62
|
-
it("stable entity has high durability", () => {
|
|
63
|
-
const scorer = createDurabilityScorer();
|
|
64
|
-
const day = 24 * 60 * 60 * 1000;
|
|
65
|
-
const now = Date.now();
|
|
66
|
-
const entries = [
|
|
67
|
-
{
|
|
68
|
-
id: "e1",
|
|
69
|
-
ts: new Date(now - 30 * day).toISOString(),
|
|
70
|
-
tool: "sync_local_diff",
|
|
71
|
-
args_summary: { files: ["src/stable.ts"] },
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
id: "e2",
|
|
75
|
-
ts: new Date(now - 1 * day).toISOString(),
|
|
76
|
-
tool: "get_function",
|
|
77
|
-
args_summary: { key: "src/other.ts" },
|
|
78
|
-
},
|
|
79
|
-
];
|
|
80
|
-
scorer.computeScores(entries);
|
|
81
|
-
const score = scorer.getScore("src/stable.ts");
|
|
82
|
-
expect(score).toBeDefined();
|
|
83
|
-
expect(score?.score).toBeGreaterThanOrEqual(0.9);
|
|
84
|
-
});
|
|
85
|
-
it("getTopUnstable returns lowest scores", () => {
|
|
86
|
-
const scorer = createDurabilityScorer();
|
|
87
|
-
const now = Date.now();
|
|
88
|
-
const day = 24 * 60 * 60 * 1000;
|
|
89
|
-
const entries = [
|
|
90
|
-
{
|
|
91
|
-
id: "e1",
|
|
92
|
-
ts: new Date(now - 10 * day).toISOString(),
|
|
93
|
-
tool: "sync_local_diff",
|
|
94
|
-
args_summary: { files: ["src/stable.ts"] },
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
id: "e2",
|
|
98
|
-
ts: new Date(now - 3 * day).toISOString(),
|
|
99
|
-
tool: "sync_local_diff",
|
|
100
|
-
args_summary: { files: ["src/flaky.ts"] },
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
id: "e3",
|
|
104
|
-
ts: new Date(now - 2 * day).toISOString(),
|
|
105
|
-
tool: "sync_local_diff",
|
|
106
|
-
args_summary: { files: ["src/flaky.ts"] },
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
id: "e4",
|
|
110
|
-
ts: new Date(now - 1 * day).toISOString(),
|
|
111
|
-
tool: "sync_local_diff",
|
|
112
|
-
args_summary: { files: ["src/flaky.ts"] },
|
|
113
|
-
},
|
|
114
|
-
];
|
|
115
|
-
scorer.computeScores(entries);
|
|
116
|
-
const unstable = scorer.getTopUnstable(1);
|
|
117
|
-
expect(unstable[0]?.entityKey).toBe("src/flaky.ts");
|
|
118
|
-
});
|
|
119
|
-
it("getTopDurable returns highest scores", () => {
|
|
120
|
-
const scorer = createDurabilityScorer();
|
|
121
|
-
const now = Date.now();
|
|
122
|
-
const day = 24 * 60 * 60 * 1000;
|
|
123
|
-
const entries = [
|
|
124
|
-
{
|
|
125
|
-
id: "e1",
|
|
126
|
-
ts: new Date(now - 10 * day).toISOString(),
|
|
127
|
-
tool: "sync_local_diff",
|
|
128
|
-
args_summary: { files: ["src/stable.ts"] },
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
id: "e2",
|
|
132
|
-
ts: new Date(now - 1 * day).toISOString(),
|
|
133
|
-
tool: "sync_local_diff",
|
|
134
|
-
args_summary: { files: ["src/flaky.ts"] },
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
id: "e3",
|
|
138
|
-
ts: new Date(now - 0.5 * day).toISOString(),
|
|
139
|
-
tool: "sync_local_diff",
|
|
140
|
-
args_summary: { files: ["src/flaky.ts"] },
|
|
141
|
-
},
|
|
142
|
-
];
|
|
143
|
-
scorer.computeScores(entries);
|
|
144
|
-
const durable = scorer.getTopDurable(1);
|
|
145
|
-
expect(durable[0]?.entityKey).toBe("src/stable.ts");
|
|
146
|
-
});
|
|
147
|
-
it("handles empty entries", () => {
|
|
148
|
-
const scorer = createDurabilityScorer();
|
|
149
|
-
const scores = scorer.computeScores([]);
|
|
150
|
-
expect(scores.size).toBe(0);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createEfficiencyTracker } from "../proxy/efficiency-tracker.js";
|
|
3
|
-
describe("createEfficiencyTracker", () => {
|
|
4
|
-
it("starts with zero state", () => {
|
|
5
|
-
const tracker = createEfficiencyTracker();
|
|
6
|
-
const snap = tracker.getSnapshot();
|
|
7
|
-
expect(snap.totalCalls).toBe(0);
|
|
8
|
-
expect(snap.originalTokens).toBe(0);
|
|
9
|
-
expect(snap.deliveredTokens).toBe(0);
|
|
10
|
-
expect(snap.efficiency).toBe(0);
|
|
11
|
-
});
|
|
12
|
-
it("records token pairs and computes efficiency", () => {
|
|
13
|
-
const tracker = createEfficiencyTracker();
|
|
14
|
-
tracker.record(1000, 600);
|
|
15
|
-
expect(tracker.getEfficiency()).toBe(40);
|
|
16
|
-
expect(tracker.getSavedTokens()).toBe(400);
|
|
17
|
-
});
|
|
18
|
-
it("accumulates across multiple calls", () => {
|
|
19
|
-
const tracker = createEfficiencyTracker();
|
|
20
|
-
tracker.record(1000, 700);
|
|
21
|
-
tracker.record(2000, 800);
|
|
22
|
-
tracker.record(500, 500);
|
|
23
|
-
const snap = tracker.getSnapshot();
|
|
24
|
-
expect(snap.totalCalls).toBe(3);
|
|
25
|
-
expect(snap.originalTokens).toBe(3500);
|
|
26
|
-
expect(snap.deliveredTokens).toBe(2000);
|
|
27
|
-
expect(snap.savedTokens).toBe(1500);
|
|
28
|
-
expect(snap.efficiency).toBe(43);
|
|
29
|
-
});
|
|
30
|
-
it("computes average savings per call", () => {
|
|
31
|
-
const tracker = createEfficiencyTracker();
|
|
32
|
-
tracker.record(1000, 500);
|
|
33
|
-
tracker.record(2000, 1000);
|
|
34
|
-
const snap = tracker.getSnapshot();
|
|
35
|
-
expect(snap.avgSavingsPerCall).toBe(750);
|
|
36
|
-
});
|
|
37
|
-
it("handles 10 calls with known values", () => {
|
|
38
|
-
const tracker = createEfficiencyTracker();
|
|
39
|
-
for (let i = 0; i < 10; i++) {
|
|
40
|
-
tracker.record(1000, 300);
|
|
41
|
-
}
|
|
42
|
-
expect(tracker.getEfficiency()).toBe(70);
|
|
43
|
-
expect(tracker.getSavedTokens()).toBe(7000);
|
|
44
|
-
expect(tracker.getSnapshot().totalCalls).toBe(10);
|
|
45
|
-
});
|
|
46
|
-
it("handles zero original tokens gracefully", () => {
|
|
47
|
-
const tracker = createEfficiencyTracker();
|
|
48
|
-
tracker.record(0, 0);
|
|
49
|
-
expect(tracker.getEfficiency()).toBe(0);
|
|
50
|
-
});
|
|
51
|
-
it("never returns negative savings", () => {
|
|
52
|
-
const tracker = createEfficiencyTracker();
|
|
53
|
-
tracker.record(100, 200);
|
|
54
|
-
expect(tracker.getSavedTokens()).toBe(0);
|
|
55
|
-
});
|
|
56
|
-
it("reset clears all state", () => {
|
|
57
|
-
const tracker = createEfficiencyTracker();
|
|
58
|
-
tracker.record(5000, 2000);
|
|
59
|
-
tracker.reset();
|
|
60
|
-
const snap = tracker.getSnapshot();
|
|
61
|
-
expect(snap.totalCalls).toBe(0);
|
|
62
|
-
expect(snap.originalTokens).toBe(0);
|
|
63
|
-
expect(snap.efficiency).toBe(0);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Sprint L8.2 — `unerr enrich` enrichment logic.
|
|
3
|
-
*
|
|
4
|
-
* Tests the prompt builder and response parser without requiring
|
|
5
|
-
* a real LLM or CozoDB instance.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, expect, it } from "vitest";
|
|
8
|
-
// Re-export internal functions for testing by importing the module
|
|
9
|
-
// and testing the prompt/parse logic through the public API patterns.
|
|
10
|
-
// Since buildEnrichmentPrompt and parseEnrichmentResponse are module-private,
|
|
11
|
-
// we test them indirectly through their behavior. We can also test them
|
|
12
|
-
// by extracting them — but for now, test the patterns they implement.
|
|
13
|
-
describe("Enrichment prompt builder (L8.2)", () => {
|
|
14
|
-
it("builds a structured prompt for entity batch", () => {
|
|
15
|
-
// Verify the prompt format expected by parseEnrichmentResponse
|
|
16
|
-
const entities = [
|
|
17
|
-
{
|
|
18
|
-
key: "fn:auth:login",
|
|
19
|
-
kind: "function",
|
|
20
|
-
name: "login",
|
|
21
|
-
filePath: "src/auth.ts",
|
|
22
|
-
signature: "login(user: User): Promise<Token>",
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
key: "cls:auth:AuthService",
|
|
26
|
-
kind: "class",
|
|
27
|
-
name: "AuthService",
|
|
28
|
-
filePath: "src/auth.ts",
|
|
29
|
-
signature: "class AuthService",
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
// The prompt should contain entity info
|
|
33
|
-
const entityBlock = entities
|
|
34
|
-
.map((e, i) => `[${i + 1}] ${e.kind} "${e.name}" in ${e.filePath}\n Signature: ${e.signature || "(none)"}`)
|
|
35
|
-
.join("\n");
|
|
36
|
-
expect(entityBlock).toContain('[1] function "login"');
|
|
37
|
-
expect(entityBlock).toContain('[2] class "AuthService"');
|
|
38
|
-
expect(entityBlock).toContain("src/auth.ts");
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
describe("Enrichment response parser (L8.2)", () => {
|
|
42
|
-
// Mirror the parseEnrichmentResponse logic
|
|
43
|
-
function parseEnrichmentResponse(text, count) {
|
|
44
|
-
const jsonMatch = text.match(/\[[\s\S]*\]/);
|
|
45
|
-
if (!jsonMatch) {
|
|
46
|
-
return Array.from({ length: count }, () => ({
|
|
47
|
-
purpose: "",
|
|
48
|
-
taxonomy: "unknown",
|
|
49
|
-
feature_area: "unknown",
|
|
50
|
-
}));
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
54
|
-
const results = [];
|
|
55
|
-
for (let i = 0; i < count; i++) {
|
|
56
|
-
const entry = parsed.find((p) => p.index === i + 1) ?? parsed[i] ?? {};
|
|
57
|
-
results.push({
|
|
58
|
-
purpose: entry.purpose ?? "",
|
|
59
|
-
taxonomy: entry.taxonomy ?? "unknown",
|
|
60
|
-
feature_area: entry.feature_area ?? "unknown",
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
return results;
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
return Array.from({ length: count }, () => ({
|
|
67
|
-
purpose: "",
|
|
68
|
-
taxonomy: "unknown",
|
|
69
|
-
feature_area: "unknown",
|
|
70
|
-
}));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
it("parses well-formed JSON array response", () => {
|
|
74
|
-
const text = JSON.stringify([
|
|
75
|
-
{
|
|
76
|
-
index: 1,
|
|
77
|
-
purpose: "Handles user login",
|
|
78
|
-
taxonomy: "authentication",
|
|
79
|
-
feature_area: "user-auth",
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
index: 2,
|
|
83
|
-
purpose: "Manages auth state",
|
|
84
|
-
taxonomy: "authentication",
|
|
85
|
-
feature_area: "user-auth",
|
|
86
|
-
},
|
|
87
|
-
]);
|
|
88
|
-
const results = parseEnrichmentResponse(text, 2);
|
|
89
|
-
expect(results).toHaveLength(2);
|
|
90
|
-
expect(results[0]?.purpose).toBe("Handles user login");
|
|
91
|
-
expect(results[0]?.taxonomy).toBe("authentication");
|
|
92
|
-
expect(results[1]?.feature_area).toBe("user-auth");
|
|
93
|
-
});
|
|
94
|
-
it("handles markdown-wrapped JSON", () => {
|
|
95
|
-
const text = '```json\n[{"index": 1, "purpose": "Test function", "taxonomy": "testing", "feature_area": "unit-tests"}]\n```';
|
|
96
|
-
const results = parseEnrichmentResponse(text, 1);
|
|
97
|
-
expect(results).toHaveLength(1);
|
|
98
|
-
expect(results[0]?.purpose).toBe("Test function");
|
|
99
|
-
expect(results[0]?.taxonomy).toBe("testing");
|
|
100
|
-
});
|
|
101
|
-
it("returns defaults for unparseable response", () => {
|
|
102
|
-
const results = parseEnrichmentResponse("Sorry, I cannot help with that.", 3);
|
|
103
|
-
expect(results).toHaveLength(3);
|
|
104
|
-
expect(results[0]?.purpose).toBe("");
|
|
105
|
-
expect(results[0]?.taxonomy).toBe("unknown");
|
|
106
|
-
expect(results[0]?.feature_area).toBe("unknown");
|
|
107
|
-
});
|
|
108
|
-
it("handles partial responses with missing fields", () => {
|
|
109
|
-
const text = JSON.stringify([
|
|
110
|
-
{ index: 1, purpose: "Does something" },
|
|
111
|
-
{ index: 2, taxonomy: "data-access" },
|
|
112
|
-
]);
|
|
113
|
-
const results = parseEnrichmentResponse(text, 2);
|
|
114
|
-
expect(results[0]?.purpose).toBe("Does something");
|
|
115
|
-
expect(results[0]?.taxonomy).toBe("unknown"); // missing → default
|
|
116
|
-
expect(results[1]?.purpose).toBe(""); // missing → default
|
|
117
|
-
expect(results[1]?.taxonomy).toBe("data-access");
|
|
118
|
-
});
|
|
119
|
-
it("handles fewer results than requested", () => {
|
|
120
|
-
const text = JSON.stringify([
|
|
121
|
-
{
|
|
122
|
-
index: 1,
|
|
123
|
-
purpose: "Only one result",
|
|
124
|
-
taxonomy: "misc",
|
|
125
|
-
feature_area: "core",
|
|
126
|
-
},
|
|
127
|
-
]);
|
|
128
|
-
const results = parseEnrichmentResponse(text, 3);
|
|
129
|
-
expect(results).toHaveLength(3);
|
|
130
|
-
expect(results[0]?.purpose).toBe("Only one result");
|
|
131
|
-
expect(results[1]?.purpose).toBe(""); // fallback
|
|
132
|
-
expect(results[2]?.purpose).toBe(""); // fallback
|
|
133
|
-
});
|
|
134
|
-
it("handles index-based matching", () => {
|
|
135
|
-
// Response with out-of-order indices
|
|
136
|
-
const text = JSON.stringify([
|
|
137
|
-
{ index: 2, purpose: "Second", taxonomy: "b", feature_area: "b" },
|
|
138
|
-
{ index: 1, purpose: "First", taxonomy: "a", feature_area: "a" },
|
|
139
|
-
]);
|
|
140
|
-
const results = parseEnrichmentResponse(text, 2);
|
|
141
|
-
expect(results[0]?.purpose).toBe("First");
|
|
142
|
-
expect(results[1]?.purpose).toBe("Second");
|
|
143
|
-
});
|
|
144
|
-
});
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sprint 7.4: Entity-level rewind tests.
|
|
3
|
-
*/
|
|
4
|
-
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import { describe, expect, it } from "vitest";
|
|
8
|
-
import { revertEntity } from "../tracking/entity-rewind.js";
|
|
9
|
-
/** Minimal mock CozoGraphStore with drift overlay backed by a Map. */
|
|
10
|
-
function createMockGraph(driftEntities = []) {
|
|
11
|
-
const overlay = new Map();
|
|
12
|
-
for (const e of driftEntities) {
|
|
13
|
-
overlay.set(e.key, e);
|
|
14
|
-
}
|
|
15
|
-
const removed = [];
|
|
16
|
-
return {
|
|
17
|
-
removed,
|
|
18
|
-
db: {
|
|
19
|
-
run: async (query, params) => {
|
|
20
|
-
// Simulate drift_overlay lookup by name (+ optional file_path filter)
|
|
21
|
-
const name = params?.name;
|
|
22
|
-
const fp = params?.fp;
|
|
23
|
-
const matches = [];
|
|
24
|
-
for (const [, e] of overlay) {
|
|
25
|
-
if (name && e.name !== name)
|
|
26
|
-
continue;
|
|
27
|
-
if (fp && e.file_path !== fp)
|
|
28
|
-
continue;
|
|
29
|
-
matches.push(e);
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
rows: matches.map((e) => [
|
|
33
|
-
e.key,
|
|
34
|
-
e.name,
|
|
35
|
-
e.kind,
|
|
36
|
-
e.signature,
|
|
37
|
-
e.body,
|
|
38
|
-
e.file_path,
|
|
39
|
-
e.line_start,
|
|
40
|
-
e.line_end,
|
|
41
|
-
e.content_hash,
|
|
42
|
-
e.drift_status,
|
|
43
|
-
e.intent_id,
|
|
44
|
-
e.modified_at,
|
|
45
|
-
e.origin,
|
|
46
|
-
e.previous_body,
|
|
47
|
-
e.previous_signature,
|
|
48
|
-
]),
|
|
49
|
-
};
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
removeDriftEntity: (key) => {
|
|
53
|
-
overlay.delete(key);
|
|
54
|
-
removed.push(key);
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
function makeDrift(overrides) {
|
|
59
|
-
return {
|
|
60
|
-
kind: "function",
|
|
61
|
-
signature: "()",
|
|
62
|
-
body: "",
|
|
63
|
-
file_path: "src/test.ts",
|
|
64
|
-
line_start: 1,
|
|
65
|
-
line_end: 3,
|
|
66
|
-
content_hash: "abc",
|
|
67
|
-
drift_status: "modified",
|
|
68
|
-
intent_id: "",
|
|
69
|
-
modified_at: new Date().toISOString(),
|
|
70
|
-
origin: "human",
|
|
71
|
-
previous_body: "",
|
|
72
|
-
previous_signature: "",
|
|
73
|
-
...overrides,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
describe("Entity-level rewind", () => {
|
|
77
|
-
it("returns error when entity not found in drift overlay", async () => {
|
|
78
|
-
const graph = createMockGraph();
|
|
79
|
-
const result = await revertEntity("nonExistent", graph, "/tmp");
|
|
80
|
-
expect(result.reverted).toBe(false);
|
|
81
|
-
expect(result.error).toContain("No drifted entity found");
|
|
82
|
-
});
|
|
83
|
-
it("reverts a MODIFIED entity by restoring previous_body", async () => {
|
|
84
|
-
const projectRoot = mkdtempSync(join(tmpdir(), "rewind-"));
|
|
85
|
-
const srcDir = join(projectRoot, "src");
|
|
86
|
-
mkdirSync(srcDir, { recursive: true });
|
|
87
|
-
// Write the current (modified) file
|
|
88
|
-
const fileContent = [
|
|
89
|
-
"// header",
|
|
90
|
-
"function doStuff() {",
|
|
91
|
-
" return 'modified';",
|
|
92
|
-
"}",
|
|
93
|
-
"// footer",
|
|
94
|
-
].join("\n");
|
|
95
|
-
writeFileSync(join(srcDir, "test.ts"), fileContent);
|
|
96
|
-
const graph = createMockGraph([
|
|
97
|
-
makeDrift({
|
|
98
|
-
key: "k1",
|
|
99
|
-
name: "doStuff",
|
|
100
|
-
file_path: "src/test.ts",
|
|
101
|
-
body: "function doStuff() {\n return 'modified';\n}",
|
|
102
|
-
line_start: 2,
|
|
103
|
-
line_end: 4,
|
|
104
|
-
drift_status: "modified",
|
|
105
|
-
previous_body: "function doStuff() {\n return 'original';\n}",
|
|
106
|
-
}),
|
|
107
|
-
]);
|
|
108
|
-
const result = await revertEntity("doStuff", graph, projectRoot);
|
|
109
|
-
expect(result.reverted).toBe(true);
|
|
110
|
-
expect(result.file).toBe("src/test.ts");
|
|
111
|
-
expect(result.drift_status).toBe("modified");
|
|
112
|
-
expect(graph.removed).toContain("k1");
|
|
113
|
-
// Verify file content was restored
|
|
114
|
-
const restored = readFileSync(join(srcDir, "test.ts"), "utf-8");
|
|
115
|
-
expect(restored).toContain("return 'original'");
|
|
116
|
-
expect(restored).not.toContain("return 'modified'");
|
|
117
|
-
expect(restored).toContain("// header");
|
|
118
|
-
expect(restored).toContain("// footer");
|
|
119
|
-
});
|
|
120
|
-
it("reverts an ADDED entity by removing its lines", async () => {
|
|
121
|
-
const projectRoot = mkdtempSync(join(tmpdir(), "rewind-"));
|
|
122
|
-
const srcDir = join(projectRoot, "src");
|
|
123
|
-
mkdirSync(srcDir, { recursive: true });
|
|
124
|
-
const fileContent = [
|
|
125
|
-
"// existing code",
|
|
126
|
-
"function newFn() {",
|
|
127
|
-
" return 42;",
|
|
128
|
-
"}",
|
|
129
|
-
"// more code",
|
|
130
|
-
].join("\n");
|
|
131
|
-
writeFileSync(join(srcDir, "test.ts"), fileContent);
|
|
132
|
-
const graph = createMockGraph([
|
|
133
|
-
makeDrift({
|
|
134
|
-
key: "k2",
|
|
135
|
-
name: "newFn",
|
|
136
|
-
file_path: "src/test.ts",
|
|
137
|
-
body: "function newFn() {\n return 42;\n}",
|
|
138
|
-
line_start: 2,
|
|
139
|
-
line_end: 4,
|
|
140
|
-
drift_status: "added",
|
|
141
|
-
previous_body: "",
|
|
142
|
-
}),
|
|
143
|
-
]);
|
|
144
|
-
const result = await revertEntity("newFn", graph, projectRoot);
|
|
145
|
-
expect(result.reverted).toBe(true);
|
|
146
|
-
expect(result.drift_status).toBe("added");
|
|
147
|
-
expect(graph.removed).toContain("k2");
|
|
148
|
-
const restored = readFileSync(join(srcDir, "test.ts"), "utf-8");
|
|
149
|
-
expect(restored).not.toContain("newFn");
|
|
150
|
-
expect(restored).toContain("// existing code");
|
|
151
|
-
expect(restored).toContain("// more code");
|
|
152
|
-
});
|
|
153
|
-
it("reverts a DELETED entity by inserting previous_body", async () => {
|
|
154
|
-
const projectRoot = mkdtempSync(join(tmpdir(), "rewind-"));
|
|
155
|
-
const srcDir = join(projectRoot, "src");
|
|
156
|
-
mkdirSync(srcDir, { recursive: true });
|
|
157
|
-
// File after deletion — the entity is missing
|
|
158
|
-
const fileContent = ["// header", "// footer"].join("\n");
|
|
159
|
-
writeFileSync(join(srcDir, "test.ts"), fileContent);
|
|
160
|
-
const graph = createMockGraph([
|
|
161
|
-
makeDrift({
|
|
162
|
-
key: "k3",
|
|
163
|
-
name: "removedFn",
|
|
164
|
-
file_path: "src/test.ts",
|
|
165
|
-
body: "",
|
|
166
|
-
line_start: 2,
|
|
167
|
-
line_end: 2,
|
|
168
|
-
drift_status: "deleted",
|
|
169
|
-
previous_body: "function removedFn() {\n return 'was here';\n}",
|
|
170
|
-
}),
|
|
171
|
-
]);
|
|
172
|
-
const result = await revertEntity("removedFn", graph, projectRoot);
|
|
173
|
-
expect(result.reverted).toBe(true);
|
|
174
|
-
expect(result.drift_status).toBe("deleted");
|
|
175
|
-
expect(graph.removed).toContain("k3");
|
|
176
|
-
const restored = readFileSync(join(srcDir, "test.ts"), "utf-8");
|
|
177
|
-
expect(restored).toContain("removedFn");
|
|
178
|
-
expect(restored).toContain("was here");
|
|
179
|
-
});
|
|
180
|
-
it("reverts dependency_changed by just removing overlay entry", async () => {
|
|
181
|
-
const graph = createMockGraph([
|
|
182
|
-
makeDrift({
|
|
183
|
-
key: "k4",
|
|
184
|
-
name: "callerFn",
|
|
185
|
-
drift_status: "dependency_changed",
|
|
186
|
-
}),
|
|
187
|
-
]);
|
|
188
|
-
const result = await revertEntity("callerFn", graph, "/tmp");
|
|
189
|
-
expect(result.reverted).toBe(true);
|
|
190
|
-
expect(result.drift_status).toBe("dependency_changed");
|
|
191
|
-
expect(graph.removed).toContain("k4");
|
|
192
|
-
});
|
|
193
|
-
it("filters by file_path when provided", async () => {
|
|
194
|
-
const graph = createMockGraph([
|
|
195
|
-
makeDrift({
|
|
196
|
-
key: "k5a",
|
|
197
|
-
name: "shared",
|
|
198
|
-
file_path: "src/a.ts",
|
|
199
|
-
drift_status: "modified",
|
|
200
|
-
}),
|
|
201
|
-
makeDrift({
|
|
202
|
-
key: "k5b",
|
|
203
|
-
name: "shared",
|
|
204
|
-
file_path: "src/b.ts",
|
|
205
|
-
drift_status: "modified",
|
|
206
|
-
previous_body: "function shared() { return 'b'; }",
|
|
207
|
-
}),
|
|
208
|
-
]);
|
|
209
|
-
const projectRoot = mkdtempSync(join(tmpdir(), "rewind-"));
|
|
210
|
-
mkdirSync(join(projectRoot, "src"), { recursive: true });
|
|
211
|
-
writeFileSync(join(projectRoot, "src", "b.ts"), "function shared() { return 'modified'; }");
|
|
212
|
-
const result = await revertEntity("shared", graph, projectRoot, "src/b.ts");
|
|
213
|
-
expect(result.reverted).toBe(true);
|
|
214
|
-
expect(result.file).toBe("src/b.ts");
|
|
215
|
-
expect(graph.removed).toContain("k5b");
|
|
216
|
-
expect(graph.removed).not.toContain("k5a");
|
|
217
|
-
});
|
|
218
|
-
it("returns error when modified entity has no previous_body", async () => {
|
|
219
|
-
const projectRoot = mkdtempSync(join(tmpdir(), "rewind-"));
|
|
220
|
-
mkdirSync(join(projectRoot, "src"), { recursive: true });
|
|
221
|
-
writeFileSync(join(projectRoot, "src", "test.ts"), "some content");
|
|
222
|
-
const graph = createMockGraph([
|
|
223
|
-
makeDrift({
|
|
224
|
-
key: "k6",
|
|
225
|
-
name: "noHistory",
|
|
226
|
-
file_path: "src/test.ts",
|
|
227
|
-
drift_status: "modified",
|
|
228
|
-
previous_body: "",
|
|
229
|
-
}),
|
|
230
|
-
]);
|
|
231
|
-
const result = await revertEntity("noHistory", graph, projectRoot);
|
|
232
|
-
expect(result.reverted).toBe(false);
|
|
233
|
-
expect(result.error).toContain("No previous body");
|
|
234
|
-
});
|
|
235
|
-
it("stores previous_body in drift overlay when entity is modified", () => {
|
|
236
|
-
// This test verifies the DriftTracker integration —
|
|
237
|
-
// modified entities should preserve the base body for rewind
|
|
238
|
-
const drift = makeDrift({
|
|
239
|
-
key: "k7",
|
|
240
|
-
name: "fn",
|
|
241
|
-
drift_status: "modified",
|
|
242
|
-
previous_body: "function fn() { return 1; }",
|
|
243
|
-
previous_signature: "()",
|
|
244
|
-
});
|
|
245
|
-
expect(drift.previous_body).toBe("function fn() { return 1; }");
|
|
246
|
-
expect(drift.previous_signature).toBe("()");
|
|
247
|
-
});
|
|
248
|
-
});
|