seer-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +3 -0
- package/LICENSE +176 -0
- package/README.md +272 -0
- package/README_dev.md +199 -0
- package/dist/bundle/ci.d.ts +47 -0
- package/dist/bundle/ci.d.ts.map +1 -0
- package/dist/bundle/ci.js +113 -0
- package/dist/bundle/ci.js.map +1 -0
- package/dist/bundle/contract.d.ts +111 -0
- package/dist/bundle/contract.d.ts.map +1 -0
- package/dist/bundle/contract.js +352 -0
- package/dist/bundle/contract.js.map +1 -0
- package/dist/bundle/export.d.ts +36 -0
- package/dist/bundle/export.d.ts.map +1 -0
- package/dist/bundle/export.js +152 -0
- package/dist/bundle/export.js.map +1 -0
- package/dist/bundle/external.d.ts +66 -0
- package/dist/bundle/external.d.ts.map +1 -0
- package/dist/bundle/external.js +238 -0
- package/dist/bundle/external.js.map +1 -0
- package/dist/bundle/format.d.ts +94 -0
- package/dist/bundle/format.d.ts.map +1 -0
- package/dist/bundle/format.js +42 -0
- package/dist/bundle/format.js.map +1 -0
- package/dist/bundle/import.d.ts +49 -0
- package/dist/bundle/import.d.ts.map +1 -0
- package/dist/bundle/import.js +116 -0
- package/dist/bundle/import.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1402 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +48 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +284 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/db/schema.d.ts +3 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +616 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/store.d.ts +1011 -0
- package/dist/db/store.d.ts.map +1 -0
- package/dist/db/store.js +3888 -0
- package/dist/db/store.js.map +1 -0
- package/dist/graph/pagerank.d.ts +9 -0
- package/dist/graph/pagerank.d.ts.map +1 -0
- package/dist/graph/pagerank.js +47 -0
- package/dist/graph/pagerank.js.map +1 -0
- package/dist/indexer/architecture.d.ts +72 -0
- package/dist/indexer/architecture.d.ts.map +1 -0
- package/dist/indexer/architecture.js +112 -0
- package/dist/indexer/architecture.js.map +1 -0
- package/dist/indexer/behavior.d.ts +75 -0
- package/dist/indexer/behavior.d.ts.map +1 -0
- package/dist/indexer/behavior.js +395 -0
- package/dist/indexer/behavior.js.map +1 -0
- package/dist/indexer/boundaries.d.ts +60 -0
- package/dist/indexer/boundaries.d.ts.map +1 -0
- package/dist/indexer/boundaries.js +366 -0
- package/dist/indexer/boundaries.js.map +1 -0
- package/dist/indexer/churn.d.ts +15 -0
- package/dist/indexer/churn.d.ts.map +1 -0
- package/dist/indexer/churn.js +49 -0
- package/dist/indexer/churn.js.map +1 -0
- package/dist/indexer/classify.d.ts +9 -0
- package/dist/indexer/classify.d.ts.map +1 -0
- package/dist/indexer/classify.js +90 -0
- package/dist/indexer/classify.js.map +1 -0
- package/dist/indexer/context.d.ts +176 -0
- package/dist/indexer/context.d.ts.map +1 -0
- package/dist/indexer/context.js +193 -0
- package/dist/indexer/context.js.map +1 -0
- package/dist/indexer/continuity.d.ts +67 -0
- package/dist/indexer/continuity.d.ts.map +1 -0
- package/dist/indexer/continuity.js +288 -0
- package/dist/indexer/continuity.js.map +1 -0
- package/dist/indexer/detectchanges.d.ts +32 -0
- package/dist/indexer/detectchanges.d.ts.map +1 -0
- package/dist/indexer/detectchanges.js +74 -0
- package/dist/indexer/detectchanges.js.map +1 -0
- package/dist/indexer/discovery.d.ts +37 -0
- package/dist/indexer/discovery.d.ts.map +1 -0
- package/dist/indexer/discovery.js +136 -0
- package/dist/indexer/discovery.js.map +1 -0
- package/dist/indexer/externaldeps.d.ts +18 -0
- package/dist/indexer/externaldeps.d.ts.map +1 -0
- package/dist/indexer/externaldeps.js +288 -0
- package/dist/indexer/externaldeps.js.map +1 -0
- package/dist/indexer/freshness.d.ts +48 -0
- package/dist/indexer/freshness.d.ts.map +1 -0
- package/dist/indexer/freshness.js +128 -0
- package/dist/indexer/freshness.js.map +1 -0
- package/dist/indexer/git.d.ts +144 -0
- package/dist/indexer/git.d.ts.map +1 -0
- package/dist/indexer/git.js +444 -0
- package/dist/indexer/git.js.map +1 -0
- package/dist/indexer/index.d.ts +145 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +930 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/modules.d.ts +62 -0
- package/dist/indexer/modules.d.ts.map +1 -0
- package/dist/indexer/modules.js +293 -0
- package/dist/indexer/modules.js.map +1 -0
- package/dist/indexer/preflight.d.ts +154 -0
- package/dist/indexer/preflight.d.ts.map +1 -0
- package/dist/indexer/preflight.js +399 -0
- package/dist/indexer/preflight.js.map +1 -0
- package/dist/indexer/protoScanner.d.ts +34 -0
- package/dist/indexer/protoScanner.d.ts.map +1 -0
- package/dist/indexer/protoScanner.js +133 -0
- package/dist/indexer/protoScanner.js.map +1 -0
- package/dist/indexer/risk.d.ts +115 -0
- package/dist/indexer/risk.d.ts.map +1 -0
- package/dist/indexer/risk.js +194 -0
- package/dist/indexer/risk.js.map +1 -0
- package/dist/indexer/serviceHostScanner.d.ts +25 -0
- package/dist/indexer/serviceHostScanner.d.ts.map +1 -0
- package/dist/indexer/serviceHostScanner.js +95 -0
- package/dist/indexer/serviceHostScanner.js.map +1 -0
- package/dist/indexer/serviceLinks.d.ts +105 -0
- package/dist/indexer/serviceLinks.d.ts.map +1 -0
- package/dist/indexer/serviceLinks.js +509 -0
- package/dist/indexer/serviceLinks.js.map +1 -0
- package/dist/indexer/shapehash.d.ts +98 -0
- package/dist/indexer/shapehash.d.ts.map +1 -0
- package/dist/indexer/shapehash.js +354 -0
- package/dist/indexer/shapehash.js.map +1 -0
- package/dist/indexer/skeleton.d.ts +15 -0
- package/dist/indexer/skeleton.d.ts.map +1 -0
- package/dist/indexer/skeleton.js +136 -0
- package/dist/indexer/skeleton.js.map +1 -0
- package/dist/indexer/symbolhistory.d.ts +41 -0
- package/dist/indexer/symbolhistory.d.ts.map +1 -0
- package/dist/indexer/symbolhistory.js +124 -0
- package/dist/indexer/symbolhistory.js.map +1 -0
- package/dist/indexer/watcher.d.ts +68 -0
- package/dist/indexer/watcher.d.ts.map +1 -0
- package/dist/indexer/watcher.js +179 -0
- package/dist/indexer/watcher.js.map +1 -0
- package/dist/mcp/server.d.ts +80 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +1610 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/parser/index.d.ts +8 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +33 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/languages/cpp.d.ts +3 -0
- package/dist/parser/languages/cpp.d.ts.map +1 -0
- package/dist/parser/languages/cpp.js +350 -0
- package/dist/parser/languages/cpp.js.map +1 -0
- package/dist/parser/languages/csharp.d.ts +3 -0
- package/dist/parser/languages/csharp.d.ts.map +1 -0
- package/dist/parser/languages/csharp.js +239 -0
- package/dist/parser/languages/csharp.js.map +1 -0
- package/dist/parser/languages/go.d.ts +3 -0
- package/dist/parser/languages/go.d.ts.map +1 -0
- package/dist/parser/languages/go.js +259 -0
- package/dist/parser/languages/go.js.map +1 -0
- package/dist/parser/languages/java.d.ts +3 -0
- package/dist/parser/languages/java.d.ts.map +1 -0
- package/dist/parser/languages/java.js +391 -0
- package/dist/parser/languages/java.js.map +1 -0
- package/dist/parser/languages/python.d.ts +3 -0
- package/dist/parser/languages/python.d.ts.map +1 -0
- package/dist/parser/languages/python.js +396 -0
- package/dist/parser/languages/python.js.map +1 -0
- package/dist/parser/languages/rust.d.ts +3 -0
- package/dist/parser/languages/rust.d.ts.map +1 -0
- package/dist/parser/languages/rust.js +159 -0
- package/dist/parser/languages/rust.js.map +1 -0
- package/dist/parser/languages/typescript.d.ts +3 -0
- package/dist/parser/languages/typescript.d.ts.map +1 -0
- package/dist/parser/languages/typescript.js +1442 -0
- package/dist/parser/languages/typescript.js.map +1 -0
- package/dist/parser/parserContext.d.ts +77 -0
- package/dist/parser/parserContext.d.ts.map +1 -0
- package/dist/parser/parserContext.js +354 -0
- package/dist/parser/parserContext.js.map +1 -0
- package/dist/parser/walker.d.ts +81 -0
- package/dist/parser/walker.d.ts.map +1 -0
- package/dist/parser/walker.js +217 -0
- package/dist/parser/walker.js.map +1 -0
- package/dist/parser/worker.d.ts +66 -0
- package/dist/parser/worker.d.ts.map +1 -0
- package/dist/parser/worker.js +129 -0
- package/dist/parser/worker.js.map +1 -0
- package/dist/parser/workerpool.d.ts +107 -0
- package/dist/parser/workerpool.d.ts.map +1 -0
- package/dist/parser/workerpool.js +383 -0
- package/dist/parser/workerpool.js.map +1 -0
- package/dist/scip/format.d.ts +87 -0
- package/dist/scip/format.d.ts.map +1 -0
- package/dist/scip/format.js +31 -0
- package/dist/scip/format.js.map +1 -0
- package/dist/scip/import.d.ts +37 -0
- package/dist/scip/import.d.ts.map +1 -0
- package/dist/scip/import.js +180 -0
- package/dist/scip/import.js.map +1 -0
- package/dist/types.d.ts +392 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/docs/architecture.md +105 -0
- package/docs/benchmarks/methodology.md +134 -0
- package/docs/benchmarks/raw-results.md +71 -0
- package/docs/benchmarks.md +74 -0
- package/docs/cli.md +148 -0
- package/docs/examples/behavior-tests.md +70 -0
- package/docs/examples/change-history.md +85 -0
- package/docs/examples/pre-edit-context.md +81 -0
- package/docs/examples/service-links.md +88 -0
- package/docs/examples.md +80 -0
- package/docs/faq.md +70 -0
- package/docs/internals.md +104 -0
- package/docs/languages.md +70 -0
- package/docs/limits.md +52 -0
- package/docs/mcp.md +199 -0
- package/docs/quickstart.md +119 -0
- package/docs/testing.md +123 -0
- package/docs/tools.md +115 -0
- package/package.json +52 -0
- package/research-codebase.md +578 -0
- package/seer-cli-docs.md +326 -0
- package/seer-master-guide.md +246 -0
- package/src/bundle/ci.ts +141 -0
- package/src/bundle/contract.ts +387 -0
- package/src/bundle/export.ts +175 -0
- package/src/bundle/external.ts +285 -0
- package/src/bundle/format.ts +92 -0
- package/src/bundle/import.ts +157 -0
- package/src/cli/index.ts +1249 -0
- package/src/cli/init.ts +389 -0
- package/src/db/schema.ts +614 -0
- package/src/db/store.ts +4306 -0
- package/src/graph/pagerank.ts +53 -0
- package/src/indexer/architecture.ts +148 -0
- package/src/indexer/behavior.ts +466 -0
- package/src/indexer/boundaries.ts +374 -0
- package/src/indexer/churn.ts +58 -0
- package/src/indexer/classify.ts +96 -0
- package/src/indexer/context.ts +340 -0
- package/src/indexer/continuity.ts +322 -0
- package/src/indexer/detectchanges.ts +94 -0
- package/src/indexer/discovery.ts +176 -0
- package/src/indexer/externaldeps.ts +243 -0
- package/src/indexer/freshness.ts +166 -0
- package/src/indexer/git.ts +453 -0
- package/src/indexer/index.ts +1092 -0
- package/src/indexer/modules.ts +358 -0
- package/src/indexer/preflight.ts +548 -0
- package/src/indexer/protoScanner.ts +147 -0
- package/src/indexer/risk.ts +304 -0
- package/src/indexer/serviceHostScanner.ts +92 -0
- package/src/indexer/serviceLinks.ts +543 -0
- package/src/indexer/shapehash.ts +370 -0
- package/src/indexer/skeleton.ts +169 -0
- package/src/indexer/symbolhistory.ts +172 -0
- package/src/indexer/watcher.ts +206 -0
- package/src/mcp/server.ts +1659 -0
- package/src/parser/index.ts +37 -0
- package/src/parser/languages/cpp.ts +361 -0
- package/src/parser/languages/csharp.ts +235 -0
- package/src/parser/languages/go.ts +259 -0
- package/src/parser/languages/java.ts +382 -0
- package/src/parser/languages/python.ts +370 -0
- package/src/parser/languages/rust.ts +164 -0
- package/src/parser/languages/typescript.ts +1435 -0
- package/src/parser/parserContext.ts +392 -0
- package/src/parser/walker.ts +306 -0
- package/src/parser/worker.ts +181 -0
- package/src/parser/workerpool.ts +448 -0
- package/src/scip/format.ts +83 -0
- package/src/scip/import.ts +216 -0
- package/src/types.ts +457 -0
- package/tests/benchmark-service-links.ts +244 -0
- package/tests/bug-regressions.ts +626 -0
- package/tests/filters.ts +264 -0
- package/tests/fixtures/Counter.tsx +38 -0
- package/tests/fixtures/caller.ts +7 -0
- package/tests/fixtures/collisions.ts +23 -0
- package/tests/fixtures/local_helper.ts +5 -0
- package/tests/fixtures/overloads.java +17 -0
- package/tests/fixtures/remote_helper.ts +4 -0
- package/tests/fixtures/sample.c +15 -0
- package/tests/fixtures/sample.cpp +47 -0
- package/tests/fixtures/sample.cs +62 -0
- package/tests/fixtures/sample.go +68 -0
- package/tests/fixtures/sample.h +30 -0
- package/tests/fixtures/sample.java +85 -0
- package/tests/fixtures/sample.py +46 -0
- package/tests/fixtures/sample.rs +78 -0
- package/tests/fixtures/sample.ts +76 -0
- package/tests/fixtures-service/HttpClients.cs +30 -0
- package/tests/fixtures-service/HttpClients.java +24 -0
- package/tests/fixtures-service/billing.ts +15 -0
- package/tests/fixtures-service/docker-compose.yml +15 -0
- package/tests/fixtures-service/gateway.ts +10 -0
- package/tests/fixtures-service/get_user.ts +11 -0
- package/tests/fixtures-service/graphql_client.ts +63 -0
- package/tests/fixtures-service/graphql_server.ts +30 -0
- package/tests/fixtures-service/grpc_client.go +30 -0
- package/tests/fixtures-service/http_clients.go +23 -0
- package/tests/fixtures-service/http_clients.py +38 -0
- package/tests/fixtures-service/http_clients.ts +49 -0
- package/tests/fixtures-service/k8s/payment-service.yaml +22 -0
- package/tests/fixtures-service/k8s_calls.ts +20 -0
- package/tests/fixtures-service/messaging.ts +87 -0
- package/tests/fixtures-service/trpc_client.ts +39 -0
- package/tests/fixtures-service/trpc_server.ts +39 -0
- package/tests/fixtures-service/user_service.proto +33 -0
- package/tests/fixtures-trackcd/Cargo.toml +11 -0
- package/tests/fixtures-trackcd/SpringController.java +36 -0
- package/tests/fixtures-trackcd/auth_service.ts +19 -0
- package/tests/fixtures-trackcd/complex_module.py +50 -0
- package/tests/fixtures-trackcd/express_app.js +30 -0
- package/tests/fixtures-trackcd/fastapi_app.py +49 -0
- package/tests/fixtures-trackcd/fastify_object_routes.js +32 -0
- package/tests/fixtures-trackcd/go.mod +8 -0
- package/tests/fixtures-trackcd/package.json +15 -0
- package/tests/fixtures-trackcd/requirements.txt +4 -0
- package/tests/fixtures-trackcd/tests/auth_service.test.ts +13 -0
- package/tests/fixtures-tracke/auth/AuthService.ts +23 -0
- package/tests/fixtures-tracke/auth/crypto.ts +7 -0
- package/tests/fixtures-tracke/billing/Billing.ts +20 -0
- package/tests/fixtures-tracke/billing/Invoice.ts +10 -0
- package/tests/fixtures-tracke/billing/server.ts +17 -0
- package/tests/fixtures-tracke/package.json +7 -0
- package/tests/fixtures-tracke/tests/auth.test.ts +23 -0
- package/tests/fixtures-tracke/tests/billing.test.ts +14 -0
- package/tests/fixtures-trackf/package.json +5 -0
- package/tests/fixtures-trackf/src/auth.ts +26 -0
- package/tests/fixtures-trackf/src/handlers.ts +35 -0
- package/tests/fixtures-tracki/billing/routes.ts +12 -0
- package/tests/fixtures-tracki/gateway/client.ts +13 -0
- package/tests/git-features.ts +267 -0
- package/tests/init.ts +141 -0
- package/tests/mcp-jit.ts +130 -0
- package/tests/mcp-smoke.ts +191 -0
- package/tests/mcp-trackcd.ts +169 -0
- package/tests/mcp-tracke.ts +229 -0
- package/tests/mcp-trackf.ts +330 -0
- package/tests/mcp-trackg.ts +219 -0
- package/tests/mcp-tracki.ts +174 -0
- package/tests/mcp-watcher.ts +126 -0
- package/tests/optspec.ts +194 -0
- package/tests/parallel-index.ts +333 -0
- package/tests/parallel-read.ts +125 -0
- package/tests/parallel-recovery.ts +241 -0
- package/tests/perf-callers.ts +145 -0
- package/tests/query-parity.ts +184 -0
- package/tests/query-perf.ts +55 -0
- package/tests/scale-parallel-parity.ts +225 -0
- package/tests/scale-test.ts +523 -0
- package/tests/smoke.ts +396 -0
- package/tests/trackcd.ts +325 -0
- package/tests/tracke-collisions.ts +255 -0
- package/tests/tracke.ts +314 -0
- package/tests/trackf-bugs.ts +406 -0
- package/tests/trackf.ts +390 -0
- package/tests/trackg.ts +1372 -0
- package/tests/tracki-boundaries.ts +202 -0
- package/tests/tracki-continuity.ts +253 -0
- package/tests/tracki-contract-diff.ts +249 -0
- package/tests/tracki-external-bundles.ts +341 -0
- package/tests/tracki-preflight.ts +251 -0
- package/tests/verify-roles.ts +51 -0
- package/tests/worker-parity.ts +286 -0
- package/tests/worker-pool.ts +262 -0
- package/tsconfig.json +20 -0
package/tests/trackf.ts
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track F feature tests — portability and precision.
|
|
3
|
+
*
|
|
4
|
+
* Exercises:
|
|
5
|
+
* - Schema v7 columns (provenance, shape_hash) and scip_imports table
|
|
6
|
+
* - Structural SimHash duplicate detection (shape-folded across renames)
|
|
7
|
+
* - Portable .seerbundle export → import round trip + integrity check
|
|
8
|
+
* - SCIP JSON import: additive precision, source-labelled, idempotent
|
|
9
|
+
* - getStats() includes provenance + scipImports + shapeHashed
|
|
10
|
+
*
|
|
11
|
+
* Run with: npx tsx tests/trackf.ts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import os from 'os';
|
|
17
|
+
import crypto from 'crypto';
|
|
18
|
+
import { Indexer } from '../src/indexer/index';
|
|
19
|
+
import { Store } from '../src/db/store';
|
|
20
|
+
import { exportBundle } from '../src/bundle/export';
|
|
21
|
+
import { importBundle, readBundleManifest } from '../src/bundle/import';
|
|
22
|
+
import { importScip } from '../src/scip/import';
|
|
23
|
+
import {
|
|
24
|
+
computeShapeHash, hammingDistance, tokenize, findDuplicates, buildShapeHashes,
|
|
25
|
+
} from '../src/indexer/shapehash';
|
|
26
|
+
|
|
27
|
+
const FIXTURES = path.join(__dirname, 'fixtures-trackf');
|
|
28
|
+
const TMP_DIR = path.join(os.tmpdir(), `seer-trackf-${Date.now()}`);
|
|
29
|
+
const TMP_DB = path.join(TMP_DIR, 'graph.db');
|
|
30
|
+
|
|
31
|
+
let passed = 0;
|
|
32
|
+
let failed = 0;
|
|
33
|
+
|
|
34
|
+
function assert(cond: boolean, msg: string): void {
|
|
35
|
+
if (cond) { console.log(` ✓ ${msg}`); passed++; }
|
|
36
|
+
else { console.error(` ✗ ${msg}`); failed++; }
|
|
37
|
+
}
|
|
38
|
+
function safeStr(v: unknown): string {
|
|
39
|
+
if (typeof v === 'bigint') return `0x${v.toString(16)}`;
|
|
40
|
+
try { return JSON.stringify(v); }
|
|
41
|
+
catch { return String(v); }
|
|
42
|
+
}
|
|
43
|
+
function assertEq<T>(actual: T, expected: T, msg: string): void {
|
|
44
|
+
assert(actual === expected, `${msg} (got ${safeStr(actual)}, expected ${safeStr(expected)})`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function cleanup(): void {
|
|
48
|
+
try { fs.rmSync(TMP_DIR, { recursive: true, force: true }); } catch { /* */ }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function run(): Promise<void> {
|
|
52
|
+
console.log('\nSeer Track F Feature Tests');
|
|
53
|
+
console.log('============================\n');
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(FIXTURES)) {
|
|
56
|
+
console.error(`Missing fixtures dir: ${FIXTURES}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fs.mkdirSync(TMP_DIR, { recursive: true });
|
|
61
|
+
|
|
62
|
+
// ── Tokenizer & shape hash unit tests ───────────────────────────────────
|
|
63
|
+
console.log('── tokenize() + computeShapeHash() ──');
|
|
64
|
+
const tokA = tokenize('function foo(x: number) { return x + 1; }');
|
|
65
|
+
const tokB = tokenize('function bar(y: number) { return y + 1; }');
|
|
66
|
+
// Renames fold to NAME — token streams must equal.
|
|
67
|
+
assertEq(JSON.stringify(tokA), JSON.stringify(tokB), 'identifier-folded token streams match across renames');
|
|
68
|
+
|
|
69
|
+
const opCount = tokA.filter(t => t.kind === 'OP').length;
|
|
70
|
+
assert(opCount > 0, 'tokenizer emits OP tokens');
|
|
71
|
+
assert(tokA.every(t => t.kind === 'NAME' || t.kind === 'OP' || t.kind === 'NUMBER'),
|
|
72
|
+
'token kinds are folded categories');
|
|
73
|
+
|
|
74
|
+
const hashA = computeShapeHash('function foo(x: number) { return x + 1; }\n'.repeat(3));
|
|
75
|
+
const hashB = computeShapeHash('function bar(y: number) { return y + 1; }\n'.repeat(3));
|
|
76
|
+
assert(hashA !== null && hashB !== null, 'computeShapeHash returns a hash for both');
|
|
77
|
+
if (hashA && hashB) {
|
|
78
|
+
assertEq(hashA, hashB, 'shape hash identical after renames');
|
|
79
|
+
assertEq(hammingDistance(hashA, hashB), 0, 'Hamming distance = 0 for identical shapes');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const hashC = computeShapeHash('function totallyDifferent() {\n while (true) {\n if (Math.random() > 0.5) {\n break;\n } else {\n continue;\n }\n }\n}\n');
|
|
83
|
+
if (hashA && hashC) {
|
|
84
|
+
const d = hammingDistance(hashA, hashC);
|
|
85
|
+
assert(d > 6, `differently-shaped functions produce a different hash (distance ${d} > 6)`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const hashTiny = computeShapeHash('x;');
|
|
89
|
+
assertEq(hashTiny, null, 'tiny input below minTokens → null hash');
|
|
90
|
+
|
|
91
|
+
// ── Index the fixtures ───────────────────────────────────────────────────
|
|
92
|
+
console.log('\n── Indexing fixtures-trackf/ ──');
|
|
93
|
+
const store = new Store(TMP_DB);
|
|
94
|
+
const indexer = new Indexer(store);
|
|
95
|
+
const r = await indexer.indexDirectory(FIXTURES, { quiet: true });
|
|
96
|
+
console.log(` files=${r.filesIndexed} symbols=${r.symbols} edges=${r.edges} shapeHashesAdded=${r.shapeHashesAdded}`);
|
|
97
|
+
assert(r.filesIndexed >= 2, 'indexed both source files');
|
|
98
|
+
assert((r.shapeHashesAdded ?? 0) >= 3, `shape hash pass ran during index (got ${r.shapeHashesAdded})`);
|
|
99
|
+
|
|
100
|
+
// ── Schema is v9 with v7 columns still present ──────────────────────────
|
|
101
|
+
console.log('\n── Schema v9 (v7 columns intact) ──');
|
|
102
|
+
const schema = store.schemaInfo();
|
|
103
|
+
assertEq(schema.current, true, 'schema is current');
|
|
104
|
+
assertEq(schema.dbVersion, 10, 'schema version is v10');
|
|
105
|
+
assertEq(store.hasV7(), true, 'hasV7() reports true');
|
|
106
|
+
|
|
107
|
+
const cols = store.rawDb().prepare("PRAGMA table_info('symbols')").all() as Array<{ name: string }>;
|
|
108
|
+
const colNames = new Set(cols.map(c => String(c.name)));
|
|
109
|
+
assert(colNames.has('provenance'), 'symbols.provenance column exists');
|
|
110
|
+
assert(colNames.has('shape_hash'), 'symbols.shape_hash column exists');
|
|
111
|
+
const edgeCols = store.rawDb().prepare("PRAGMA table_info('edges')").all() as Array<{ name: string }>;
|
|
112
|
+
assert(edgeCols.some(c => String(c.name) === 'provenance'), 'edges.provenance column exists');
|
|
113
|
+
|
|
114
|
+
// ── Default provenance is tree-sitter ────────────────────────────────────
|
|
115
|
+
console.log('\n── Default provenance ──');
|
|
116
|
+
const stats = store.getStats();
|
|
117
|
+
assert((stats.provenance?.symbols['tree-sitter'] ?? 0) > 0, 'tree-sitter symbols present');
|
|
118
|
+
assertEq(stats.provenance?.symbols.scip ?? 0, 0, 'no SCIP symbols yet');
|
|
119
|
+
assertEq(stats.scipImports ?? -1, 0, 'no SCIP imports yet');
|
|
120
|
+
assert((stats.shapeHashed ?? 0) >= 3, `shapeHashed counter ≥ 3 (got ${stats.shapeHashed})`);
|
|
121
|
+
|
|
122
|
+
// ── SimHash duplicates: fetchUserById ≡ fetchOrderById ───────────────────
|
|
123
|
+
console.log('\n── Duplicate detection (SimHash) ──');
|
|
124
|
+
const clusters = findDuplicates(store, { maxDistance: 4, minLoc: 3 });
|
|
125
|
+
console.log(` ${clusters.length} cluster(s):`);
|
|
126
|
+
for (const c of clusters) {
|
|
127
|
+
console.log(` fp=${c.fingerprint.toString(16).slice(0, 12)} size=${c.symbols.length}`);
|
|
128
|
+
for (const s of c.symbols) {
|
|
129
|
+
console.log(` d=${s.hammingFromAnchor} ${s.qualifiedName ?? s.name} ${s.file}:${s.lineStart + 1}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
assert(clusters.length >= 1, 'at least one duplicate cluster found');
|
|
133
|
+
const fetchCluster = clusters.find(c =>
|
|
134
|
+
c.symbols.some(s => s.name === 'fetchUserById') &&
|
|
135
|
+
c.symbols.some(s => s.name === 'fetchOrderById'));
|
|
136
|
+
assert(fetchCluster !== undefined,
|
|
137
|
+
'fetchUserById + fetchOrderById are in the same duplicate cluster');
|
|
138
|
+
if (fetchCluster) {
|
|
139
|
+
// The renamed-twin pair must have hamming distance 0 (identical shape).
|
|
140
|
+
const ord = fetchCluster.symbols.find(s => s.name === 'fetchOrderById')!;
|
|
141
|
+
assertEq(ord.hammingFromAnchor, 0,
|
|
142
|
+
'fetchOrderById has 0 Hamming distance from the fetchUserById anchor');
|
|
143
|
+
}
|
|
144
|
+
// sumNumbers must NOT be in the fetch cluster.
|
|
145
|
+
const sumInFetchCluster = fetchCluster?.symbols.some(s => s.name === 'sumNumbers') ?? false;
|
|
146
|
+
assertEq(sumInFetchCluster, false, 'sumNumbers is not clustered with the fetch* twins');
|
|
147
|
+
|
|
148
|
+
// Forcing re-hash is a no-op on hashed symbols (idempotent counters).
|
|
149
|
+
const reHash = buildShapeHashes(store);
|
|
150
|
+
assertEq(reHash.symbolsHashed, 0,
|
|
151
|
+
're-running buildShapeHashes is a no-op (no symbols missing a hash)');
|
|
152
|
+
|
|
153
|
+
// ── Portable bundle: export → integrity round-trip → import ──────────────
|
|
154
|
+
console.log('\n── Portable bundle (export + import) ──');
|
|
155
|
+
const bundleOut = path.join(TMP_DIR, 'test.seerbundle');
|
|
156
|
+
const exp = await exportBundle(TMP_DB, FIXTURES, { out: bundleOut, compressionLevel: 9 });
|
|
157
|
+
assert(fs.existsSync(bundleOut), 'bundle file exists on disk');
|
|
158
|
+
assert(exp.bytes > 100, `bundle has meaningful size (${exp.bytes} bytes)`);
|
|
159
|
+
assertEq(exp.manifest.schemaVersion, 10, 'manifest.schemaVersion = 10');
|
|
160
|
+
assertEq(exp.manifest.bundleFormatVersion, 1, 'manifest.bundleFormatVersion = 1');
|
|
161
|
+
assert(exp.manifest.source.rosterHash.length === 64, 'rosterHash is sha256-length');
|
|
162
|
+
assert(exp.manifest.dbSha256.length === 64, 'dbSha256 is sha256-length');
|
|
163
|
+
assert(exp.manifest.index.symbols >= 3, 'manifest.index.symbols ≥ 3');
|
|
164
|
+
assert(exp.manifest.index.modules >= 0, 'manifest.index.modules present');
|
|
165
|
+
assert(exp.manifest.scipImports.length === 0, 'manifest.scipImports empty pre-SCIP');
|
|
166
|
+
|
|
167
|
+
// readBundleManifest doesn't decompress the DB — should be cheap.
|
|
168
|
+
const manifestPeek = readBundleManifest(bundleOut);
|
|
169
|
+
assertEq(manifestPeek.dbSha256, exp.manifest.dbSha256, 'readBundleManifest matches the exporter');
|
|
170
|
+
|
|
171
|
+
// Import to a different DB location and verify it works.
|
|
172
|
+
store.close();
|
|
173
|
+
const importedDb = path.join(TMP_DIR, 'imported.db');
|
|
174
|
+
const imp = await importBundle(bundleOut, {
|
|
175
|
+
repoRoot: FIXTURES, dbOut: importedDb,
|
|
176
|
+
});
|
|
177
|
+
assert(fs.existsSync(importedDb), 'imported DB file exists');
|
|
178
|
+
assertEq(imp.manifest.dbSha256, exp.manifest.dbSha256, 'import manifest sha matches export');
|
|
179
|
+
assertEq(imp.dbPath, importedDb, 'import wrote to requested path');
|
|
180
|
+
|
|
181
|
+
// Open the imported DB and verify it has the same symbol count.
|
|
182
|
+
const imported = Store.openReadOnly(importedDb);
|
|
183
|
+
try {
|
|
184
|
+
const istat = imported.getStats();
|
|
185
|
+
assertEq(istat.symbols, exp.manifest.index.symbols,
|
|
186
|
+
'imported DB has the same symbol count as the manifest');
|
|
187
|
+
assertEq(istat.edges, exp.manifest.index.edges,
|
|
188
|
+
'imported DB has the same edge count as the manifest');
|
|
189
|
+
assertEq(imported.schemaInfo().dbVersion, 10, 'imported DB is schema v9');
|
|
190
|
+
} finally { imported.close(); }
|
|
191
|
+
|
|
192
|
+
// Refuse-to-overwrite contract.
|
|
193
|
+
let threw = false;
|
|
194
|
+
try {
|
|
195
|
+
await importBundle(bundleOut, { repoRoot: FIXTURES, dbOut: importedDb });
|
|
196
|
+
} catch (err) {
|
|
197
|
+
threw = (err as Error).message.includes('overwrite');
|
|
198
|
+
}
|
|
199
|
+
assert(threw, 'second import without overwrite=true refuses');
|
|
200
|
+
|
|
201
|
+
// Overwrite explicit.
|
|
202
|
+
const imp2 = await importBundle(bundleOut, {
|
|
203
|
+
repoRoot: FIXTURES, dbOut: importedDb, overwrite: true,
|
|
204
|
+
});
|
|
205
|
+
assertEq(imp2.dbPath, importedDb, 'overwrite=true completes');
|
|
206
|
+
|
|
207
|
+
// Corrupted bundle: tamper one byte → integrity check fails.
|
|
208
|
+
console.log('\n── Bundle integrity check ──');
|
|
209
|
+
const badBundle = path.join(TMP_DIR, 'bad.seerbundle');
|
|
210
|
+
const orig = fs.readFileSync(bundleOut);
|
|
211
|
+
const tampered = Buffer.from(orig);
|
|
212
|
+
// Flip a byte well inside the compressed DB section (past manifest).
|
|
213
|
+
const manifestLen = orig.readUInt32BE(8);
|
|
214
|
+
const flipIndex = 12 + manifestLen + 24;
|
|
215
|
+
if (flipIndex < tampered.length) tampered[flipIndex] = tampered[flipIndex] ^ 0xFF;
|
|
216
|
+
fs.writeFileSync(badBundle, tampered);
|
|
217
|
+
threw = false;
|
|
218
|
+
let badErr: string | null = null;
|
|
219
|
+
try {
|
|
220
|
+
await importBundle(badBundle, {
|
|
221
|
+
repoRoot: FIXTURES, dbOut: path.join(TMP_DIR, 'bad-out.db'),
|
|
222
|
+
});
|
|
223
|
+
} catch (err) {
|
|
224
|
+
badErr = (err as Error).message;
|
|
225
|
+
// Any of: explicit integrity check, gunzip decode error, sha mismatch.
|
|
226
|
+
threw = true;
|
|
227
|
+
}
|
|
228
|
+
assert(threw, `tampered bundle is rejected (got error: ${badErr})`);
|
|
229
|
+
|
|
230
|
+
// Bad magic.
|
|
231
|
+
const noMagic = path.join(TMP_DIR, 'no-magic.seerbundle');
|
|
232
|
+
fs.writeFileSync(noMagic, Buffer.from('NOPE0001000000000', 'utf-8'));
|
|
233
|
+
threw = false;
|
|
234
|
+
try {
|
|
235
|
+
await importBundle(noMagic, {
|
|
236
|
+
repoRoot: FIXTURES, dbOut: path.join(TMP_DIR, 'no-magic.db'),
|
|
237
|
+
});
|
|
238
|
+
} catch (err) {
|
|
239
|
+
threw = (err as Error).message.includes('Not a Seer bundle');
|
|
240
|
+
}
|
|
241
|
+
assert(threw, 'bundle with bad magic is rejected');
|
|
242
|
+
|
|
243
|
+
// ── SCIP import: precision overlay, additive, source-labelled ────────────
|
|
244
|
+
console.log('\n── SCIP precision import ──');
|
|
245
|
+
|
|
246
|
+
// Re-open writeable for SCIP.
|
|
247
|
+
const wstore = new Store(TMP_DB);
|
|
248
|
+
try {
|
|
249
|
+
// Pre-SCIP baseline.
|
|
250
|
+
const beforeProv = wstore.getProvenanceCounts();
|
|
251
|
+
const beforeTSSymbols = beforeProv.symbols['tree-sitter'];
|
|
252
|
+
const beforeTSEdges = beforeProv.edges['tree-sitter'];
|
|
253
|
+
assert(beforeTSSymbols > 0, 'tree-sitter symbols exist pre-SCIP');
|
|
254
|
+
|
|
255
|
+
// Hand-author a SCIP doc that adds a new symbol AND confirms an existing
|
|
256
|
+
// tree-sitter one (AuthService.login).
|
|
257
|
+
const scipJson = {
|
|
258
|
+
tool: 'scip-test/0.1.0',
|
|
259
|
+
projectRoot: FIXTURES,
|
|
260
|
+
documents: [
|
|
261
|
+
{
|
|
262
|
+
relativePath: 'src/auth.ts',
|
|
263
|
+
symbols: [
|
|
264
|
+
{
|
|
265
|
+
symbolId: 'auth#AuthService.login',
|
|
266
|
+
displayName: 'login',
|
|
267
|
+
qualifiedName: 'AuthService.login',
|
|
268
|
+
kind: 'method',
|
|
269
|
+
relativePath: 'src/auth.ts',
|
|
270
|
+
// Match the tree-sitter row's line range — should merge.
|
|
271
|
+
range: { startLine: 3, startCharacter: 0, endLine: 6, endCharacter: 1 },
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
symbolId: 'auth#scipOnlyHelper',
|
|
275
|
+
displayName: 'scipOnlyHelper',
|
|
276
|
+
qualifiedName: 'scipOnlyHelper',
|
|
277
|
+
kind: 'function',
|
|
278
|
+
relativePath: 'src/auth.ts',
|
|
279
|
+
// Outside any existing tree-sitter symbol's range → fresh row.
|
|
280
|
+
range: { startLine: 28, startCharacter: 0, endLine: 30, endCharacter: 1 },
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
occurrences: [
|
|
284
|
+
// reference from scipOnlyHelper → login
|
|
285
|
+
{
|
|
286
|
+
symbolId: 'auth#AuthService.login',
|
|
287
|
+
relativePath: 'src/auth.ts',
|
|
288
|
+
range: { startLine: 29, startCharacter: 2, endLine: 29, endCharacter: 7 },
|
|
289
|
+
role: 'reference',
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
};
|
|
295
|
+
const scipPath = path.join(TMP_DIR, 'auth.scip.json');
|
|
296
|
+
fs.writeFileSync(scipPath, JSON.stringify(scipJson, null, 2));
|
|
297
|
+
|
|
298
|
+
const scipResult = await importScip(scipPath, wstore, { repoRoot: FIXTURES });
|
|
299
|
+
console.log(` docs=${scipResult.documentsProcessed} new=${scipResult.symbolsInserted} merged=${scipResult.symbolsMerged} edges=${scipResult.edgesInserted}`);
|
|
300
|
+
assertEq(scipResult.documentsProcessed, 1, '1 SCIP doc processed');
|
|
301
|
+
assert(scipResult.symbolsInserted >= 1, '≥1 new SCIP symbol inserted (scipOnlyHelper)');
|
|
302
|
+
assert(scipResult.symbolsMerged >= 1, '≥1 SCIP symbol merged with tree-sitter (AuthService.login)');
|
|
303
|
+
assertEq(scipResult.edgesInserted, 1, '1 SCIP edge inserted');
|
|
304
|
+
assertEq(scipResult.filesMissing, 0, 'no SCIP files missing from the index');
|
|
305
|
+
|
|
306
|
+
// tree-sitter rows survive (additive, not destructive).
|
|
307
|
+
const afterProv = wstore.getProvenanceCounts();
|
|
308
|
+
assert(afterProv.symbols['tree-sitter'] + afterProv.symbols['scip-merge'] === beforeTSSymbols,
|
|
309
|
+
'no tree-sitter symbols destroyed (merged ones moved to scip-merge bucket)');
|
|
310
|
+
assert(afterProv.symbols.scip >= 1, 'SCIP-pure symbols exist');
|
|
311
|
+
assert(afterProv.symbols['scip-merge'] >= 1, 'scip-merge symbols exist (precision confirmed)');
|
|
312
|
+
assert(afterProv.edges.scip === 1, '1 SCIP-provenance edge in the DB');
|
|
313
|
+
assert(afterProv.edges['tree-sitter'] >= beforeTSEdges,
|
|
314
|
+
'tree-sitter edges preserved');
|
|
315
|
+
|
|
316
|
+
// Idempotent: re-importing same file is a no-op.
|
|
317
|
+
const reScip = await importScip(scipPath, wstore, { repoRoot: FIXTURES });
|
|
318
|
+
assertEq(reScip.symbolsInserted, 0, 're-import inserts 0 symbols (idempotent)');
|
|
319
|
+
assertEq(reScip.edgesInserted, 0, 're-import inserts 0 edges (idempotent)');
|
|
320
|
+
|
|
321
|
+
// scip_imports table records the layer.
|
|
322
|
+
const scipImports = wstore.listScipImports();
|
|
323
|
+
assertEq(scipImports.length, 1, 'one row in scip_imports');
|
|
324
|
+
assertEq(scipImports[0].tool, 'scip-test/0.1.0', 'tool field captured');
|
|
325
|
+
assert(scipImports[0].sha256.length === 64, 'sha256 stored');
|
|
326
|
+
|
|
327
|
+
// hasScipImport idempotency probe.
|
|
328
|
+
assertEq(wstore.hasScipImport(scipImports[0].path, scipImports[0].sha256), true,
|
|
329
|
+
'hasScipImport(path, sha) = true for the recorded layer');
|
|
330
|
+
|
|
331
|
+
// Modify the SCIP file → sha changes → re-import clears previous SCIP-pure rows.
|
|
332
|
+
const scipJson2 = JSON.parse(JSON.stringify(scipJson));
|
|
333
|
+
scipJson2.tool = 'scip-test/0.2.0';
|
|
334
|
+
fs.writeFileSync(scipPath, JSON.stringify(scipJson2, null, 2));
|
|
335
|
+
const scipResult2 = await importScip(scipPath, wstore, { repoRoot: FIXTURES });
|
|
336
|
+
assert(scipResult2.symbolsInserted + scipResult2.symbolsMerged >= 2,
|
|
337
|
+
're-import after content change inserts/merges again');
|
|
338
|
+
const scipImports2 = wstore.listScipImports();
|
|
339
|
+
assertEq(scipImports2.length, 1, 'still one row (same path, new sha replaces)');
|
|
340
|
+
assertEq(scipImports2[0].tool, 'scip-test/0.2.0', 'tool field updated');
|
|
341
|
+
|
|
342
|
+
// clearScipProvenance(path) removes ONLY that path's SCIP rows.
|
|
343
|
+
const cleared = wstore.clearScipProvenance(scipImports2[0].path);
|
|
344
|
+
assert(cleared >= 1, 'clearScipProvenance removed at least one row');
|
|
345
|
+
assertEq(wstore.listScipImports().length, 0, 'scip_imports table emptied');
|
|
346
|
+
const afterClear = wstore.getProvenanceCounts();
|
|
347
|
+
assertEq(afterClear.symbols.scip, 0, 'no SCIP-pure symbols after clear');
|
|
348
|
+
assertEq(afterClear.symbols['scip-merge'], 0, 'scip-merge demoted back to tree-sitter');
|
|
349
|
+
|
|
350
|
+
// ── Stats includes Track-F fields ──────────────────────────────────────
|
|
351
|
+
console.log('\n── Stats includes Track-F fields ──');
|
|
352
|
+
const finalStats = wstore.getStats();
|
|
353
|
+
assert(finalStats.provenance != null, 'stats.provenance present');
|
|
354
|
+
assert(finalStats.scipImports != null, 'stats.scipImports present');
|
|
355
|
+
assert(finalStats.shapeHashed != null && finalStats.shapeHashed >= 3, 'stats.shapeHashed populated');
|
|
356
|
+
} finally { wstore.close(); }
|
|
357
|
+
|
|
358
|
+
// ── Negative path: importing into a wholly fresh DB should still work ──
|
|
359
|
+
console.log('\n── Bundle import lands an idle DB ──');
|
|
360
|
+
const idleDb = path.join(TMP_DIR, 'idle.db');
|
|
361
|
+
const restored = await importBundle(bundleOut, {
|
|
362
|
+
repoRoot: FIXTURES, dbOut: idleDb,
|
|
363
|
+
});
|
|
364
|
+
assertEq(restored.manifest.schemaVersion, 10, 'restored DB schema v9');
|
|
365
|
+
const restoredStore = Store.openReadOnly(idleDb);
|
|
366
|
+
try {
|
|
367
|
+
const rstat = restoredStore.getStats();
|
|
368
|
+
assertEq(rstat.symbols, exp.manifest.index.symbols,
|
|
369
|
+
'restored idle DB has the exported symbol count');
|
|
370
|
+
} finally { restoredStore.close(); }
|
|
371
|
+
|
|
372
|
+
cleanup();
|
|
373
|
+
|
|
374
|
+
console.log(`\n══════════════════════════════════════════════════════════════`);
|
|
375
|
+
console.log(` Results: ${passed} passed, ${failed} failed`);
|
|
376
|
+
if (failed > 0) {
|
|
377
|
+
console.error('\n TRACK F TESTS FAILED\n');
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
console.log('\n All Track F features verified. ✓\n');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
run().catch(err => {
|
|
384
|
+
console.error('trackf crashed:', err);
|
|
385
|
+
try { cleanup(); } catch { /* */ }
|
|
386
|
+
process.exit(1);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// Silence unused-import warning for crypto (used in test helpers below).
|
|
390
|
+
void crypto;
|