@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,297 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sprint 3: Local Intelligence Deepening — tests for blast radius entities,
|
|
3
|
-
* convention matching, health grade enhancements, and prefetch.
|
|
4
|
-
*/
|
|
5
|
-
import { describe, expect, it, vi } from "vitest";
|
|
6
|
-
import { checkFileStructure, checkImportDirection, checkNamingConventions, matchConventions, } from "../intelligence/convention-matcher.js";
|
|
7
|
-
// ── 3.1: Blast Radius Entities ────────────────────────────────────
|
|
8
|
-
describe("Sprint 3.1: getBlastRadiusEntities", () => {
|
|
9
|
-
it("returns empty array when no callers exist", () => {
|
|
10
|
-
// Simulate: entity with no callers
|
|
11
|
-
const mockGraph = createMockGraphStore({
|
|
12
|
-
transitiveRows: [],
|
|
13
|
-
entityRows: new Map(),
|
|
14
|
-
});
|
|
15
|
-
const result = mockGraph.getBlastRadiusEntities("isolated-fn", 2);
|
|
16
|
-
expect(result).toEqual([]);
|
|
17
|
-
});
|
|
18
|
-
it("returns entities with depth from recursive traversal", () => {
|
|
19
|
-
const mockGraph = createMockGraphStore({
|
|
20
|
-
transitiveRows: [
|
|
21
|
-
["caller-a", 1],
|
|
22
|
-
["caller-b", 1],
|
|
23
|
-
["caller-c", 2],
|
|
24
|
-
],
|
|
25
|
-
entityRows: new Map([
|
|
26
|
-
["caller-a", ["caller-a", "callerA", "src/a.ts"]],
|
|
27
|
-
["caller-b", ["caller-b", "callerB", "src/b.ts"]],
|
|
28
|
-
["caller-c", ["caller-c", "callerC", "src/c.ts"]],
|
|
29
|
-
]),
|
|
30
|
-
});
|
|
31
|
-
const result = mockGraph.getBlastRadiusEntities("target-fn", 2);
|
|
32
|
-
expect(result).toHaveLength(3);
|
|
33
|
-
expect(result[0]).toEqual({
|
|
34
|
-
key: "caller-a",
|
|
35
|
-
name: "callerA",
|
|
36
|
-
file: "src/a.ts",
|
|
37
|
-
depth: 1,
|
|
38
|
-
});
|
|
39
|
-
expect(result[2]).toEqual({
|
|
40
|
-
key: "caller-c",
|
|
41
|
-
name: "callerC",
|
|
42
|
-
file: "src/c.ts",
|
|
43
|
-
depth: 2,
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
it("excludes the source entity from results", () => {
|
|
47
|
-
const mockGraph = createMockGraphStore({
|
|
48
|
-
transitiveRows: [
|
|
49
|
-
["target-fn", 1], // self-reference
|
|
50
|
-
["caller-a", 1],
|
|
51
|
-
],
|
|
52
|
-
entityRows: new Map([["caller-a", ["caller-a", "callerA", "src/a.ts"]]]),
|
|
53
|
-
});
|
|
54
|
-
const result = mockGraph.getBlastRadiusEntities("target-fn", 2);
|
|
55
|
-
expect(result).toHaveLength(1);
|
|
56
|
-
expect(result[0]?.key).toBe("caller-a");
|
|
57
|
-
});
|
|
58
|
-
it("uses minimum depth when entity appears at multiple depths", () => {
|
|
59
|
-
const mockGraph = createMockGraphStore({
|
|
60
|
-
transitiveRows: [
|
|
61
|
-
["caller-a", 2],
|
|
62
|
-
["caller-a", 1], // same entity at closer depth
|
|
63
|
-
],
|
|
64
|
-
entityRows: new Map([["caller-a", ["caller-a", "callerA", "src/a.ts"]]]),
|
|
65
|
-
});
|
|
66
|
-
const result = mockGraph.getBlastRadiusEntities("target-fn", 3);
|
|
67
|
-
expect(result).toHaveLength(1);
|
|
68
|
-
expect(result[0]?.depth).toBe(1);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
// ── 3.2: Convention Pattern Matching ──────────────────────────────
|
|
72
|
-
describe("Sprint 3.2: Convention Pattern Matching", () => {
|
|
73
|
-
describe("checkNamingConventions", () => {
|
|
74
|
-
it("detects non-PascalCase class names", () => {
|
|
75
|
-
const entities = [
|
|
76
|
-
makeEntity("myClass", "class", "src/foo.ts"),
|
|
77
|
-
];
|
|
78
|
-
const violations = checkNamingConventions(entities);
|
|
79
|
-
expect(violations).toHaveLength(1);
|
|
80
|
-
expect(violations[0]?.type).toBe("naming");
|
|
81
|
-
expect(violations[0]?.message).toContain("myClass");
|
|
82
|
-
});
|
|
83
|
-
it("passes PascalCase class names", () => {
|
|
84
|
-
const entities = [
|
|
85
|
-
makeEntity("MyClass", "class", "src/foo.ts"),
|
|
86
|
-
];
|
|
87
|
-
const violations = checkNamingConventions(entities);
|
|
88
|
-
expect(violations).toHaveLength(0);
|
|
89
|
-
});
|
|
90
|
-
it("detects PascalCase function names", () => {
|
|
91
|
-
const entities = [
|
|
92
|
-
makeEntity("DoSomething", "function", "src/foo.ts"),
|
|
93
|
-
];
|
|
94
|
-
const violations = checkNamingConventions(entities);
|
|
95
|
-
expect(violations).toHaveLength(1);
|
|
96
|
-
expect(violations[0]?.message).toContain("DoSomething");
|
|
97
|
-
});
|
|
98
|
-
it("passes camelCase function names", () => {
|
|
99
|
-
const entities = [
|
|
100
|
-
makeEntity("doSomething", "function", "src/foo.ts"),
|
|
101
|
-
];
|
|
102
|
-
const violations = checkNamingConventions(entities);
|
|
103
|
-
expect(violations).toHaveLength(0);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
describe("checkFileStructure", () => {
|
|
107
|
-
it("flags missing test file", async () => {
|
|
108
|
-
const graph = createConventionGraph({ hasTestFile: false });
|
|
109
|
-
const violations = await checkFileStructure("src/service.ts", graph);
|
|
110
|
-
expect(violations).toHaveLength(1);
|
|
111
|
-
expect(violations[0]?.type).toBe("file_structure");
|
|
112
|
-
expect(violations[0]?.message).toContain(".test.ts");
|
|
113
|
-
});
|
|
114
|
-
it("passes when test file exists", async () => {
|
|
115
|
-
const graph = createConventionGraph({ hasTestFile: true });
|
|
116
|
-
const violations = await checkFileStructure("src/service.ts", graph);
|
|
117
|
-
expect(violations).toHaveLength(0);
|
|
118
|
-
});
|
|
119
|
-
it("skips test files themselves", async () => {
|
|
120
|
-
const graph = createConventionGraph({ hasTestFile: false });
|
|
121
|
-
const violations = await checkFileStructure("src/service.test.ts", graph);
|
|
122
|
-
expect(violations).toHaveLength(0);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
describe("checkImportDirection", () => {
|
|
126
|
-
it("flags service importing from controller", async () => {
|
|
127
|
-
const graph = createConventionGraph({
|
|
128
|
-
imports: [{ imported_file: "src/controllers/userCtrl.ts" }],
|
|
129
|
-
});
|
|
130
|
-
const violations = await checkImportDirection("src/services/userService.ts", graph);
|
|
131
|
-
expect(violations).toHaveLength(1);
|
|
132
|
-
expect(violations[0]?.type).toBe("import_direction");
|
|
133
|
-
expect(violations[0]?.severity).toBe("error");
|
|
134
|
-
});
|
|
135
|
-
it("passes when import direction is valid", async () => {
|
|
136
|
-
const graph = createConventionGraph({
|
|
137
|
-
imports: [{ imported_file: "src/utils/helper.ts" }],
|
|
138
|
-
});
|
|
139
|
-
const violations = await checkImportDirection("src/services/userService.ts", graph);
|
|
140
|
-
expect(violations).toHaveLength(0);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
describe("matchConventions (composite)", () => {
|
|
144
|
-
it("runs all checks and aggregates violations", async () => {
|
|
145
|
-
const graph = createConventionGraph({
|
|
146
|
-
hasTestFile: false,
|
|
147
|
-
entities: [makeEntity("myBadClass", "class", "src/services/bad.ts")],
|
|
148
|
-
imports: [{ imported_file: "src/controllers/ctrl.ts" }],
|
|
149
|
-
});
|
|
150
|
-
const violations = await matchConventions("src/services/bad.ts", graph);
|
|
151
|
-
// Naming (class) + file structure + import direction
|
|
152
|
-
expect(violations.length).toBeGreaterThanOrEqual(2);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
// ── 3.3: Health Grade Enhancement ─────────────────────────────────
|
|
157
|
-
describe("Sprint 3.3: Health Grade Enhancement", () => {
|
|
158
|
-
it("HealthGradeResult includes new fields", async () => {
|
|
159
|
-
const { computeHealthGrade } = await import("../intelligence/health-grade.js");
|
|
160
|
-
const db = createHealthGradeDb({
|
|
161
|
-
entities: 100,
|
|
162
|
-
edges: 200,
|
|
163
|
-
rules: 5,
|
|
164
|
-
deadFunctions: 10,
|
|
165
|
-
});
|
|
166
|
-
const result = await computeHealthGrade(db);
|
|
167
|
-
expect(result.score).toBeGreaterThan(0);
|
|
168
|
-
expect(result.grade).toBeDefined();
|
|
169
|
-
expect(result.conventionAdherence).toBeDefined();
|
|
170
|
-
expect(result.maxImportDepth).toBeDefined();
|
|
171
|
-
});
|
|
172
|
-
it("circular deps reduce health score", async () => {
|
|
173
|
-
const { computeHealthGrade } = await import("../intelligence/health-grade.js");
|
|
174
|
-
const dbNoCycles = createHealthGradeDb({ entities: 100, edges: 200 });
|
|
175
|
-
const dbWithCycles = createHealthGradeDb({
|
|
176
|
-
entities: 100,
|
|
177
|
-
edges: 200,
|
|
178
|
-
circularDeps: [["a", "b"]],
|
|
179
|
-
});
|
|
180
|
-
const scoreNoCycles = (await computeHealthGrade(dbNoCycles)).score;
|
|
181
|
-
const scoreWithCycles = (await computeHealthGrade(dbWithCycles)).score;
|
|
182
|
-
expect(scoreWithCycles).toBeLessThanOrEqual(scoreNoCycles);
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
// ── Helpers ───────────────────────────────────────────────────────
|
|
186
|
-
function makeEntity(name, kind, filePath) {
|
|
187
|
-
return {
|
|
188
|
-
key: `${filePath}:${name}`,
|
|
189
|
-
kind,
|
|
190
|
-
name,
|
|
191
|
-
file_path: filePath,
|
|
192
|
-
start_line: 1,
|
|
193
|
-
end_line: 0,
|
|
194
|
-
signature: `${name}()`,
|
|
195
|
-
body: "",
|
|
196
|
-
fan_in: 0,
|
|
197
|
-
fan_out: 0,
|
|
198
|
-
community: -1,
|
|
199
|
-
risk_level: "normal",
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
function createMockGraphStore(opts) {
|
|
203
|
-
return {
|
|
204
|
-
getBlastRadiusEntities(entityKey, maxDepth = 2) {
|
|
205
|
-
const depthMap = new Map();
|
|
206
|
-
for (const row of opts.transitiveRows) {
|
|
207
|
-
const key = row[0];
|
|
208
|
-
const depth = row[1];
|
|
209
|
-
if (key === entityKey)
|
|
210
|
-
continue;
|
|
211
|
-
const existing = depthMap.get(key);
|
|
212
|
-
if (existing === undefined || depth < existing) {
|
|
213
|
-
depthMap.set(key, depth);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
const entities = [];
|
|
217
|
-
for (const [key, depth] of depthMap) {
|
|
218
|
-
const row = opts.entityRows.get(key);
|
|
219
|
-
if (row) {
|
|
220
|
-
entities.push({
|
|
221
|
-
key,
|
|
222
|
-
name: row[1],
|
|
223
|
-
file: row[2],
|
|
224
|
-
depth,
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
entities.sort((a, b) => a.depth - b.depth || a.key.localeCompare(b.key));
|
|
229
|
-
return entities;
|
|
230
|
-
},
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
function createConventionGraph(opts) {
|
|
234
|
-
return {
|
|
235
|
-
getEntitiesByFile: vi.fn().mockImplementation((filePath) => {
|
|
236
|
-
// If checking for test sibling and hasTestFile is true, return entities
|
|
237
|
-
if (opts?.hasTestFile && filePath.includes(".test.")) {
|
|
238
|
-
return [makeEntity("testFn", "function", filePath)];
|
|
239
|
-
}
|
|
240
|
-
return opts?.entities ?? [];
|
|
241
|
-
}),
|
|
242
|
-
getImports: vi.fn().mockReturnValue(opts?.imports ?? []),
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
function createHealthGradeDb(opts) {
|
|
246
|
-
const entityCount = opts?.entities ?? 50;
|
|
247
|
-
const edgeCount = opts?.edges ?? 100;
|
|
248
|
-
const ruleCount = opts?.rules ?? 0;
|
|
249
|
-
const deadFnCount = opts?.deadFunctions ?? 0;
|
|
250
|
-
const circularDeps = opts?.circularDeps ?? [];
|
|
251
|
-
return {
|
|
252
|
-
async run(query, _params) {
|
|
253
|
-
// Count queries
|
|
254
|
-
if (query.includes("count(key)") && query.includes("*entities{")) {
|
|
255
|
-
return { rows: [[entityCount]] };
|
|
256
|
-
}
|
|
257
|
-
if (query.includes("count(from_key)") && query.includes("*edges[")) {
|
|
258
|
-
return { rows: [[edgeCount]] };
|
|
259
|
-
}
|
|
260
|
-
if (query.includes("count(key)") && query.includes("*rules[")) {
|
|
261
|
-
return { rows: [[ruleCount]] };
|
|
262
|
-
}
|
|
263
|
-
// Dead functions
|
|
264
|
-
if (query.includes('kind = "function"') &&
|
|
265
|
-
query.includes("fan_in == 0")) {
|
|
266
|
-
const rows = [];
|
|
267
|
-
for (let i = 0; i < deadFnCount; i++) {
|
|
268
|
-
rows.push([`dead-fn-${i}`, `deadFn${i}`, `src/dead${i}.ts`, 0]);
|
|
269
|
-
}
|
|
270
|
-
return { rows };
|
|
271
|
-
}
|
|
272
|
-
// High risk entities
|
|
273
|
-
if (query.includes('risk_level == "high"')) {
|
|
274
|
-
return { rows: [] };
|
|
275
|
-
}
|
|
276
|
-
// Circular deps (2-hop)
|
|
277
|
-
if (query.includes("*edges[a, b") && query.includes("*edges[b, a")) {
|
|
278
|
-
return {
|
|
279
|
-
rows: circularDeps.map((c) => [c[0], c[1]]),
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
// Max import depth
|
|
283
|
-
if (query.includes("max(depth)")) {
|
|
284
|
-
return { rows: [[3]] };
|
|
285
|
-
}
|
|
286
|
-
// Convention adherence
|
|
287
|
-
if (query.includes("*patterns[") && query.includes("naming")) {
|
|
288
|
-
return { rows: [] };
|
|
289
|
-
}
|
|
290
|
-
// Drift impact
|
|
291
|
-
if (query.includes("*drift_overlay[") && query.includes("fi > 5")) {
|
|
292
|
-
return { rows: [[0]] };
|
|
293
|
-
}
|
|
294
|
-
return { rows: [] };
|
|
295
|
-
},
|
|
296
|
-
};
|
|
297
|
-
}
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sprint 5/E: Standalone MCP Server Package — tests for the workspace package
|
|
3
|
-
* structure, server factory, and IDE auto-configuration.
|
|
4
|
-
*/
|
|
5
|
-
import * as fs from "node:fs";
|
|
6
|
-
import * as os from "node:os";
|
|
7
|
-
import * as path from "node:path";
|
|
8
|
-
import { describe, expect, it } from "vitest";
|
|
9
|
-
import { checkIdeConfig, repairIdeConfig } from "../commands/config-verify.js";
|
|
10
|
-
describe("Sprint E: MCP Package Structure", () => {
|
|
11
|
-
it("packages/mcp/src/server.ts exists", () => {
|
|
12
|
-
const serverPath = path.join(process.cwd(), "packages", "mcp", "src", "server.ts");
|
|
13
|
-
expect(fs.existsSync(serverPath)).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
it("server.ts exports createMcpServer function", () => {
|
|
16
|
-
const serverPath = path.join(process.cwd(), "packages", "mcp", "src", "server.ts");
|
|
17
|
-
const content = fs.readFileSync(serverPath, "utf-8");
|
|
18
|
-
expect(content).toContain("export function createMcpServer");
|
|
19
|
-
});
|
|
20
|
-
it("types.ts exports McpToolDefinition interface", () => {
|
|
21
|
-
const typesPath = path.join(process.cwd(), "packages", "mcp", "src", "types.ts");
|
|
22
|
-
const content = fs.readFileSync(typesPath, "utf-8");
|
|
23
|
-
expect(content).toContain("McpToolDefinition");
|
|
24
|
-
expect(content).toContain("McpToolHandler");
|
|
25
|
-
expect(content).toContain("McpServerConfig");
|
|
26
|
-
});
|
|
27
|
-
it("index.ts barrel exports server and types", () => {
|
|
28
|
-
const indexPath = path.join(process.cwd(), "packages", "mcp", "src", "index.ts");
|
|
29
|
-
const content = fs.readFileSync(indexPath, "utf-8");
|
|
30
|
-
expect(content).toContain("createMcpServer");
|
|
31
|
-
expect(content).toContain("McpServerConfig");
|
|
32
|
-
});
|
|
33
|
-
it("STDIO transport exists", () => {
|
|
34
|
-
const stdioPath = path.join(process.cwd(), "packages", "mcp", "src", "transports", "stdio.ts");
|
|
35
|
-
expect(fs.existsSync(stdioPath)).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
it("HTTP transport exists", () => {
|
|
38
|
-
const httpPath = path.join(process.cwd(), "packages", "mcp", "src", "transports", "http.ts");
|
|
39
|
-
expect(fs.existsSync(httpPath)).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
it("package.json has correct name and exports", () => {
|
|
42
|
-
const pkgPath = path.join(process.cwd(), "packages", "mcp", "package.json");
|
|
43
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
44
|
-
expect(pkg.name).toBe("@unerr/unerr-mcp");
|
|
45
|
-
expect(pkg.type).toBe("module");
|
|
46
|
-
expect(pkg.exports["."]).toBeDefined();
|
|
47
|
-
expect(pkg.exports["./transports/stdio"]).toBeDefined();
|
|
48
|
-
expect(pkg.exports["./transports/http"]).toBeDefined();
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
describe("Sprint 5.4: IDE Auto-Configuration", () => {
|
|
52
|
-
it("checkIdeConfig detects missing MCP config", () => {
|
|
53
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "unerr-test-ide-"));
|
|
54
|
-
try {
|
|
55
|
-
const configPath = path.join(tmpDir, ".cursor", "mcp.json");
|
|
56
|
-
const result = checkIdeConfig("cursor", configPath);
|
|
57
|
-
expect(result.configured).toBe(false);
|
|
58
|
-
}
|
|
59
|
-
finally {
|
|
60
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
it("repairIdeConfig creates config for Cursor", () => {
|
|
64
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "unerr-test-rep-"));
|
|
65
|
-
try {
|
|
66
|
-
const configPath = path.join(tmpDir, ".cursor", "mcp.json");
|
|
67
|
-
repairIdeConfig("cursor", configPath);
|
|
68
|
-
expect(fs.existsSync(configPath)).toBe(true);
|
|
69
|
-
const content = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
70
|
-
expect(content.mcpServers).toBeDefined();
|
|
71
|
-
}
|
|
72
|
-
finally {
|
|
73
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
it("repairIdeConfig creates config for Claude Code", () => {
|
|
77
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "unerr-test-cc-"));
|
|
78
|
-
try {
|
|
79
|
-
const configPath = path.join(tmpDir, ".mcp.json");
|
|
80
|
-
repairIdeConfig("claude-code", configPath);
|
|
81
|
-
expect(fs.existsSync(configPath)).toBe(true);
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
it("repairIdeConfig creates config for VS Code", () => {
|
|
88
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "unerr-test-vsc-"));
|
|
89
|
-
try {
|
|
90
|
-
const configPath = path.join(tmpDir, ".vscode", "mcp.json");
|
|
91
|
-
repairIdeConfig("vscode", configPath);
|
|
92
|
-
expect(fs.existsSync(configPath)).toBe(true);
|
|
93
|
-
}
|
|
94
|
-
finally {
|
|
95
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
it("checkIdeConfig detects existing config", () => {
|
|
99
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "unerr-test-ex-"));
|
|
100
|
-
try {
|
|
101
|
-
const configPath = path.join(tmpDir, ".cursor", "mcp.json");
|
|
102
|
-
repairIdeConfig("cursor", configPath);
|
|
103
|
-
const result = checkIdeConfig("cursor", configPath);
|
|
104
|
-
expect(result.configured).toBe(true);
|
|
105
|
-
}
|
|
106
|
-
finally {
|
|
107
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
it("repairIdeConfig is idempotent", () => {
|
|
111
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "unerr-test-id-"));
|
|
112
|
-
try {
|
|
113
|
-
const configPath = path.join(tmpDir, ".cursor", "mcp.json");
|
|
114
|
-
repairIdeConfig("cursor", configPath);
|
|
115
|
-
const before = fs.readFileSync(configPath, "utf-8");
|
|
116
|
-
repairIdeConfig("cursor", configPath);
|
|
117
|
-
const after = fs.readFileSync(configPath, "utf-8");
|
|
118
|
-
expect(JSON.parse(before)).toEqual(JSON.parse(after));
|
|
119
|
-
}
|
|
120
|
-
finally {
|
|
121
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
it("checkIdeConfig supports windsurf IDE", () => {
|
|
125
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "unerr-test-ws-"));
|
|
126
|
-
try {
|
|
127
|
-
const configPath = path.join(tmpDir, ".windsurf", "mcp.json");
|
|
128
|
-
repairIdeConfig("windsurf", configPath);
|
|
129
|
-
const result = checkIdeConfig("windsurf", configPath);
|
|
130
|
-
expect(result.configured).toBe(true);
|
|
131
|
-
}
|
|
132
|
-
finally {
|
|
133
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
});
|