@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,179 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
-
import { removeInstructionSection, writeInstructionFile, } from "../config/instruction-writer.js";
|
|
6
|
-
describe("instruction-writer", () => {
|
|
7
|
-
let tmpDir;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
tmpDir = join(tmpdir(), `unerr-instr-test-${Date.now()}`);
|
|
10
|
-
mkdirSync(tmpDir, { recursive: true });
|
|
11
|
-
});
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
rmSync(tmpDir, { recursive: true, force: true });
|
|
14
|
-
});
|
|
15
|
-
describe("writeInstructionFile — markdown (claude-code)", () => {
|
|
16
|
-
it("creates CLAUDE.md with sentinel markers when file does not exist", () => {
|
|
17
|
-
const result = writeInstructionFile(tmpDir, "claude-code");
|
|
18
|
-
expect(result.action).toBe("created");
|
|
19
|
-
expect(result.path).toBe(join(tmpDir, "CLAUDE.md"));
|
|
20
|
-
const content = readFileSync(result.path, "utf-8");
|
|
21
|
-
expect(content).toContain("<!-- unerr:start -->");
|
|
22
|
-
expect(content).toContain("<!-- unerr:end -->");
|
|
23
|
-
expect(content).toContain("get_references");
|
|
24
|
-
expect(content).toContain("search_code");
|
|
25
|
-
});
|
|
26
|
-
it("appends to existing CLAUDE.md without sentinel", () => {
|
|
27
|
-
const existingContent = "# My Project\n\nExisting instructions.\n";
|
|
28
|
-
writeFileSync(join(tmpDir, "CLAUDE.md"), existingContent);
|
|
29
|
-
const result = writeInstructionFile(tmpDir, "claude-code");
|
|
30
|
-
expect(result.action).toBe("updated");
|
|
31
|
-
const content = readFileSync(result.path, "utf-8");
|
|
32
|
-
expect(content).toContain("# My Project");
|
|
33
|
-
expect(content).toContain("Existing instructions.");
|
|
34
|
-
expect(content).toContain("<!-- unerr:start -->");
|
|
35
|
-
expect(content).toContain("get_references");
|
|
36
|
-
});
|
|
37
|
-
it("skips when sentinel content is identical", () => {
|
|
38
|
-
writeInstructionFile(tmpDir, "claude-code");
|
|
39
|
-
const result = writeInstructionFile(tmpDir, "claude-code");
|
|
40
|
-
expect(result.action).toBe("skipped");
|
|
41
|
-
});
|
|
42
|
-
it("updates when sentinel content differs", () => {
|
|
43
|
-
// Write with old content
|
|
44
|
-
const oldContent = "<!-- unerr:start -->\nOld content\n<!-- unerr:end -->\n";
|
|
45
|
-
writeFileSync(join(tmpDir, "CLAUDE.md"), oldContent);
|
|
46
|
-
const result = writeInstructionFile(tmpDir, "claude-code");
|
|
47
|
-
expect(result.action).toBe("updated");
|
|
48
|
-
const content = readFileSync(result.path, "utf-8");
|
|
49
|
-
expect(content).not.toContain("Old content");
|
|
50
|
-
expect(content).toContain("get_references");
|
|
51
|
-
});
|
|
52
|
-
it("preserves content before and after sentinel block", () => {
|
|
53
|
-
const existing = "# Header\n\n<!-- unerr:start -->\nOld\n<!-- unerr:end -->\n\n# Footer\n";
|
|
54
|
-
writeFileSync(join(tmpDir, "CLAUDE.md"), existing);
|
|
55
|
-
writeInstructionFile(tmpDir, "claude-code");
|
|
56
|
-
const content = readFileSync(join(tmpDir, "CLAUDE.md"), "utf-8");
|
|
57
|
-
expect(content).toContain("# Header");
|
|
58
|
-
expect(content).toContain("# Footer");
|
|
59
|
-
expect(content).toContain("get_references");
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe("writeInstructionFile — mdc (cursor)", () => {
|
|
63
|
-
it("creates .cursor/rules/unerr-instructions.mdc", () => {
|
|
64
|
-
const result = writeInstructionFile(tmpDir, "cursor");
|
|
65
|
-
expect(result.action).toBe("created");
|
|
66
|
-
const content = readFileSync(result.path, "utf-8");
|
|
67
|
-
expect(content).toContain("alwaysApply: true");
|
|
68
|
-
expect(content).toContain("get_references");
|
|
69
|
-
});
|
|
70
|
-
it("skips when content is identical", () => {
|
|
71
|
-
writeInstructionFile(tmpDir, "cursor");
|
|
72
|
-
const result = writeInstructionFile(tmpDir, "cursor");
|
|
73
|
-
expect(result.action).toBe("skipped");
|
|
74
|
-
});
|
|
75
|
-
it("updates when content differs", () => {
|
|
76
|
-
const filePath = join(tmpDir, ".cursor", "rules", "unerr-instructions.mdc");
|
|
77
|
-
mkdirSync(join(tmpDir, ".cursor", "rules"), { recursive: true });
|
|
78
|
-
writeFileSync(filePath, "old mdc content");
|
|
79
|
-
const result = writeInstructionFile(tmpDir, "cursor");
|
|
80
|
-
expect(result.action).toBe("updated");
|
|
81
|
-
const content = readFileSync(filePath, "utf-8");
|
|
82
|
-
expect(content).toContain("alwaysApply: true");
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
describe("writeInstructionFile — windsurf-rule (windsurf)", () => {
|
|
86
|
-
it("creates .windsurf/rules/unerr-instructions.md with trigger frontmatter", () => {
|
|
87
|
-
const result = writeInstructionFile(tmpDir, "windsurf");
|
|
88
|
-
expect(result.action).toBe("created");
|
|
89
|
-
const content = readFileSync(result.path, "utf-8");
|
|
90
|
-
expect(content).toContain("trigger: always_on");
|
|
91
|
-
expect(content).toContain("get_references");
|
|
92
|
-
});
|
|
93
|
-
it("skips when content is identical", () => {
|
|
94
|
-
writeInstructionFile(tmpDir, "windsurf");
|
|
95
|
-
const result = writeInstructionFile(tmpDir, "windsurf");
|
|
96
|
-
expect(result.action).toBe("skipped");
|
|
97
|
-
});
|
|
98
|
-
it("updates when content differs", () => {
|
|
99
|
-
const filePath = join(tmpDir, ".windsurf", "rules", "unerr-instructions.md");
|
|
100
|
-
mkdirSync(join(tmpDir, ".windsurf", "rules"), { recursive: true });
|
|
101
|
-
writeFileSync(filePath, "old windsurf content");
|
|
102
|
-
const result = writeInstructionFile(tmpDir, "windsurf");
|
|
103
|
-
expect(result.action).toBe("updated");
|
|
104
|
-
const content = readFileSync(filePath, "utf-8");
|
|
105
|
-
expect(content).toContain("trigger: always_on");
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
describe("writeInstructionFile — agents without instruction files", () => {
|
|
109
|
-
it("returns skipped for zed", () => {
|
|
110
|
-
const result = writeInstructionFile(tmpDir, "zed");
|
|
111
|
-
expect(result.action).toBe("skipped");
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
describe("writeInstructionFile — other agents", () => {
|
|
115
|
-
it("creates AGENTS.md for codex", () => {
|
|
116
|
-
const result = writeInstructionFile(tmpDir, "codex");
|
|
117
|
-
expect(result.action).toBe("created");
|
|
118
|
-
expect(result.path).toContain("AGENTS.md");
|
|
119
|
-
});
|
|
120
|
-
it("creates GEMINI.md for gemini-cli", () => {
|
|
121
|
-
const result = writeInstructionFile(tmpDir, "gemini-cli");
|
|
122
|
-
expect(result.action).toBe("created");
|
|
123
|
-
expect(result.path).toContain("GEMINI.md");
|
|
124
|
-
});
|
|
125
|
-
it("creates .github/copilot-instructions.md for vscode", () => {
|
|
126
|
-
const result = writeInstructionFile(tmpDir, "vscode");
|
|
127
|
-
expect(result.action).toBe("created");
|
|
128
|
-
expect(result.path).toContain("copilot-instructions.md");
|
|
129
|
-
});
|
|
130
|
-
it("creates .clinerules for cline", () => {
|
|
131
|
-
const result = writeInstructionFile(tmpDir, "cline");
|
|
132
|
-
expect(result.action).toBe("created");
|
|
133
|
-
expect(result.path).toContain(".clinerules");
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
describe("removeInstructionSection", () => {
|
|
137
|
-
it("removes sentinel block from markdown file", () => {
|
|
138
|
-
const existing = "# Header\n\nSome content.\n";
|
|
139
|
-
writeFileSync(join(tmpDir, "CLAUDE.md"), existing);
|
|
140
|
-
writeInstructionFile(tmpDir, "claude-code");
|
|
141
|
-
const removed = removeInstructionSection(tmpDir, "claude-code");
|
|
142
|
-
expect(removed).toBe(true);
|
|
143
|
-
const content = readFileSync(join(tmpDir, "CLAUDE.md"), "utf-8");
|
|
144
|
-
expect(content).toContain("# Header");
|
|
145
|
-
expect(content).not.toContain("<!-- unerr:start -->");
|
|
146
|
-
expect(content).not.toContain("get_references");
|
|
147
|
-
});
|
|
148
|
-
it("deletes .mdc file for cursor", () => {
|
|
149
|
-
writeInstructionFile(tmpDir, "cursor");
|
|
150
|
-
const filePath = join(tmpDir, ".cursor", "rules", "unerr-instructions.mdc");
|
|
151
|
-
expect(existsSync(filePath)).toBe(true);
|
|
152
|
-
const removed = removeInstructionSection(tmpDir, "cursor");
|
|
153
|
-
expect(removed).toBe(true);
|
|
154
|
-
expect(existsSync(filePath)).toBe(false);
|
|
155
|
-
});
|
|
156
|
-
it("returns false when no instruction file exists", () => {
|
|
157
|
-
const removed = removeInstructionSection(tmpDir, "claude-code");
|
|
158
|
-
expect(removed).toBe(false);
|
|
159
|
-
});
|
|
160
|
-
it("removes windsurf-rule file", () => {
|
|
161
|
-
writeInstructionFile(tmpDir, "windsurf");
|
|
162
|
-
const filePath = join(tmpDir, ".windsurf", "rules", "unerr-instructions.md");
|
|
163
|
-
expect(existsSync(filePath)).toBe(true);
|
|
164
|
-
const removed = removeInstructionSection(tmpDir, "windsurf");
|
|
165
|
-
expect(removed).toBe(true);
|
|
166
|
-
expect(existsSync(filePath)).toBe(false);
|
|
167
|
-
});
|
|
168
|
-
it("returns false for agents without instruction files", () => {
|
|
169
|
-
const removed = removeInstructionSection(tmpDir, "zed");
|
|
170
|
-
expect(removed).toBe(false);
|
|
171
|
-
});
|
|
172
|
-
it("deletes file if only sentinel content remains", () => {
|
|
173
|
-
writeInstructionFile(tmpDir, "claude-code");
|
|
174
|
-
const removed = removeInstructionSection(tmpDir, "claude-code");
|
|
175
|
-
expect(removed).toBe(true);
|
|
176
|
-
expect(existsSync(join(tmpDir, "CLAUDE.md"))).toBe(false);
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
});
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sprint P: Intelligence integration + performance tests.
|
|
3
|
-
*
|
|
4
|
-
* Tests the full pipeline: confidence propagation, response enrichment,
|
|
5
|
-
* computation scheduling, and performance benchmarks.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, expect, it } from "vitest";
|
|
8
|
-
import { buildReverseAdjacency, computeBlastRadius, } from "../intelligence/blast-radius.js";
|
|
9
|
-
import { clearPending, getPendingCount, scheduleComputation, } from "../intelligence/computation-scheduler.js";
|
|
10
|
-
import { confidenceToScore, propagateConfidence, } from "../intelligence/confidence-propagation.js";
|
|
11
|
-
import { entityKey } from "../intelligence/indexer/entity-key.js";
|
|
12
|
-
import { enrichResponse } from "../proxy/response-enrichment.js";
|
|
13
|
-
describe("Confidence Propagation (P.10)", () => {
|
|
14
|
-
it("MIN across path: one heuristic → whole path is heuristic", () => {
|
|
15
|
-
const result = propagateConfidence([
|
|
16
|
-
"compiler-verified",
|
|
17
|
-
"structural",
|
|
18
|
-
"heuristic",
|
|
19
|
-
"structural",
|
|
20
|
-
]);
|
|
21
|
-
expect(result.level).toBe("heuristic");
|
|
22
|
-
expect(result.edgeSources.heuristic).toBe(1);
|
|
23
|
-
expect(result.edgeSources.structural).toBe(2);
|
|
24
|
-
expect(result.pathLength).toBe(4);
|
|
25
|
-
});
|
|
26
|
-
it("all compiler-verified → compiler-verified result", () => {
|
|
27
|
-
const result = propagateConfidence([
|
|
28
|
-
"compiler-verified",
|
|
29
|
-
"compiler-verified",
|
|
30
|
-
]);
|
|
31
|
-
expect(result.level).toBe("compiler-verified");
|
|
32
|
-
});
|
|
33
|
-
it("empty path → structural default", () => {
|
|
34
|
-
const result = propagateConfidence([]);
|
|
35
|
-
expect(result.level).toBe("structural");
|
|
36
|
-
expect(result.pathLength).toBe(0);
|
|
37
|
-
});
|
|
38
|
-
it("confidenceToScore maps correctly", () => {
|
|
39
|
-
expect(confidenceToScore("compiler-verified")).toBe(1.0);
|
|
40
|
-
expect(confidenceToScore("structural")).toBe(0.85);
|
|
41
|
-
expect(confidenceToScore("heuristic")).toBe(0.5);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
describe("Response Enrichment (P.9)", () => {
|
|
45
|
-
it("enriches response with _meta block", () => {
|
|
46
|
-
const result = enrichResponse({ entities: ["a", "b"] }, {
|
|
47
|
-
confidence: "structural",
|
|
48
|
-
riskLevel: "high",
|
|
49
|
-
resolutionMs: 2.3,
|
|
50
|
-
edgeSources: { structural: 5 },
|
|
51
|
-
});
|
|
52
|
-
expect(result._meta.confidence).toBe("structural");
|
|
53
|
-
expect(result._meta.confidence_score).toBe(0.85);
|
|
54
|
-
expect(result._meta.risk_level).toBe("high");
|
|
55
|
-
expect(result._meta.resolution_ms).toBe(2.3);
|
|
56
|
-
expect(result._meta.edge_sources.structural).toBe(5);
|
|
57
|
-
});
|
|
58
|
-
it("includes _context when provided", () => {
|
|
59
|
-
const result = enrichResponse({}, {
|
|
60
|
-
confidence: "structural",
|
|
61
|
-
riskLevel: "normal",
|
|
62
|
-
resolutionMs: 1,
|
|
63
|
-
edgeSources: {},
|
|
64
|
-
}, { "dev.unerr/conventions": [{ name: "test" }] });
|
|
65
|
-
expect(result._context?.["dev.unerr/conventions"]).toBeDefined();
|
|
66
|
-
});
|
|
67
|
-
it("omits _context when empty", () => {
|
|
68
|
-
const result = enrichResponse({}, {
|
|
69
|
-
confidence: "structural",
|
|
70
|
-
riskLevel: "normal",
|
|
71
|
-
resolutionMs: 1,
|
|
72
|
-
edgeSources: {},
|
|
73
|
-
});
|
|
74
|
-
expect(result._context).toBeUndefined();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
describe("Computation Scheduler (P.11)", () => {
|
|
78
|
-
it("executes critical computations immediately", async () => {
|
|
79
|
-
let executed = false;
|
|
80
|
-
const ran = await scheduleComputation("test-critical", "critical", async () => {
|
|
81
|
-
executed = true;
|
|
82
|
-
});
|
|
83
|
-
expect(ran).toBe(true);
|
|
84
|
-
expect(executed).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
it("executes normal computations when resources available", async () => {
|
|
87
|
-
let executed = false;
|
|
88
|
-
const ran = await scheduleComputation("test-normal", "normal", async () => {
|
|
89
|
-
executed = true;
|
|
90
|
-
});
|
|
91
|
-
expect(ran).toBe(true);
|
|
92
|
-
expect(executed).toBe(true);
|
|
93
|
-
});
|
|
94
|
-
it("getPendingCount reflects queue", () => {
|
|
95
|
-
clearPending();
|
|
96
|
-
expect(getPendingCount()).toBe(0);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
describe("Performance: Blast Radius <5ms (P.12)", () => {
|
|
100
|
-
it("200-entity graph blast radius completes in <5ms", () => {
|
|
101
|
-
const target = {
|
|
102
|
-
key: entityKey("center.ts", "function", "center", ""),
|
|
103
|
-
kind: "function",
|
|
104
|
-
name: "center",
|
|
105
|
-
file_path: "center.ts",
|
|
106
|
-
start_line: 1,
|
|
107
|
-
end_line: 5,
|
|
108
|
-
signature: "center()",
|
|
109
|
-
body_hash: "h",
|
|
110
|
-
exported: true,
|
|
111
|
-
parent_key: null,
|
|
112
|
-
language: "typescript",
|
|
113
|
-
is_async: false,
|
|
114
|
-
parameter_count: 0,
|
|
115
|
-
doc: null,
|
|
116
|
-
community: 0,
|
|
117
|
-
risk_level: "high",
|
|
118
|
-
};
|
|
119
|
-
const allEntities = [target];
|
|
120
|
-
const allEdges = [];
|
|
121
|
-
for (let i = 0; i < 200; i++) {
|
|
122
|
-
const caller = {
|
|
123
|
-
...target,
|
|
124
|
-
key: entityKey(`f${i}.ts`, "function", `fn${i}`, ""),
|
|
125
|
-
name: `fn${i}`,
|
|
126
|
-
file_path: `f${i}.ts`,
|
|
127
|
-
};
|
|
128
|
-
allEntities.push(caller);
|
|
129
|
-
allEdges.push({
|
|
130
|
-
from_key: caller.key,
|
|
131
|
-
to_key: target.key,
|
|
132
|
-
type: "calls",
|
|
133
|
-
file_path: caller.file_path,
|
|
134
|
-
line: 1,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
const entities = new Map(allEntities.map((e) => [e.key, e]));
|
|
138
|
-
const adj = buildReverseAdjacency(allEdges);
|
|
139
|
-
const start = performance.now();
|
|
140
|
-
const result = computeBlastRadius(target.key, entities, adj, {
|
|
141
|
-
maxHops: 2,
|
|
142
|
-
});
|
|
143
|
-
const elapsed = performance.now() - start;
|
|
144
|
-
expect(result.totalAffected).toBe(200);
|
|
145
|
-
expect(elapsed).toBeLessThan(5);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
describe("End-to-End Pipeline (P.13)", () => {
|
|
149
|
-
it("index → query → enriched response with confidence", () => {
|
|
150
|
-
const entities = new Map([
|
|
151
|
-
[
|
|
152
|
-
"target",
|
|
153
|
-
{
|
|
154
|
-
key: "target",
|
|
155
|
-
kind: "function",
|
|
156
|
-
name: "processOrder",
|
|
157
|
-
file_path: "src/orders.ts",
|
|
158
|
-
start_line: 1,
|
|
159
|
-
end_line: 10,
|
|
160
|
-
signature: "processOrder()",
|
|
161
|
-
body_hash: "h",
|
|
162
|
-
exported: true,
|
|
163
|
-
parent_key: null,
|
|
164
|
-
language: "typescript",
|
|
165
|
-
is_async: true,
|
|
166
|
-
parameter_count: 1,
|
|
167
|
-
doc: null,
|
|
168
|
-
community: 0,
|
|
169
|
-
risk_level: "high",
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
[
|
|
173
|
-
"caller",
|
|
174
|
-
{
|
|
175
|
-
key: "caller",
|
|
176
|
-
kind: "function",
|
|
177
|
-
name: "checkout",
|
|
178
|
-
file_path: "src/checkout.ts",
|
|
179
|
-
start_line: 1,
|
|
180
|
-
end_line: 5,
|
|
181
|
-
signature: "checkout()",
|
|
182
|
-
body_hash: "h2",
|
|
183
|
-
exported: true,
|
|
184
|
-
parent_key: null,
|
|
185
|
-
language: "typescript",
|
|
186
|
-
is_async: true,
|
|
187
|
-
parameter_count: 0,
|
|
188
|
-
doc: null,
|
|
189
|
-
community: 1,
|
|
190
|
-
risk_level: "normal",
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
]);
|
|
194
|
-
const edges = [
|
|
195
|
-
{
|
|
196
|
-
from_key: "caller",
|
|
197
|
-
to_key: "target",
|
|
198
|
-
type: "calls",
|
|
199
|
-
file_path: "src/checkout.ts",
|
|
200
|
-
line: 3,
|
|
201
|
-
},
|
|
202
|
-
];
|
|
203
|
-
const adj = buildReverseAdjacency(edges);
|
|
204
|
-
const blastResult = computeBlastRadius("target", entities, adj);
|
|
205
|
-
const confidence = propagateConfidence(["structural"]);
|
|
206
|
-
const enriched = enrichResponse(blastResult, {
|
|
207
|
-
confidence: confidence.level,
|
|
208
|
-
riskLevel: "high",
|
|
209
|
-
resolutionMs: blastResult.resolvedInMs,
|
|
210
|
-
edgeSources: confidence.edgeSources,
|
|
211
|
-
});
|
|
212
|
-
expect(enriched._meta.confidence).toBe("structural");
|
|
213
|
-
expect(enriched._meta.risk_level).toBe("high");
|
|
214
|
-
expect(enriched._meta.resolution_ms).toBeGreaterThanOrEqual(0);
|
|
215
|
-
expect(enriched.content.totalAffected).toBe(1);
|
|
216
|
-
});
|
|
217
|
-
});
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* P10-TEST-04: Intent Correlator tests — chain building, pending correlations.
|
|
3
|
-
*/
|
|
4
|
-
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
8
|
-
import { IntentCorrelator } from "../tracking/intent-correlator.js";
|
|
9
|
-
import { ShadowLedger } from "../tracking/shadow-ledger.js";
|
|
10
|
-
let tempDir;
|
|
11
|
-
let unerrDir;
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
tempDir = join(tmpdir(), `unerr-correlator-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
14
|
-
unerrDir = join(tempDir, ".unerr");
|
|
15
|
-
mkdirSync(unerrDir, { recursive: true });
|
|
16
|
-
});
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
try {
|
|
19
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
/* ignore */
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
describe("IntentCorrelator", () => {
|
|
26
|
-
it("creates pending correlation on sync_local_diff", () => {
|
|
27
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
28
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
29
|
-
// Record a root intent and a sync
|
|
30
|
-
ledger.record("get_function", { key: "abc" }, { found: true }, "main", "aaa");
|
|
31
|
-
ledger.record("sync_local_diff", {
|
|
32
|
-
prompt: "Fix the auth bug",
|
|
33
|
-
files: [{ path: "src/auth.ts", content: "..." }],
|
|
34
|
-
entitiesAffected: ["auth::login"],
|
|
35
|
-
}, {}, "main", "aaa");
|
|
36
|
-
const result = correlator.onSyncLocalDiff(ledger, {
|
|
37
|
-
prompt: "Fix the auth bug",
|
|
38
|
-
files: [{ path: "src/auth.ts", content: "..." }],
|
|
39
|
-
entitiesAffected: ["auth::login"],
|
|
40
|
-
});
|
|
41
|
-
expect(result).not.toBeNull();
|
|
42
|
-
expect(result?.prompt).toBe("Fix the auth bug");
|
|
43
|
-
expect(result?.files).toEqual(["src/auth.ts"]);
|
|
44
|
-
expect(result?.entities).toEqual(["auth::login"]);
|
|
45
|
-
expect(result?.toolChain.length).toBeGreaterThan(0);
|
|
46
|
-
});
|
|
47
|
-
it("extracts files from structured format", () => {
|
|
48
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
49
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
50
|
-
ledger.record("get_function", {}, {}, "main", "aaa");
|
|
51
|
-
const result = correlator.onSyncLocalDiff(ledger, {
|
|
52
|
-
files: [
|
|
53
|
-
{ path: "src/a.ts", content: "code" },
|
|
54
|
-
{ path: "src/b.ts", content: "code" },
|
|
55
|
-
],
|
|
56
|
-
});
|
|
57
|
-
expect(result).not.toBeNull();
|
|
58
|
-
expect(result?.files).toEqual(["src/a.ts", "src/b.ts"]);
|
|
59
|
-
});
|
|
60
|
-
it("extracts files from diff format", () => {
|
|
61
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
62
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
63
|
-
ledger.record("get_function", {}, {}, "main", "aaa");
|
|
64
|
-
const diff = `--- a/src/auth.ts
|
|
65
|
-
+++ b/src/auth.ts
|
|
66
|
-
@@ -1,3 +1,4 @@
|
|
67
|
-
+import { foo } from "bar"
|
|
68
|
-
export function login() {}`;
|
|
69
|
-
const result = correlator.onSyncLocalDiff(ledger, { diff });
|
|
70
|
-
expect(result).not.toBeNull();
|
|
71
|
-
expect(result?.files).toContain("src/auth.ts");
|
|
72
|
-
});
|
|
73
|
-
it("getPending returns only uncommitted correlations", () => {
|
|
74
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
75
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
76
|
-
ledger.record("get_function", {}, {}, "main", "aaa");
|
|
77
|
-
correlator.onSyncLocalDiff(ledger, {
|
|
78
|
-
files: [{ path: "a.ts", content: "" }],
|
|
79
|
-
});
|
|
80
|
-
correlator.onSyncLocalDiff(ledger, {
|
|
81
|
-
files: [{ path: "b.ts", content: "" }],
|
|
82
|
-
});
|
|
83
|
-
expect(correlator.getPending()).toHaveLength(2);
|
|
84
|
-
expect(correlator.getPendingCount()).toBe(2);
|
|
85
|
-
});
|
|
86
|
-
it("associateCommit links correlations by overlapping files", () => {
|
|
87
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
88
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
89
|
-
ledger.record("get_function", {}, {}, "main", "aaa");
|
|
90
|
-
correlator.onSyncLocalDiff(ledger, {
|
|
91
|
-
files: [{ path: "src/a.ts", content: "" }],
|
|
92
|
-
});
|
|
93
|
-
correlator.onSyncLocalDiff(ledger, {
|
|
94
|
-
files: [{ path: "src/b.ts", content: "" }],
|
|
95
|
-
});
|
|
96
|
-
// Commit only includes a.ts
|
|
97
|
-
const associated = correlator.associateCommit("commit123", ["src/a.ts"]);
|
|
98
|
-
expect(associated).toBe(1);
|
|
99
|
-
// One pending, one committed
|
|
100
|
-
expect(correlator.getPending()).toHaveLength(1);
|
|
101
|
-
expect(correlator.getCommittedUnflushed()).toHaveLength(1);
|
|
102
|
-
expect(correlator.getCommittedUnflushed()[0]?.commitSha).toBe("commit123");
|
|
103
|
-
});
|
|
104
|
-
it("removeFlushed clears committed correlations", () => {
|
|
105
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
106
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
107
|
-
ledger.record("get_function", {}, {}, "main", "aaa");
|
|
108
|
-
const c1 = correlator.onSyncLocalDiff(ledger, {
|
|
109
|
-
files: [{ path: "a.ts", content: "" }],
|
|
110
|
-
});
|
|
111
|
-
correlator.associateCommit("commit123", ["a.ts"]);
|
|
112
|
-
correlator.removeFlushed([c1.rootIntentId]);
|
|
113
|
-
expect(correlator.getAll()).toHaveLength(0);
|
|
114
|
-
});
|
|
115
|
-
it("clear removes all correlations", () => {
|
|
116
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
117
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
118
|
-
ledger.record("get_function", {}, {}, "main", "aaa");
|
|
119
|
-
correlator.onSyncLocalDiff(ledger, {
|
|
120
|
-
files: [{ path: "a.ts", content: "" }],
|
|
121
|
-
});
|
|
122
|
-
correlator.onSyncLocalDiff(ledger, {
|
|
123
|
-
files: [{ path: "b.ts", content: "" }],
|
|
124
|
-
});
|
|
125
|
-
correlator.clear();
|
|
126
|
-
expect(correlator.getAll()).toHaveLength(0);
|
|
127
|
-
expect(correlator.getPendingCount()).toBe(0);
|
|
128
|
-
});
|
|
129
|
-
it("persists to disk and loads on new instance", () => {
|
|
130
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
131
|
-
const correlator1 = new IntentCorrelator(unerrDir);
|
|
132
|
-
ledger.record("get_function", {}, {}, "main", "aaa");
|
|
133
|
-
correlator1.onSyncLocalDiff(ledger, {
|
|
134
|
-
prompt: "Fix bug",
|
|
135
|
-
files: [{ path: "src/fix.ts", content: "" }],
|
|
136
|
-
});
|
|
137
|
-
// Verify file exists
|
|
138
|
-
const pendingPath = join(unerrDir, "ledger", "pending_correlations.json");
|
|
139
|
-
expect(existsSync(pendingPath)).toBe(true);
|
|
140
|
-
// New instance should load persisted data
|
|
141
|
-
const correlator2 = new IntentCorrelator(unerrDir);
|
|
142
|
-
expect(correlator2.getPendingCount()).toBe(1);
|
|
143
|
-
expect(correlator2.getPending()[0]?.prompt).toBe("Fix bug");
|
|
144
|
-
});
|
|
145
|
-
it("returns null when no root intent exists", () => {
|
|
146
|
-
// Create a ledger but don't record anything — getCurrentRootId() returns null
|
|
147
|
-
// We need a fresh ledger with no entries so root ID is null
|
|
148
|
-
const ledgerDir = join(unerrDir, "ledger");
|
|
149
|
-
mkdirSync(ledgerDir, { recursive: true });
|
|
150
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
151
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
152
|
-
// Don't record any entries — root ID should be null
|
|
153
|
-
// Actually we need to check: a fresh ShadowLedger has currentRootId = null
|
|
154
|
-
// But onSyncLocalDiff checks getCurrentRootId() which would be null
|
|
155
|
-
const result = correlator.onSyncLocalDiff(ledger, {
|
|
156
|
-
files: [{ path: "a.ts", content: "" }],
|
|
157
|
-
});
|
|
158
|
-
expect(result).toBeNull();
|
|
159
|
-
});
|
|
160
|
-
it("builds tool chain from correlated entries", () => {
|
|
161
|
-
const ledger = new ShadowLedger(unerrDir);
|
|
162
|
-
const correlator = new IntentCorrelator(unerrDir);
|
|
163
|
-
// Build a chain: root → child1 → child2 → sync
|
|
164
|
-
ledger.record("get_function", { key: "abc" }, { found: true }, "main", "aaa");
|
|
165
|
-
ledger.record("get_callers", { key: "abc" }, { count: 3 }, "main", "aaa");
|
|
166
|
-
ledger.record("get_file", { key: "file1" }, { found: true }, "main", "aaa");
|
|
167
|
-
const result = correlator.onSyncLocalDiff(ledger, {
|
|
168
|
-
files: [{ path: "src/a.ts", content: "" }],
|
|
169
|
-
});
|
|
170
|
-
expect(result).not.toBeNull();
|
|
171
|
-
// Chain should include root + correlated entries
|
|
172
|
-
expect(result?.toolChain.length).toBeGreaterThanOrEqual(1);
|
|
173
|
-
expect(result?.toolChain).toContain("get_function");
|
|
174
|
-
});
|
|
175
|
-
});
|