@veewo/gitnexus 1.3.9 → 1.3.11-rc.1
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/dist/benchmark/analyze-memory-sampler.d.ts +10 -0
- package/dist/benchmark/analyze-memory-sampler.js +12 -0
- package/dist/benchmark/analyze-memory-sampler.test.js +12 -0
- package/dist/benchmark/io.test.js +48 -5
- package/dist/benchmark/u2-e2e/config.d.ts +1 -0
- package/dist/benchmark/u2-e2e/retrieval-runner.js +25 -3
- package/dist/benchmark/u2-e2e/retrieval-runner.test.js +44 -1
- package/dist/benchmark/unity-lazy-context-sampler.d.ts +58 -0
- package/dist/benchmark/unity-lazy-context-sampler.js +217 -0
- package/dist/benchmark/unity-lazy-context-sampler.test.js +32 -0
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze-close-policy.d.ts +5 -0
- package/dist/cli/analyze-close-policy.js +9 -0
- package/dist/cli/analyze-close-policy.test.js +12 -0
- package/dist/cli/analyze-multi-scope-regression.test.js +1 -1
- package/dist/cli/analyze-options.d.ts +19 -0
- package/dist/cli/analyze-options.js +35 -0
- package/dist/cli/analyze-options.test.js +42 -1
- package/dist/cli/analyze-runtime-summary.d.ts +2 -0
- package/dist/cli/analyze-runtime-summary.js +9 -0
- package/dist/cli/analyze-runtime-summary.test.js +14 -0
- package/dist/cli/analyze.d.ts +1 -0
- package/dist/cli/analyze.js +95 -41
- package/dist/cli/eval-server.js +3 -0
- package/dist/cli/exit-code.d.ts +13 -0
- package/dist/cli/exit-code.js +25 -0
- package/dist/cli/exit-code.test.js +28 -0
- package/dist/cli/index.js +9 -2
- package/dist/cli/mcp.js +3 -0
- package/dist/cli/repo-manager-alias.test.js +24 -1
- package/dist/cli/setup.js +3 -2
- package/dist/cli/setup.test.js +67 -0
- package/dist/cli/tool.d.ts +3 -1
- package/dist/cli/tool.js +2 -0
- package/dist/core/graph/types.d.ts +1 -1
- package/dist/core/ingestion/filesystem-walker.d.ts +6 -0
- package/dist/core/ingestion/filesystem-walker.js +17 -0
- package/dist/core/ingestion/filesystem-walker.test.js +51 -0
- package/dist/core/ingestion/pipeline.js +4 -3
- package/dist/core/ingestion/unity-parity-seed.d.ts +9 -0
- package/dist/core/ingestion/unity-parity-seed.js +69 -0
- package/dist/core/ingestion/unity-parity-seed.test.d.ts +1 -0
- package/dist/core/ingestion/unity-parity-seed.test.js +35 -0
- package/dist/core/ingestion/unity-resource-processor.d.ts +2 -0
- package/dist/core/ingestion/unity-resource-processor.js +87 -53
- package/dist/core/ingestion/unity-resource-processor.test.js +37 -39
- package/dist/core/kuzu/csv-generator.d.ts +20 -1
- package/dist/core/kuzu/csv-generator.js +92 -25
- package/dist/core/kuzu/csv-generator.test.d.ts +1 -0
- package/dist/core/kuzu/csv-generator.test.js +28 -0
- package/dist/core/kuzu/kuzu-adapter.js +35 -54
- package/dist/core/kuzu/relationship-pair-buckets.d.ts +17 -0
- package/dist/core/kuzu/relationship-pair-buckets.js +79 -0
- package/dist/core/kuzu/relationship-pair-buckets.test.d.ts +1 -0
- package/dist/core/kuzu/relationship-pair-buckets.test.js +10 -0
- package/dist/core/kuzu/schema.d.ts +1 -1
- package/dist/core/kuzu/schema.js +1 -0
- package/dist/core/unity/options.d.ts +2 -0
- package/dist/core/unity/options.js +9 -0
- package/dist/core/unity/options.test.js +8 -1
- package/dist/core/unity/resolver.d.ts +3 -0
- package/dist/core/unity/resolver.js +56 -2
- package/dist/core/unity/resolver.test.js +46 -0
- package/dist/core/unity/scan-context.d.ts +5 -0
- package/dist/core/unity/scan-context.js +133 -44
- package/dist/core/unity/scan-context.test.js +41 -2
- package/dist/core/unity/serialized-type-index.d.ts +5 -0
- package/dist/core/unity/serialized-type-index.js +44 -13
- package/dist/core/unity/serialized-type-index.test.js +9 -1
- package/dist/mcp/local/local-backend.d.ts +16 -0
- package/dist/mcp/local/local-backend.js +320 -4
- package/dist/mcp/local/local-backend.unity-merge.test.d.ts +1 -0
- package/dist/mcp/local/local-backend.unity-merge.test.js +261 -0
- package/dist/mcp/local/unity-enrichment.d.ts +15 -0
- package/dist/mcp/local/unity-enrichment.js +69 -5
- package/dist/mcp/local/unity-enrichment.test.js +69 -1
- package/dist/mcp/local/unity-lazy-config.d.ts +6 -0
- package/dist/mcp/local/unity-lazy-config.js +7 -0
- package/dist/mcp/local/unity-lazy-config.test.d.ts +1 -0
- package/dist/mcp/local/unity-lazy-config.test.js +9 -0
- package/dist/mcp/local/unity-lazy-hydrator.d.ts +15 -0
- package/dist/mcp/local/unity-lazy-hydrator.js +43 -0
- package/dist/mcp/local/unity-lazy-hydrator.test.d.ts +1 -0
- package/dist/mcp/local/unity-lazy-hydrator.test.js +66 -0
- package/dist/mcp/local/unity-lazy-overlay.d.ts +3 -0
- package/dist/mcp/local/unity-lazy-overlay.js +89 -0
- package/dist/mcp/local/unity-lazy-overlay.test.d.ts +1 -0
- package/dist/mcp/local/unity-lazy-overlay.test.js +83 -0
- package/dist/mcp/local/unity-parity-cache.d.ts +7 -0
- package/dist/mcp/local/unity-parity-cache.js +88 -0
- package/dist/mcp/local/unity-parity-cache.test.d.ts +1 -0
- package/dist/mcp/local/unity-parity-cache.test.js +143 -0
- package/dist/mcp/local/unity-parity-seed-loader.d.ts +2 -0
- package/dist/mcp/local/unity-parity-seed-loader.js +30 -0
- package/dist/mcp/local/unity-parity-seed-loader.test.d.ts +1 -0
- package/dist/mcp/local/unity-parity-seed-loader.test.js +25 -0
- package/dist/mcp/local/unity-parity-warmup-queue.d.ts +6 -0
- package/dist/mcp/local/unity-parity-warmup-queue.js +28 -0
- package/dist/mcp/local/unity-parity-warmup-queue.test.d.ts +1 -0
- package/dist/mcp/local/unity-parity-warmup-queue.test.js +15 -0
- package/dist/mcp/resources.js +1 -1
- package/dist/mcp/staleness.js +1 -1
- package/dist/mcp/tools.js +24 -2
- package/dist/storage/repo-manager.d.ts +6 -0
- package/dist/types/pipeline.d.ts +7 -0
- package/package.json +6 -3
- package/skills/gitnexus-cli.md +18 -0
- package/skills/gitnexus-debugging.md +16 -2
- package/skills/gitnexus-exploring.md +15 -1
- package/skills/gitnexus-guide.md +15 -0
- package/skills/gitnexus-impact-analysis.md +2 -0
- package/skills/gitnexus-refactoring.md +5 -1
- package/dist/cli/analyze-custom-modules-regression.test.js +0 -75
- package/dist/cli/analyze-modules-diagnostics.test.js +0 -36
- package/dist/core/ingestion/modules/assignment-engine.d.ts +0 -33
- package/dist/core/ingestion/modules/assignment-engine.js +0 -179
- package/dist/core/ingestion/modules/assignment-engine.test.js +0 -111
- package/dist/core/ingestion/modules/config-loader.d.ts +0 -2
- package/dist/core/ingestion/modules/config-loader.js +0 -186
- package/dist/core/ingestion/modules/config-loader.test.js +0 -57
- package/dist/core/ingestion/modules/rule-matcher.d.ts +0 -12
- package/dist/core/ingestion/modules/rule-matcher.js +0 -63
- package/dist/core/ingestion/modules/rule-matcher.test.js +0 -58
- package/dist/core/ingestion/modules/types.d.ts +0 -44
- package/dist/core/ingestion/modules/types.js +0 -2
- package/dist/mcp/local/cluster-aggregation.d.ts +0 -20
- package/dist/mcp/local/cluster-aggregation.js +0 -48
- package/dist/mcp/local/cluster-aggregation.test.js +0 -22
- /package/dist/{cli/analyze-custom-modules-regression.test.d.ts → benchmark/analyze-memory-sampler.test.d.ts} +0 -0
- /package/dist/{cli/analyze-modules-diagnostics.test.d.ts → benchmark/unity-lazy-context-sampler.test.d.ts} +0 -0
- /package/dist/{core/ingestion/modules/assignment-engine.test.d.ts → cli/analyze-close-policy.test.d.ts} +0 -0
- /package/dist/{core/ingestion/modules/config-loader.test.d.ts → cli/analyze-runtime-summary.test.d.ts} +0 -0
- /package/dist/{core/ingestion/modules/rule-matcher.test.d.ts → cli/exit-code.test.d.ts} +0 -0
- /package/dist/{mcp/local/cluster-aggregation.test.d.ts → core/ingestion/filesystem-walker.test.d.ts} +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function buildAnalyzeMemoryReport(input) {
|
|
2
|
+
return {
|
|
3
|
+
capturedAt: new Date().toISOString(),
|
|
4
|
+
summary: {
|
|
5
|
+
analyzeRealSec: input.analyze.realSec,
|
|
6
|
+
analyzeMaxRssBytes: input.analyze.maxRssBytes,
|
|
7
|
+
coldResourceBindings: input.queryCold.resourceBindings,
|
|
8
|
+
warmResourceBindings: input.queryWarm.resourceBindings,
|
|
9
|
+
},
|
|
10
|
+
input,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { buildAnalyzeMemoryReport } from './analyze-memory-sampler.js';
|
|
4
|
+
test('buildAnalyzeMemoryReport summarizes analyze and query measurements', () => {
|
|
5
|
+
const report = buildAnalyzeMemoryReport({
|
|
6
|
+
analyze: { realSec: 10, maxRssBytes: 1024, phases: { pipelineSec: 3, kuzuSec: 5, ftsSec: 1 } },
|
|
7
|
+
queryCold: { realSec: 2, maxRssBytes: 512, resourceBindings: 4, unityDiagnostics: [] },
|
|
8
|
+
queryWarm: { realSec: 1, maxRssBytes: 256, resourceBindings: 4, unityDiagnostics: [] },
|
|
9
|
+
});
|
|
10
|
+
assert.equal(report.summary.analyzeRealSec, 10);
|
|
11
|
+
assert.equal(report.summary.coldResourceBindings, 4);
|
|
12
|
+
});
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import fs from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
4
7
|
import { loadBenchmarkDataset } from './io.js';
|
|
8
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const projectRoot = path.resolve(here, '../..');
|
|
5
10
|
test('loadBenchmarkDataset parses thresholds and jsonl rows', async () => {
|
|
6
|
-
const root = path.resolve('../benchmarks/unity-baseline/v1');
|
|
11
|
+
const root = path.resolve(projectRoot, '../benchmarks/unity-baseline/v1');
|
|
7
12
|
const ds = await loadBenchmarkDataset(root);
|
|
8
13
|
assert.equal(typeof ds.thresholds.query.precisionMin, 'number');
|
|
9
14
|
assert.ok(ds.symbols.length > 0);
|
|
@@ -11,11 +16,41 @@ test('loadBenchmarkDataset parses thresholds and jsonl rows', async () => {
|
|
|
11
16
|
assert.ok(ds.tasks.length > 0);
|
|
12
17
|
});
|
|
13
18
|
test('loadBenchmarkDataset rejects missing required fields', async () => {
|
|
14
|
-
const badRoot = path.
|
|
15
|
-
|
|
19
|
+
const badRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-bad-dataset-'));
|
|
20
|
+
try {
|
|
21
|
+
await fs.writeFile(path.join(badRoot, 'thresholds.json'), JSON.stringify({
|
|
22
|
+
query: { precisionMin: 0.7, recallMin: 0.7, avgLatencyMsMax: 200, p95LatencyMsMax: 400 },
|
|
23
|
+
context: { coverageMin: 0.7, latencyMsMax: 400 },
|
|
24
|
+
impact: { recallMin: 0.7, avgLatencyMsMax: 400 },
|
|
25
|
+
}), 'utf-8');
|
|
26
|
+
await fs.writeFile(path.join(badRoot, 'symbols.jsonl'), `${JSON.stringify({
|
|
27
|
+
symbol_uid: 'Class:Foo',
|
|
28
|
+
file_path: 'Assets/Foo.cs',
|
|
29
|
+
symbol_name: 'Foo',
|
|
30
|
+
symbol_type: 'Class',
|
|
31
|
+
start_line: 1,
|
|
32
|
+
end_line: 10,
|
|
33
|
+
})}\n`, 'utf-8');
|
|
34
|
+
await fs.writeFile(path.join(badRoot, 'relations.jsonl'), `${JSON.stringify({
|
|
35
|
+
src_uid: 'Class:Foo',
|
|
36
|
+
edge_type: 'CALLS',
|
|
37
|
+
dst_uid: 'Method:Bar',
|
|
38
|
+
must_exist: true,
|
|
39
|
+
})}\n`, 'utf-8');
|
|
40
|
+
await fs.writeFile(path.join(badRoot, 'tasks.jsonl'), `${JSON.stringify({
|
|
41
|
+
tool: 'query',
|
|
42
|
+
input: {},
|
|
43
|
+
must_hit_uids: [],
|
|
44
|
+
// intentionally omit must_not_hit_uids
|
|
45
|
+
})}\n`, 'utf-8');
|
|
46
|
+
await assert.rejects(() => loadBenchmarkDataset(badRoot), /missing required field/i);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await fs.rm(badRoot, { recursive: true, force: true });
|
|
50
|
+
}
|
|
16
51
|
});
|
|
17
52
|
test('loadBenchmarkDataset parses neonspark-v1 dataset', async () => {
|
|
18
|
-
const root = path.resolve('../benchmarks/unity-baseline/neonspark-v1');
|
|
53
|
+
const root = path.resolve(projectRoot, '../benchmarks/unity-baseline/neonspark-v1');
|
|
19
54
|
const ds = await loadBenchmarkDataset(root);
|
|
20
55
|
assert.equal(ds.symbols.length, 20);
|
|
21
56
|
assert.ok(ds.relations.length > 0);
|
|
@@ -24,7 +59,7 @@ test('loadBenchmarkDataset parses neonspark-v1 dataset', async () => {
|
|
|
24
59
|
assert.ok(ds.tasks.some((t) => t.tool === 'impact'));
|
|
25
60
|
});
|
|
26
61
|
test('loadBenchmarkDataset parses neonspark-v2 dataset', async () => {
|
|
27
|
-
const root = path.resolve('../benchmarks/unity-baseline/neonspark-v2');
|
|
62
|
+
const root = path.resolve(projectRoot, '../benchmarks/unity-baseline/neonspark-v2');
|
|
28
63
|
const ds = await loadBenchmarkDataset(root);
|
|
29
64
|
assert.ok(ds.symbols.length >= 40 && ds.symbols.length <= 60);
|
|
30
65
|
assert.ok(ds.relations.length > 0);
|
|
@@ -33,3 +68,11 @@ test('loadBenchmarkDataset parses neonspark-v2 dataset', async () => {
|
|
|
33
68
|
assert.ok(ds.tasks.some((t) => t.tool === 'context'));
|
|
34
69
|
assert.ok(ds.tasks.some((t) => t.tool === 'impact'));
|
|
35
70
|
});
|
|
71
|
+
test('latest unity hydration gate report includes hydrationMetaSummary schema', async () => {
|
|
72
|
+
const reportPath = path.resolve(projectRoot, 'docs/reports/2026-03-15-unity-hydration-gates.json');
|
|
73
|
+
const raw = await fs.readFile(reportPath, 'utf-8');
|
|
74
|
+
const report = JSON.parse(raw);
|
|
75
|
+
assert.ok(report.hydrationMetaSummary);
|
|
76
|
+
assert.equal(typeof report.hydrationMetaSummary.compactNeedsRetryRate, 'number');
|
|
77
|
+
assert.equal(typeof report.hydrationMetaSummary.parityCompleteRate, 'number');
|
|
78
|
+
});
|
|
@@ -17,6 +17,7 @@ export interface SymbolScenario {
|
|
|
17
17
|
kind: 'component' | 'scriptableobject' | 'serializable-class' | 'partial-component';
|
|
18
18
|
objectives: string[];
|
|
19
19
|
contextFileHint?: string;
|
|
20
|
+
contextUnityHydration?: 'compact' | 'parity';
|
|
20
21
|
deepDivePlan: Array<{
|
|
21
22
|
tool: 'query' | 'context' | 'impact' | 'cypher';
|
|
22
23
|
input: Record<string, unknown>;
|
|
@@ -53,12 +53,29 @@ function hasDeepDiveEvidence(output) {
|
|
|
53
53
|
const outgoingRefs = countRefs(output?.outgoing);
|
|
54
54
|
return processSymbols + definitions + candidates + rows + byDepth + impacted + incomingRefs + outgoingRefs > 0;
|
|
55
55
|
}
|
|
56
|
-
function assertScenario(scenario, contextOnOutput, deepDiveOutputs) {
|
|
56
|
+
function assertScenario(scenario, contextOnOutput, deepDiveOutputs, contextUnityHydration) {
|
|
57
57
|
const failures = [];
|
|
58
58
|
const bindings = Array.isArray(contextOnOutput?.resourceBindings) ? contextOnOutput.resourceBindings : [];
|
|
59
59
|
const hasBindings = bindings.length > 0;
|
|
60
60
|
const hasResolvedReferences = bindings.some((binding) => Array.isArray(binding?.resolvedReferences) && binding.resolvedReferences.length > 0);
|
|
61
61
|
const hasAssetTypeBinding = bindings.some((binding) => typeof binding?.resourceType === 'string' && binding.resourceType.length > 0);
|
|
62
|
+
const hydrationMeta = contextOnOutput?.hydrationMeta && typeof contextOnOutput.hydrationMeta === 'object'
|
|
63
|
+
? contextOnOutput.hydrationMeta
|
|
64
|
+
: null;
|
|
65
|
+
if (!hydrationMeta) {
|
|
66
|
+
failures.push(`${scenario.symbol}: context(on) must include hydrationMeta`);
|
|
67
|
+
}
|
|
68
|
+
else if (contextUnityHydration === 'compact') {
|
|
69
|
+
if (typeof hydrationMeta.needsParityRetry !== 'boolean') {
|
|
70
|
+
failures.push(`${scenario.symbol}: context(on) hydrationMeta.needsParityRetry must be boolean`);
|
|
71
|
+
}
|
|
72
|
+
if (hydrationMeta.isComplete === false && hydrationMeta.needsParityRetry !== true) {
|
|
73
|
+
failures.push(`${scenario.symbol}: context(on) incomplete compact response must set hydrationMeta.needsParityRetry=true`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (hydrationMeta.isComplete !== true) {
|
|
77
|
+
failures.push(`${scenario.symbol}: context(on) parity response must set hydrationMeta.isComplete=true`);
|
|
78
|
+
}
|
|
62
79
|
if (scenario.symbol === 'MainUIManager' || scenario.symbol === 'PlayerActor') {
|
|
63
80
|
if (!hasBindings) {
|
|
64
81
|
failures.push(`${scenario.symbol}: context(on) must include resourceBindings`);
|
|
@@ -142,7 +159,12 @@ export async function runSymbolScenario(runner, scenario, repo) {
|
|
|
142
159
|
const t0 = performance.now();
|
|
143
160
|
const contextOff = await runContextWithDisambiguation(runner, scenario, contextOffInput);
|
|
144
161
|
steps.push(buildMetric('context-off', 'context', performance.now() - t0, contextOffInput, contextOff));
|
|
145
|
-
const
|
|
162
|
+
const contextUnityHydration = scenario.contextUnityHydration === 'parity' ? 'parity' : 'compact';
|
|
163
|
+
const contextOnInput = {
|
|
164
|
+
...baseContextInput,
|
|
165
|
+
unity_resources: 'on',
|
|
166
|
+
unity_hydration_mode: contextUnityHydration,
|
|
167
|
+
};
|
|
146
168
|
const t1 = performance.now();
|
|
147
169
|
const contextOn = await runContextWithDisambiguation(runner, scenario, contextOnInput);
|
|
148
170
|
steps.push(buildMetric('context-on', 'context', performance.now() - t1, contextOnInput, contextOn));
|
|
@@ -161,6 +183,6 @@ export async function runSymbolScenario(runner, scenario, repo) {
|
|
|
161
183
|
return {
|
|
162
184
|
symbol: scenario.symbol,
|
|
163
185
|
steps,
|
|
164
|
-
assertions: assertScenario(scenario, contextOn, deepDiveOutputs),
|
|
186
|
+
assertions: assertScenario(scenario, contextOn, deepDiveOutputs, contextUnityHydration),
|
|
165
187
|
};
|
|
166
188
|
}
|
|
@@ -8,6 +8,12 @@ test('runSymbolScenario executes context off/on + deepDive and records metrics',
|
|
|
8
8
|
if (input.unity_resources === 'on') {
|
|
9
9
|
return {
|
|
10
10
|
status: 'found',
|
|
11
|
+
hydrationMeta: {
|
|
12
|
+
requestedMode: 'compact',
|
|
13
|
+
effectiveMode: 'compact',
|
|
14
|
+
isComplete: false,
|
|
15
|
+
needsParityRetry: true,
|
|
16
|
+
},
|
|
11
17
|
resourceBindings: [
|
|
12
18
|
{
|
|
13
19
|
resourcePath: 'Assets/Prefabs/UI.prefab',
|
|
@@ -36,7 +42,11 @@ test('runSymbolScenario executes context off/on + deepDive and records metrics',
|
|
|
36
42
|
});
|
|
37
43
|
test('AssetRef requires context(on) resourceBindings after serializable-class coverage', async () => {
|
|
38
44
|
const noEvidenceRunner = {
|
|
39
|
-
context: async () => ({
|
|
45
|
+
context: async () => ({
|
|
46
|
+
status: 'found',
|
|
47
|
+
hydrationMeta: { requestedMode: 'compact', effectiveMode: 'compact', isComplete: false, needsParityRetry: true },
|
|
48
|
+
resourceBindings: [],
|
|
49
|
+
}),
|
|
40
50
|
query: async () => ({ process_symbols: [] }),
|
|
41
51
|
impact: async () => ({ impactedCount: 0 }),
|
|
42
52
|
cypher: async () => ({ rows: [] }),
|
|
@@ -54,6 +64,7 @@ test('AssetRef requires deep-dive evidence even when context(on) has resourceBin
|
|
|
54
64
|
const noDeepDiveEvidenceRunner = {
|
|
55
65
|
context: async () => ({
|
|
56
66
|
status: 'found',
|
|
67
|
+
hydrationMeta: { requestedMode: 'compact', effectiveMode: 'compact', isComplete: false, needsParityRetry: true },
|
|
57
68
|
resourceBindings: [{ resourcePath: 'Assets/Data/Unlock.asset', resourceType: 'asset' }],
|
|
58
69
|
}),
|
|
59
70
|
query: async () => ({ process_symbols: [] }),
|
|
@@ -73,6 +84,7 @@ test('AssetRef passes when context(on) bindings and deep-dive evidence are both
|
|
|
73
84
|
const satisfiedRunner = {
|
|
74
85
|
context: async () => ({
|
|
75
86
|
status: 'found',
|
|
87
|
+
hydrationMeta: { requestedMode: 'compact', effectiveMode: 'compact', isComplete: false, needsParityRetry: true },
|
|
76
88
|
resourceBindings: [{ resourcePath: 'Assets/Data/Unlock.asset', resourceType: 'asset' }],
|
|
77
89
|
}),
|
|
78
90
|
query: async () => ({ process_symbols: [{ id: 'Class:Assets/Scripts/UnlockContent.cs:UnlockContent' }] }),
|
|
@@ -107,6 +119,12 @@ test('runSymbolScenario retries context with file hint when response is ambiguou
|
|
|
107
119
|
if (input.file_path === hint) {
|
|
108
120
|
return {
|
|
109
121
|
status: 'found',
|
|
122
|
+
hydrationMeta: {
|
|
123
|
+
requestedMode: 'compact',
|
|
124
|
+
effectiveMode: 'compact',
|
|
125
|
+
isComplete: false,
|
|
126
|
+
needsParityRetry: true,
|
|
127
|
+
},
|
|
110
128
|
resourceBindings: [
|
|
111
129
|
{
|
|
112
130
|
resourcePath: 'Assets/Prefabs/Player.prefab',
|
|
@@ -143,3 +161,28 @@ test('runSymbolScenario retries context with file hint when response is ambiguou
|
|
|
143
161
|
assert.equal(out.steps[1]?.output?.status, 'found');
|
|
144
162
|
assert.equal(out.assertions.pass, true);
|
|
145
163
|
});
|
|
164
|
+
test('runSymbolScenario fails when compact context hydrationMeta.needsParityRetry is missing', async () => {
|
|
165
|
+
const runner = {
|
|
166
|
+
context: async (input) => {
|
|
167
|
+
if (input.unity_resources === 'on') {
|
|
168
|
+
return {
|
|
169
|
+
status: 'found',
|
|
170
|
+
hydrationMeta: { requestedMode: 'compact', effectiveMode: 'compact', isComplete: false },
|
|
171
|
+
resourceBindings: [{ resourcePath: 'Assets/Prefabs/A.prefab', resourceType: 'prefab' }],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return { status: 'found' };
|
|
175
|
+
},
|
|
176
|
+
query: async () => ({ process_symbols: [{ id: 'Class:A' }] }),
|
|
177
|
+
impact: async () => ({ impactedCount: 1 }),
|
|
178
|
+
cypher: async () => ({ rows: [] }),
|
|
179
|
+
};
|
|
180
|
+
const out = await runSymbolScenario(runner, {
|
|
181
|
+
symbol: 'MainUIManager',
|
|
182
|
+
kind: 'component',
|
|
183
|
+
objectives: ['verify hydration contract'],
|
|
184
|
+
deepDivePlan: [{ tool: 'query', input: { query: 'MainUIManager' } }],
|
|
185
|
+
});
|
|
186
|
+
assert.equal(out.assertions.pass, false);
|
|
187
|
+
assert.ok(out.assertions.failures.some((f) => f.includes('hydrationMeta.needsParityRetry')));
|
|
188
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface UnityLazyContextMetrics {
|
|
2
|
+
coldMs: number;
|
|
3
|
+
warmMs: number;
|
|
4
|
+
coldMaxRssBytes: number;
|
|
5
|
+
warmMaxRssBytes: number;
|
|
6
|
+
}
|
|
7
|
+
export interface UnityLazyContextThresholds {
|
|
8
|
+
coldMsMax?: number;
|
|
9
|
+
warmMsMax?: number;
|
|
10
|
+
coldMaxRssBytesMax?: number;
|
|
11
|
+
warmMaxRssBytesMax?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface UnityLazyThresholdVerdict {
|
|
14
|
+
pass: boolean;
|
|
15
|
+
checks: Record<string, {
|
|
16
|
+
pass: boolean;
|
|
17
|
+
actual: number;
|
|
18
|
+
expected: number;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export interface UnityLazyContextSample {
|
|
22
|
+
durationMs: number;
|
|
23
|
+
maxRssBytes: number;
|
|
24
|
+
exitCode: number;
|
|
25
|
+
stdout: string;
|
|
26
|
+
stderr: string;
|
|
27
|
+
hydrationMeta?: {
|
|
28
|
+
requestedMode?: string;
|
|
29
|
+
effectiveMode?: string;
|
|
30
|
+
isComplete?: boolean;
|
|
31
|
+
needsParityRetry?: boolean;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface UnityHydrationMetaSummary {
|
|
35
|
+
compactSamples: number;
|
|
36
|
+
paritySamples: number;
|
|
37
|
+
compactNeedsRetryRate: number;
|
|
38
|
+
parityCompleteRate: number;
|
|
39
|
+
}
|
|
40
|
+
export interface UnityLazyContextSamplerConfig {
|
|
41
|
+
targetPath: string;
|
|
42
|
+
repo: string;
|
|
43
|
+
symbol: string;
|
|
44
|
+
file: string;
|
|
45
|
+
unityHydration?: 'compact' | 'parity';
|
|
46
|
+
thresholds?: UnityLazyContextThresholds;
|
|
47
|
+
}
|
|
48
|
+
export type UnityLazyContextRunner = (input: UnityLazyContextSamplerConfig & {
|
|
49
|
+
warm: boolean;
|
|
50
|
+
}) => Promise<UnityLazyContextSample>;
|
|
51
|
+
export declare function evaluateUnityLazyContextThresholds(metrics: UnityLazyContextMetrics, thresholds?: UnityLazyContextThresholds): UnityLazyThresholdVerdict;
|
|
52
|
+
export declare function runUnityLazyContextSampler(runner: UnityLazyContextRunner, config: UnityLazyContextSamplerConfig): Promise<{
|
|
53
|
+
capturedAt: string;
|
|
54
|
+
config: Omit<UnityLazyContextSamplerConfig, 'thresholds'>;
|
|
55
|
+
metrics: UnityLazyContextMetrics;
|
|
56
|
+
hydrationMetaSummary: UnityHydrationMetaSummary;
|
|
57
|
+
thresholdVerdict: UnityLazyThresholdVerdict;
|
|
58
|
+
}>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
export function evaluateUnityLazyContextThresholds(metrics, thresholds) {
|
|
6
|
+
const verdict = { pass: true, checks: {} };
|
|
7
|
+
if (!thresholds) {
|
|
8
|
+
return verdict;
|
|
9
|
+
}
|
|
10
|
+
const checks = [
|
|
11
|
+
['coldMs', metrics.coldMs, thresholds.coldMsMax],
|
|
12
|
+
['warmMs', metrics.warmMs, thresholds.warmMsMax],
|
|
13
|
+
['coldMaxRssBytes', metrics.coldMaxRssBytes, thresholds.coldMaxRssBytesMax],
|
|
14
|
+
['warmMaxRssBytes', metrics.warmMaxRssBytes, thresholds.warmMaxRssBytesMax],
|
|
15
|
+
];
|
|
16
|
+
for (const [name, actual, expected] of checks) {
|
|
17
|
+
if (typeof expected !== 'number')
|
|
18
|
+
continue;
|
|
19
|
+
const pass = actual <= expected;
|
|
20
|
+
verdict.checks[name] = { pass, actual, expected };
|
|
21
|
+
if (!pass)
|
|
22
|
+
verdict.pass = false;
|
|
23
|
+
}
|
|
24
|
+
return verdict;
|
|
25
|
+
}
|
|
26
|
+
export async function runUnityLazyContextSampler(runner, config) {
|
|
27
|
+
const cold = await runner({ ...config, warm: false });
|
|
28
|
+
if (cold.exitCode !== 0) {
|
|
29
|
+
throw new Error(`Cold run failed: ${cold.stderr || cold.stdout}`);
|
|
30
|
+
}
|
|
31
|
+
const warm = await runner({ ...config, warm: true });
|
|
32
|
+
if (warm.exitCode !== 0) {
|
|
33
|
+
throw new Error(`Warm run failed: ${warm.stderr || warm.stdout}`);
|
|
34
|
+
}
|
|
35
|
+
const metrics = {
|
|
36
|
+
coldMs: round1(cold.durationMs),
|
|
37
|
+
warmMs: round1(warm.durationMs),
|
|
38
|
+
coldMaxRssBytes: cold.maxRssBytes,
|
|
39
|
+
warmMaxRssBytes: warm.maxRssBytes,
|
|
40
|
+
};
|
|
41
|
+
const hydrationMetaSummary = summarizeHydrationMeta([cold, warm]);
|
|
42
|
+
return {
|
|
43
|
+
capturedAt: new Date().toISOString(),
|
|
44
|
+
config: {
|
|
45
|
+
targetPath: config.targetPath,
|
|
46
|
+
repo: config.repo,
|
|
47
|
+
symbol: config.symbol,
|
|
48
|
+
file: config.file,
|
|
49
|
+
unityHydration: config.unityHydration || 'compact',
|
|
50
|
+
},
|
|
51
|
+
metrics,
|
|
52
|
+
hydrationMetaSummary,
|
|
53
|
+
thresholdVerdict: evaluateUnityLazyContextThresholds(metrics, config.thresholds),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function runCliContextSample(input) {
|
|
57
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
58
|
+
const thisDir = path.dirname(thisFile);
|
|
59
|
+
const cliPath = path.resolve(thisDir, '../cli/index.js');
|
|
60
|
+
const args = [
|
|
61
|
+
'-l',
|
|
62
|
+
'node',
|
|
63
|
+
cliPath,
|
|
64
|
+
'context',
|
|
65
|
+
input.symbol,
|
|
66
|
+
'--repo',
|
|
67
|
+
input.repo,
|
|
68
|
+
'--file',
|
|
69
|
+
input.file,
|
|
70
|
+
'--unity-resources',
|
|
71
|
+
'auto',
|
|
72
|
+
'--unity-hydration',
|
|
73
|
+
input.unityHydration || 'compact',
|
|
74
|
+
];
|
|
75
|
+
const startedAt = Date.now();
|
|
76
|
+
const proc = spawn('/usr/bin/time', args, { cwd: input.targetPath, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
77
|
+
let stdout = '';
|
|
78
|
+
let stderr = '';
|
|
79
|
+
proc.stdout.on('data', (chunk) => { stdout += chunk.toString(); });
|
|
80
|
+
proc.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
|
|
81
|
+
const exitCode = await new Promise((resolve) => {
|
|
82
|
+
proc.on('close', (code) => resolve(code ?? 1));
|
|
83
|
+
});
|
|
84
|
+
const rssMatch = stderr.match(/maximum resident set size[^0-9]*([0-9]+)|([0-9]+)\s+maximum resident set size/i);
|
|
85
|
+
const maxRssBytes = rssMatch ? Number(rssMatch[1] || rssMatch[2] || 0) : 0;
|
|
86
|
+
const parsedPayload = extractFirstJsonObject(stdout) || extractFirstJsonObject(stderr);
|
|
87
|
+
const hydrationMeta = parsedPayload && typeof parsedPayload === 'object' && parsedPayload.hydrationMeta
|
|
88
|
+
? parsedPayload.hydrationMeta
|
|
89
|
+
: undefined;
|
|
90
|
+
return {
|
|
91
|
+
durationMs: Date.now() - startedAt,
|
|
92
|
+
maxRssBytes,
|
|
93
|
+
exitCode,
|
|
94
|
+
stdout,
|
|
95
|
+
stderr,
|
|
96
|
+
hydrationMeta,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function parseArgs(argv) {
|
|
100
|
+
const get = (name) => {
|
|
101
|
+
const index = argv.indexOf(name);
|
|
102
|
+
if (index === -1 || index + 1 >= argv.length)
|
|
103
|
+
return undefined;
|
|
104
|
+
return argv[index + 1];
|
|
105
|
+
};
|
|
106
|
+
const targetPath = get('--target-path');
|
|
107
|
+
const repo = get('--repo');
|
|
108
|
+
const symbol = get('--symbol');
|
|
109
|
+
const file = get('--file');
|
|
110
|
+
const unityHydrationRaw = String(get('--unity-hydration') || 'compact').trim().toLowerCase();
|
|
111
|
+
const unityHydration = unityHydrationRaw === 'parity' ? 'parity' : 'compact';
|
|
112
|
+
if (!targetPath)
|
|
113
|
+
throw new Error('Missing required arg: --target-path <path>');
|
|
114
|
+
if (!repo)
|
|
115
|
+
throw new Error('Missing required arg: --repo <repo>');
|
|
116
|
+
if (!symbol)
|
|
117
|
+
throw new Error('Missing required arg: --symbol <symbol>');
|
|
118
|
+
if (!file)
|
|
119
|
+
throw new Error('Missing required arg: --file <file>');
|
|
120
|
+
return {
|
|
121
|
+
targetPath: path.resolve(targetPath),
|
|
122
|
+
repo,
|
|
123
|
+
symbol,
|
|
124
|
+
file,
|
|
125
|
+
unityHydration,
|
|
126
|
+
thresholds: get('--thresholds') ? path.resolve(get('--thresholds')) : undefined,
|
|
127
|
+
report: get('--report') ? path.resolve(get('--report')) : undefined,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function round1(value) {
|
|
131
|
+
return Number(value.toFixed(1));
|
|
132
|
+
}
|
|
133
|
+
async function main() {
|
|
134
|
+
const args = parseArgs(process.argv.slice(2));
|
|
135
|
+
const thresholds = args.thresholds
|
|
136
|
+
? JSON.parse(await fs.readFile(args.thresholds, 'utf-8'))
|
|
137
|
+
: undefined;
|
|
138
|
+
const report = await runUnityLazyContextSampler(runCliContextSample, {
|
|
139
|
+
targetPath: args.targetPath,
|
|
140
|
+
repo: args.repo,
|
|
141
|
+
symbol: args.symbol,
|
|
142
|
+
file: args.file,
|
|
143
|
+
unityHydration: args.unityHydration,
|
|
144
|
+
thresholds,
|
|
145
|
+
});
|
|
146
|
+
const payload = JSON.stringify(report, null, 2);
|
|
147
|
+
if (args.report) {
|
|
148
|
+
await fs.mkdir(path.dirname(args.report), { recursive: true });
|
|
149
|
+
await fs.writeFile(args.report, payload, 'utf-8');
|
|
150
|
+
console.log(`[unity-lazy-context-sampler] report written: ${args.report}`);
|
|
151
|
+
}
|
|
152
|
+
console.log(payload);
|
|
153
|
+
if (!report.thresholdVerdict.pass) {
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const modulePath = process.argv[1] ? path.resolve(process.argv[1]) : '';
|
|
158
|
+
if (import.meta.url === `file://${modulePath}`) {
|
|
159
|
+
main().catch((error) => {
|
|
160
|
+
console.error(`[unity-lazy-context-sampler] failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function summarizeHydrationMeta(samples) {
|
|
165
|
+
let compactSamples = 0;
|
|
166
|
+
let compactNeedsRetry = 0;
|
|
167
|
+
let paritySamples = 0;
|
|
168
|
+
let parityComplete = 0;
|
|
169
|
+
for (const sample of samples) {
|
|
170
|
+
const mode = String(sample.hydrationMeta?.effectiveMode || '').toLowerCase();
|
|
171
|
+
if (mode === 'parity') {
|
|
172
|
+
paritySamples += 1;
|
|
173
|
+
if (sample.hydrationMeta?.isComplete === true) {
|
|
174
|
+
parityComplete += 1;
|
|
175
|
+
}
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (mode === 'compact') {
|
|
179
|
+
compactSamples += 1;
|
|
180
|
+
if (sample.hydrationMeta?.needsParityRetry === true) {
|
|
181
|
+
compactNeedsRetry += 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
compactSamples,
|
|
187
|
+
paritySamples,
|
|
188
|
+
compactNeedsRetryRate: compactSamples > 0 ? round1(compactNeedsRetry / compactSamples) : 0,
|
|
189
|
+
parityCompleteRate: paritySamples > 0 ? round1(parityComplete / paritySamples) : 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function extractFirstJsonObject(text) {
|
|
193
|
+
if (!text)
|
|
194
|
+
return null;
|
|
195
|
+
const start = text.indexOf('{');
|
|
196
|
+
if (start < 0)
|
|
197
|
+
return null;
|
|
198
|
+
let depth = 0;
|
|
199
|
+
for (let i = start; i < text.length; i += 1) {
|
|
200
|
+
const char = text[i];
|
|
201
|
+
if (char === '{')
|
|
202
|
+
depth += 1;
|
|
203
|
+
if (char === '}') {
|
|
204
|
+
depth -= 1;
|
|
205
|
+
if (depth === 0) {
|
|
206
|
+
const candidate = text.slice(start, i + 1);
|
|
207
|
+
try {
|
|
208
|
+
return JSON.parse(candidate);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { runUnityLazyContextSampler } from './unity-lazy-context-sampler.js';
|
|
4
|
+
test('sampler emits cold/warm latency and rss metrics with threshold verdict', async () => {
|
|
5
|
+
const fakeRunner = async ({ warm }) => ({
|
|
6
|
+
durationMs: warm ? 420 : 6200,
|
|
7
|
+
maxRssBytes: warm ? 650 * 1024 * 1024 : 1700 * 1024 * 1024,
|
|
8
|
+
exitCode: 0,
|
|
9
|
+
stdout: '',
|
|
10
|
+
stderr: '',
|
|
11
|
+
hydrationMeta: warm
|
|
12
|
+
? { requestedMode: 'compact', effectiveMode: 'parity', isComplete: true, needsParityRetry: false }
|
|
13
|
+
: { requestedMode: 'compact', effectiveMode: 'compact', isComplete: false, needsParityRetry: true },
|
|
14
|
+
});
|
|
15
|
+
const report = await runUnityLazyContextSampler(fakeRunner, {
|
|
16
|
+
targetPath: '/tmp/repo',
|
|
17
|
+
repo: 'neonnew-core',
|
|
18
|
+
symbol: 'DoorObj',
|
|
19
|
+
file: 'Assets/NEON/Code/Game/Doors/DoorObj.cs',
|
|
20
|
+
thresholds: {
|
|
21
|
+
coldMsMax: 7000,
|
|
22
|
+
warmMsMax: 1000,
|
|
23
|
+
coldMaxRssBytesMax: 2 * 1024 * 1024 * 1024,
|
|
24
|
+
warmMaxRssBytesMax: 1 * 1024 * 1024 * 1024,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
assert.ok(report.metrics.coldMs > 0);
|
|
28
|
+
assert.equal(typeof report.hydrationMetaSummary.compactNeedsRetryRate, 'number');
|
|
29
|
+
assert.equal(typeof report.hydrationMetaSummary.parityCompleteRate, 'number');
|
|
30
|
+
assert.ok(typeof report.thresholdVerdict.pass === 'boolean');
|
|
31
|
+
assert.equal(report.thresholdVerdict.pass, true);
|
|
32
|
+
});
|
package/dist/cli/ai-context.js
CHANGED
|
@@ -38,7 +38,7 @@ This project is indexed by GitNexus as **${projectName}** (${stats.nodes || 0} s
|
|
|
38
38
|
2. **Match your task to a skill below** and **read that skill file**
|
|
39
39
|
3. **Follow the skill's workflow and checklist**
|
|
40
40
|
|
|
41
|
-
> If step 1 warns the index is stale,
|
|
41
|
+
> If step 1 warns the index is stale, ask user whether to rebuild index via \`npx -y gitnexus analyze\` first (it reuses previous analyze scope/options by default; add \`--no-reuse-options\` to reset). If user declines, explicitly warn that retrieval may not reflect current codebase. For build/analyze/test commands, use a 10-30 minute timeout; on failure/timeout, report exact tool output and do not auto-retry or silently fall back to glob/grep.
|
|
42
42
|
|
|
43
43
|
## Skills
|
|
44
44
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kuzu native close may segfault on some macOS environments.
|
|
3
|
+
* We skip explicit close on analyze exit there, unless force-enabled.
|
|
4
|
+
*/
|
|
5
|
+
export declare function shouldCloseKuzuOnAnalyzeExit(platform?: NodeJS.Platform, forceCloseOnExit?: string | undefined): boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kuzu native close may segfault on some macOS environments.
|
|
3
|
+
* We skip explicit close on analyze exit there, unless force-enabled.
|
|
4
|
+
*/
|
|
5
|
+
export function shouldCloseKuzuOnAnalyzeExit(platform = process.platform, forceCloseOnExit = process.env.GITNEXUS_FORCE_KUZU_CLOSE_ON_EXIT) {
|
|
6
|
+
if (forceCloseOnExit === '1')
|
|
7
|
+
return true;
|
|
8
|
+
return platform !== 'darwin';
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { shouldCloseKuzuOnAnalyzeExit } from './analyze-close-policy.js';
|
|
4
|
+
test('shouldCloseKuzuOnAnalyzeExit skips close on darwin by default', () => {
|
|
5
|
+
assert.equal(shouldCloseKuzuOnAnalyzeExit('darwin', undefined), false);
|
|
6
|
+
});
|
|
7
|
+
test('shouldCloseKuzuOnAnalyzeExit closes on non-darwin platforms', () => {
|
|
8
|
+
assert.equal(shouldCloseKuzuOnAnalyzeExit('linux', undefined), true);
|
|
9
|
+
});
|
|
10
|
+
test('shouldCloseKuzuOnAnalyzeExit can be force-enabled on darwin', () => {
|
|
11
|
+
assert.equal(shouldCloseKuzuOnAnalyzeExit('darwin', '1'), true);
|
|
12
|
+
});
|
|
@@ -27,6 +27,6 @@ test('pipeline forwards extension-filtered scoped paths to unity enrich', { time
|
|
|
27
27
|
includeExtensions: ['.cs'],
|
|
28
28
|
scopeRules: ['Assets'],
|
|
29
29
|
});
|
|
30
|
-
assert.
|
|
30
|
+
assert.ok((result.unityResult?.bindingCount || 0) > 0);
|
|
31
31
|
assert.ok(result.unityResult?.diagnostics.some((message) => message.includes('scanContext:')));
|
|
32
32
|
});
|
|
@@ -2,6 +2,25 @@ export interface AnalyzeScopeOptions {
|
|
|
2
2
|
scopeManifest?: string;
|
|
3
3
|
scopePrefix?: string[] | string;
|
|
4
4
|
}
|
|
5
|
+
export interface StoredAnalyzeOptions {
|
|
6
|
+
includeExtensions?: string[];
|
|
7
|
+
scopeRules?: string[];
|
|
8
|
+
repoAlias?: string;
|
|
9
|
+
embeddings?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ResolveAnalyzeOptionsInput extends AnalyzeScopeOptions {
|
|
12
|
+
extensions?: string;
|
|
13
|
+
repoAlias?: string;
|
|
14
|
+
embeddings?: boolean;
|
|
15
|
+
reuseOptions?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface EffectiveAnalyzeOptions {
|
|
18
|
+
includeExtensions: string[];
|
|
19
|
+
scopeRules: string[];
|
|
20
|
+
repoAlias?: string;
|
|
21
|
+
embeddings: boolean;
|
|
22
|
+
}
|
|
5
23
|
export declare function parseExtensionList(rawExtensions?: string): string[];
|
|
6
24
|
export declare function normalizeRepoAlias(repoAlias?: string): string | undefined;
|
|
7
25
|
export declare function resolveAnalyzeScopeRules(options?: AnalyzeScopeOptions): Promise<string[]>;
|
|
26
|
+
export declare function resolveEffectiveAnalyzeOptions(options?: ResolveAnalyzeOptionsInput, stored?: StoredAnalyzeOptions): Promise<EffectiveAnalyzeOptions>;
|