@veewo/gitnexus 1.5.0-rc.4 → 1.5.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/agent-context/runner.js +3 -0
- package/dist/benchmark/agent-context/runner.test.js +22 -0
- package/dist/benchmark/agent-context/tool-runner.d.ts +7 -6
- package/dist/benchmark/agent-safe-query-context/io.d.ts +2 -0
- package/dist/benchmark/agent-safe-query-context/io.js +86 -0
- package/dist/benchmark/agent-safe-query-context/io.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/io.test.js +13 -0
- package/dist/benchmark/agent-safe-query-context/report.d.ts +57 -0
- package/dist/benchmark/agent-safe-query-context/report.js +159 -0
- package/dist/benchmark/agent-safe-query-context/report.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/report.test.js +362 -0
- package/dist/benchmark/agent-safe-query-context/runner.d.ts +44 -0
- package/dist/benchmark/agent-safe-query-context/runner.js +406 -0
- package/dist/benchmark/agent-safe-query-context/runner.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/runner.test.js +290 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.d.ts +20 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.js +225 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/semantic-tuple.test.js +122 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.d.ts +47 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.js +128 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.test.d.ts +1 -0
- package/dist/benchmark/agent-safe-query-context/subagent-live.test.js +155 -0
- package/dist/benchmark/agent-safe-query-context/telemetry-tool.d.ts +9 -0
- package/dist/benchmark/agent-safe-query-context/telemetry-tool.js +77 -0
- package/dist/benchmark/agent-safe-query-context/types.d.ts +61 -0
- package/dist/benchmark/agent-safe-query-context/types.js +8 -0
- package/dist/benchmark/analyze-runner.d.ts +1 -1
- package/dist/benchmark/analyze-runner.js +4 -3
- package/dist/benchmark/analyze-runner.test.js +7 -0
- package/dist/benchmark/runtime-poc/provenance-artifact.d.ts +47 -0
- package/dist/benchmark/runtime-poc/provenance-artifact.js +89 -0
- package/dist/benchmark/runtime-poc/runner.d.ts +31 -0
- package/dist/benchmark/runtime-poc/runner.js +163 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.d.ts +8 -0
- package/dist/benchmark/u2-e2e/hydration-policy-repeatability-runner.js +21 -0
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.d.ts +0 -1
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.js +53 -51
- package/dist/benchmark/u2-e2e/phase2-runtime-claim-acceptance-runner.test.js +0 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +1 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +82 -18
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +1 -2
- package/dist/benchmark/u2-e2e/retrieval-runner.js +15 -7
- package/dist/benchmark/u2-e2e/retrieval-runner.test.js +46 -0
- package/dist/cli/ai-context.d.ts +0 -1
- package/dist/cli/ai-context.js +5 -6
- package/dist/cli/ai-context.test.js +8 -0
- package/dist/cli/analyze-options.js +58 -34
- package/dist/cli/analyze-options.test.js +57 -0
- package/dist/cli/analyze-runtime-summary.js +2 -0
- package/dist/cli/analyze-runtime-summary.test.js +12 -0
- package/dist/cli/analyze-summary.d.ts +4 -0
- package/dist/cli/analyze-summary.js +43 -0
- package/dist/cli/analyze-summary.test.js +65 -1
- package/dist/cli/analyze.d.ts +11 -0
- package/dist/cli/analyze.js +34 -5
- package/dist/cli/analyze.test.d.ts +1 -0
- package/dist/cli/analyze.test.js +25 -0
- package/dist/cli/benchmark-agent-context.js +1 -1
- package/dist/cli/benchmark-agent-safe-query-context.d.ts +20 -0
- package/dist/cli/benchmark-agent-safe-query-context.js +39 -0
- package/dist/cli/benchmark-agent-safe-query-context.test.d.ts +1 -0
- package/dist/cli/benchmark-agent-safe-query-context.test.js +271 -0
- package/dist/cli/benchmark-unity.js +1 -1
- package/dist/cli/benchmark-unity.test.js +5 -1
- package/dist/cli/benchmark.d.ts +29 -0
- package/dist/cli/benchmark.js +55 -0
- package/dist/cli/index.js +27 -2
- package/dist/cli/rule-lab.d.ts +3 -7
- package/dist/cli/rule-lab.js +13 -22
- package/dist/cli/rule-lab.test.js +23 -3
- package/dist/cli/scope-manifest-config.d.ts +9 -0
- package/dist/cli/scope-manifest-config.js +37 -0
- package/dist/cli/setup.js +40 -41
- package/dist/cli/setup.test.js +14 -14
- package/dist/cli/sync-manifest.d.ts +27 -0
- package/dist/cli/sync-manifest.js +200 -0
- package/dist/cli/sync-manifest.test.d.ts +1 -0
- package/dist/cli/sync-manifest.test.js +88 -0
- package/dist/cli/tool.d.ts +2 -0
- package/dist/cli/tool.js +2 -0
- package/dist/core/config/unity-config.d.ts +1 -1
- package/dist/core/config/unity-config.js +1 -1
- package/dist/core/ingestion/call-processor.d.ts +2 -1
- package/dist/core/ingestion/call-processor.js +28 -6
- package/dist/core/ingestion/heritage-processor.d.ts +2 -1
- package/dist/core/ingestion/heritage-processor.js +30 -7
- package/dist/core/ingestion/import-processor.d.ts +2 -1
- package/dist/core/ingestion/import-processor.js +28 -6
- package/dist/core/ingestion/parsing-processor.d.ts +5 -3
- package/dist/core/ingestion/parsing-processor.js +46 -13
- package/dist/core/ingestion/pipeline.js +100 -19
- package/dist/core/ingestion/unity-lifecycle-synthetic-calls.test.js +18 -20
- package/dist/core/ingestion/unity-parity-seed.d.ts +2 -1
- package/dist/core/ingestion/unity-parity-seed.js +8 -0
- package/dist/core/ingestion/unity-resource-processor.d.ts +11 -0
- package/dist/core/ingestion/unity-resource-processor.js +102 -0
- package/dist/core/ingestion/unity-resource-processor.test.js +449 -0
- package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +16 -1
- package/dist/core/ingestion/unity-runtime-binding-rules.js +193 -42
- package/dist/core/ingestion/workers/parse-worker.d.ts +2 -0
- package/dist/core/ingestion/workers/parse-worker.js +50 -6
- package/dist/core/lbug/csv-generator.test.js +2 -2
- package/dist/core/tree-sitter/csharp-define-profile.d.ts +6 -0
- package/dist/core/tree-sitter/csharp-define-profile.js +43 -0
- package/dist/core/tree-sitter/csharp-preproc-normalizer.d.ts +14 -0
- package/dist/core/tree-sitter/csharp-preproc-normalizer.js +261 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +10 -0
- package/dist/core/tree-sitter/parser-loader.js +19 -0
- package/dist/core/unity/doc-contract.test.d.ts +1 -0
- package/dist/core/unity/doc-contract.test.js +30 -0
- package/dist/core/unity/prefab-source-scan.d.ts +25 -0
- package/dist/core/unity/prefab-source-scan.js +152 -0
- package/dist/core/unity/prefab-source-scan.test.d.ts +1 -0
- package/dist/core/unity/prefab-source-scan.test.js +70 -0
- package/dist/core/unity/scan-context.d.ts +12 -0
- package/dist/core/unity/scan-context.js +50 -2
- package/dist/core/unity/scan-context.test.js +74 -0
- package/dist/mcp/local/agent-safe-response.d.ts +10 -0
- package/dist/mcp/local/agent-safe-response.js +639 -0
- package/dist/mcp/local/derived-process-reader.js +1 -1
- package/dist/mcp/local/local-backend.d.ts +18 -1
- package/dist/mcp/local/local-backend.js +319 -125
- package/dist/mcp/local/process-confidence.d.ts +1 -2
- package/dist/mcp/local/process-confidence.js +0 -3
- package/dist/mcp/local/process-confidence.test.js +4 -2
- package/dist/mcp/local/process-evidence.d.ts +1 -8
- package/dist/mcp/local/process-evidence.js +1 -23
- package/dist/mcp/local/process-evidence.test.js +2 -16
- package/dist/mcp/local/process-ref.d.ts +1 -1
- package/dist/mcp/local/runtime-chain-closure-evaluator.d.ts +33 -0
- package/dist/mcp/local/runtime-chain-closure-evaluator.js +273 -0
- package/dist/mcp/local/runtime-chain-graph-candidates.d.ts +23 -0
- package/dist/mcp/local/runtime-chain-graph-candidates.js +131 -0
- package/dist/mcp/local/runtime-chain-verify.d.ts +1 -1
- package/dist/mcp/local/runtime-chain-verify.js +149 -138
- package/dist/mcp/local/runtime-chain-verify.test.js +126 -68
- package/dist/mcp/local/runtime-claim-rule-registry.d.ts +4 -0
- package/dist/mcp/local/runtime-claim-rule-registry.js +4 -0
- package/dist/mcp/local/runtime-claim-rule-registry.test.js +37 -4
- package/dist/mcp/local/runtime-claim.d.ts +11 -0
- package/dist/mcp/local/runtime-claim.js +28 -0
- package/dist/mcp/local/unity-evidence-view.d.ts +1 -1
- package/dist/mcp/local/unity-evidence-view.js +1 -1
- package/dist/mcp/local/unity-evidence-view.test.js +22 -0
- package/dist/mcp/tools.js +51 -21
- package/dist/rule-lab/analyze.d.ts +2 -1
- package/dist/rule-lab/analyze.js +94 -59
- package/dist/rule-lab/analyze.test.js +238 -20
- package/dist/rule-lab/curate.d.ts +2 -1
- package/dist/rule-lab/curate.js +24 -3
- package/dist/rule-lab/curate.test.js +65 -0
- package/dist/rule-lab/curation-input-builder.d.ts +45 -0
- package/dist/rule-lab/curation-input-builder.js +133 -0
- package/dist/rule-lab/promote.js +80 -7
- package/dist/rule-lab/promote.test.js +150 -0
- package/dist/rule-lab/review-pack.d.ts +3 -0
- package/dist/rule-lab/review-pack.js +41 -1
- package/dist/rule-lab/review-pack.test.js +67 -0
- package/dist/rule-lab/types.d.ts +29 -0
- package/dist/types/pipeline.d.ts +16 -0
- package/package.json +14 -13
- package/scripts/check-sync-manifest-traceability.mjs +203 -0
- package/scripts/run-node-tests.mjs +61 -0
- package/scripts/tree-sitter-audit-classify.mjs +172 -0
- package/skills/_shared/unity-rule-authoring-contract.md +64 -0
- package/skills/_shared/unity-runtime-process-contract.md +16 -0
- package/skills/gitnexus-cli.md +44 -4
- package/skills/gitnexus-debugging.md +9 -0
- package/skills/gitnexus-exploring.md +66 -18
- package/skills/gitnexus-guide.md +42 -3
- package/skills/gitnexus-impact-analysis.md +8 -0
- package/skills/gitnexus-pr-review.md +8 -0
- package/skills/gitnexus-refactoring.md +8 -0
- package/skills/gitnexus-unity-rule-gen.md +66 -312
|
@@ -43,6 +43,52 @@ test('runSymbolScenario executes context off/on + deepDive and records metrics',
|
|
|
43
43
|
assert.ok(out.steps.every((s) => s.totalTokensEst >= 0));
|
|
44
44
|
assert.equal(out.assertions.pass, true);
|
|
45
45
|
});
|
|
46
|
+
test('runSymbolScenario injects response_profile=full for legacy context/query steps', async () => {
|
|
47
|
+
const seen = [];
|
|
48
|
+
const runner = {
|
|
49
|
+
context: async (input) => {
|
|
50
|
+
seen.push({ tool: 'context', input });
|
|
51
|
+
if (input.unity_resources === 'on') {
|
|
52
|
+
return {
|
|
53
|
+
status: 'found',
|
|
54
|
+
hydrationMeta: {
|
|
55
|
+
requestedMode: 'compact',
|
|
56
|
+
effectiveMode: 'compact',
|
|
57
|
+
isComplete: false,
|
|
58
|
+
needsParityRetry: true,
|
|
59
|
+
},
|
|
60
|
+
resourceBindings: [{ resourcePath: 'Assets/Prefabs/UI.prefab', resourceType: 'prefab' }],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { status: 'found' };
|
|
64
|
+
},
|
|
65
|
+
query: async (input) => {
|
|
66
|
+
seen.push({ tool: 'query', input });
|
|
67
|
+
return {
|
|
68
|
+
process_symbols: [{ id: 'Class:MainUIManager' }],
|
|
69
|
+
definitions: [{ name: 'DoorObj', resourceBindings: [{ resourcePath: 'Assets/A.prefab' }] }],
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
impact: async (input) => {
|
|
73
|
+
seen.push({ tool: 'impact', input });
|
|
74
|
+
return { impactedCount: 1 };
|
|
75
|
+
},
|
|
76
|
+
cypher: async (input) => {
|
|
77
|
+
seen.push({ tool: 'cypher', input });
|
|
78
|
+
return { rows: [] };
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
await runSymbolScenario(runner, {
|
|
82
|
+
symbol: 'MainUIManager',
|
|
83
|
+
kind: 'component',
|
|
84
|
+
objectives: ['verify context'],
|
|
85
|
+
deepDivePlan: [{ tool: 'query', input: { query: 'MainUIManager' } }],
|
|
86
|
+
});
|
|
87
|
+
const contextCalls = seen.filter((entry) => entry.tool === 'context');
|
|
88
|
+
const queryCalls = seen.filter((entry) => entry.tool === 'query');
|
|
89
|
+
assert.equal(contextCalls.every((entry) => entry.input.response_profile === 'full'), true);
|
|
90
|
+
assert.equal(queryCalls.every((entry) => entry.input.response_profile === 'full'), true);
|
|
91
|
+
});
|
|
46
92
|
test('AssetRef requires context(on) resourceBindings after serializable-class coverage', async () => {
|
|
47
93
|
const noEvidenceRunner = {
|
|
48
94
|
context: async () => ({
|
package/dist/cli/ai-context.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ interface RepoStats {
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function generateAIContextFiles(repoPath: string, _storagePath: string, projectName: string, stats: RepoStats, options?: {
|
|
22
22
|
skillScope?: SkillScope;
|
|
23
|
-
cliPackageSpec?: string;
|
|
24
23
|
}, generatedSkills?: GeneratedSkillInfo[]): Promise<{
|
|
25
24
|
files: string[];
|
|
26
25
|
}>;
|
package/dist/cli/ai-context.js
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import fs from 'fs/promises';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
|
-
import { buildNpxCommand, resolveCliSpec } from '../config/cli-spec.js';
|
|
12
11
|
// ESM equivalent of __dirname
|
|
13
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
13
|
const __dirname = path.dirname(__filename);
|
|
@@ -25,14 +24,13 @@ const GITNEXUS_END_MARKER = '<!-- gitnexus:end -->';
|
|
|
25
24
|
* - Exact tool commands with parameters — vague directives get ignored
|
|
26
25
|
* - Self-review checklist — forces model to verify its own work
|
|
27
26
|
*/
|
|
28
|
-
function generateGitNexusContent(projectName, stats, skillScope,
|
|
27
|
+
function generateGitNexusContent(projectName, stats, skillScope, generatedSkills) {
|
|
29
28
|
const skillRoot = skillScope === 'global'
|
|
30
29
|
? '~/.agents/skills/gitnexus'
|
|
31
30
|
: '.agents/skills/gitnexus';
|
|
32
31
|
const generatedRows = (generatedSkills && generatedSkills.length > 0)
|
|
33
32
|
? `\n${generatedSkills.map((s) => `| Work in the ${s.label} area (${s.symbolCount} symbols) | \`.claude/skills/generated/${s.name}/SKILL.md\` |`).join('\n')}`
|
|
34
33
|
: '';
|
|
35
|
-
const reindexCmd = buildNpxCommand(cliPackageSpec, 'analyze');
|
|
36
34
|
return `${GITNEXUS_START_MARKER}
|
|
37
35
|
# GitNexus MCP
|
|
38
36
|
|
|
@@ -44,7 +42,9 @@ function generateGitNexusContent(projectName, stats, skillScope, cliPackageSpec,
|
|
|
44
42
|
4. **Follow config/state file rules:** \`docs/gitnexus-config-files.md\`
|
|
45
43
|
5. **If user asks to release/publish a specific version and this repo has \`DISTRIBUTION.md\`, execute that workflow in full-release mode by default** (unless user explicitly asks \`prepare-only\` or \`publish-only\`).
|
|
46
44
|
|
|
47
|
-
> If step 1 warns the index is stale, ask user whether to rebuild index via \`gitnexus analyze\` when local CLI exists; otherwise resolve the pinned npx package spec from \`~/.gitnexus/config.json\` (\`cliPackageSpec\` first, then \`cliVersion\`) and run
|
|
45
|
+
> If step 1 warns the index is stale, ask user whether to rebuild index via \`gitnexus analyze\` when local CLI exists; otherwise resolve the pinned npx package spec from \`~/.gitnexus/config.json\` (\`cliPackageSpec\` first, then \`cliVersion\`) and run \`npx -y <resolved-spec> analyze\` (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.
|
|
46
|
+
> \`query/context\` slim guidance is narrowing-first: inspect \`decision.recommended_follow_up\`, \`missing_proof_targets\`, and \`suggested_context_targets\` before upgrading to \`response_profile=full\`.
|
|
47
|
+
> Query-time runtime closure is graph-only and does not require \`verification_rules\` / \`trigger_tokens\`; if you need hydration diagnostics such as \`needsParityRetry\` or strict fallback state, rerun with \`response_profile=full\` and then use parity before closure claims.
|
|
48
48
|
|
|
49
49
|
## Skills
|
|
50
50
|
|
|
@@ -204,8 +204,7 @@ async function copyDirRecursive(src, dest) {
|
|
|
204
204
|
*/
|
|
205
205
|
export async function generateAIContextFiles(repoPath, _storagePath, projectName, stats, options, generatedSkills) {
|
|
206
206
|
const skillScope = options?.skillScope === 'global' ? 'global' : 'project';
|
|
207
|
-
const
|
|
208
|
-
const content = generateGitNexusContent(projectName, stats, skillScope, cliPackageSpec, generatedSkills);
|
|
207
|
+
const content = generateGitNexusContent(projectName, stats, skillScope, generatedSkills);
|
|
209
208
|
const createdFiles = [];
|
|
210
209
|
// Create AGENTS.md (standard for Cursor, Windsurf, OpenCode, Codex, Cline, etc.)
|
|
211
210
|
const agentsPath = path.join(repoPath, 'AGENTS.md');
|
|
@@ -21,10 +21,14 @@ test('generateAIContextFiles installs repo skills under .agents/skills/gitnexus'
|
|
|
21
21
|
const claudeContent = await fs.readFile(claudePath, 'utf-8');
|
|
22
22
|
await fs.access(skillPath);
|
|
23
23
|
await fs.access(sharedRuntimeContractPath);
|
|
24
|
+
assert.match(agentsContent, /slim guidance is narrowing-first/);
|
|
25
|
+
assert.match(agentsContent, /Query-time runtime closure is graph-only/);
|
|
24
26
|
assert.match(agentsContent, /\.agents\/skills\/gitnexus\/gitnexus-exploring\/SKILL\.md/);
|
|
25
27
|
assert.match(claudeContent, /\.agents\/skills\/gitnexus\/gitnexus-exploring\/SKILL\.md/);
|
|
26
28
|
assert.doesNotMatch(agentsContent, /## Unity Runtime Process 真理源/);
|
|
27
29
|
assert.doesNotMatch(claudeContent, /## Unity Runtime Process 真理源/);
|
|
30
|
+
assert.doesNotMatch(agentsContent, /## Dev Workflow \(Source Build\)/);
|
|
31
|
+
assert.doesNotMatch(claudeContent, /## Dev Workflow \(Source Build\)/);
|
|
28
32
|
assert.equal(agentsContent, claudeContent, 'AGENTS.md and CLAUDE.md should stay content-identical');
|
|
29
33
|
assert.ok(result.files.some((entry) => entry.includes('.agents/skills/gitnexus/')), 'expected generated file summary to include .agents/skills/gitnexus/');
|
|
30
34
|
await assert.rejects(fs.access(legacyClaudeSkillsDir));
|
|
@@ -46,10 +50,14 @@ test('generateAIContextFiles with global scope skips repo skill install', async
|
|
|
46
50
|
const localSkillsDir = path.join(repoPath, '.agents', 'skills', 'gitnexus');
|
|
47
51
|
const agentsContent = await fs.readFile(agentsPath, 'utf-8');
|
|
48
52
|
const claudeContent = await fs.readFile(claudePath, 'utf-8');
|
|
53
|
+
assert.match(agentsContent, /slim guidance is narrowing-first/);
|
|
54
|
+
assert.match(agentsContent, /Query-time runtime closure is graph-only/);
|
|
49
55
|
assert.match(agentsContent, /~\/\.agents\/skills\/gitnexus\/gitnexus-exploring\/SKILL\.md/);
|
|
50
56
|
assert.match(claudeContent, /~\/\.agents\/skills\/gitnexus\/gitnexus-exploring\/SKILL\.md/);
|
|
51
57
|
assert.doesNotMatch(agentsContent, /## Unity Runtime Process 真理源/);
|
|
52
58
|
assert.doesNotMatch(claudeContent, /## Unity Runtime Process 真理源/);
|
|
59
|
+
assert.doesNotMatch(agentsContent, /## Dev Workflow \(Source Build\)/);
|
|
60
|
+
assert.doesNotMatch(claudeContent, /## Dev Workflow \(Source Build\)/);
|
|
53
61
|
assert.equal(agentsContent, claudeContent, 'AGENTS.md and CLAUDE.md should stay content-identical');
|
|
54
62
|
assert.ok(!result.files.some((entry) => entry.includes('.agents/skills/gitnexus/')), 'did not expect repo-local skills in generated file summary');
|
|
55
63
|
await assert.rejects(fs.access(localSkillsDir));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { normalizeScopeRules
|
|
3
|
+
import { normalizeScopeRules } from '../core/ingestion/scope-filter.js';
|
|
4
|
+
import { parseScopeManifestConfig } from './scope-manifest-config.js';
|
|
4
5
|
const REPO_ALIAS_REGEX = /^[a-zA-Z0-9._-]{3,64}$/;
|
|
5
6
|
export function parseExtensionList(rawExtensions) {
|
|
6
7
|
return (rawExtensions || '')
|
|
@@ -21,38 +22,16 @@ export function normalizeRepoAlias(repoAlias) {
|
|
|
21
22
|
return normalized;
|
|
22
23
|
}
|
|
23
24
|
export async function resolveAnalyzeScopeRules(options) {
|
|
24
|
-
|
|
25
|
+
let manifestRules = [];
|
|
25
26
|
if (options?.scopeManifest) {
|
|
26
27
|
const manifestPath = path.resolve(options.scopeManifest);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
content = await fs.readFile(manifestPath, 'utf-8');
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
throw new Error(`Scope manifest not found: ${manifestPath}`);
|
|
33
|
-
}
|
|
34
|
-
const manifestRules = parseScopeRules(content);
|
|
28
|
+
const manifest = await readScopeManifestConfig(manifestPath);
|
|
29
|
+
manifestRules = manifest.scopeRules;
|
|
35
30
|
if (manifestRules.length === 0) {
|
|
36
31
|
throw new Error(`Scope manifest has no valid scope rules: ${manifestPath}`);
|
|
37
32
|
}
|
|
38
|
-
rules.push(...manifestRules);
|
|
39
|
-
}
|
|
40
|
-
const prefixesRaw = Array.isArray(options?.scopePrefix)
|
|
41
|
-
? options?.scopePrefix || []
|
|
42
|
-
: options?.scopePrefix
|
|
43
|
-
? [options.scopePrefix]
|
|
44
|
-
: [];
|
|
45
|
-
for (const prefix of prefixesRaw) {
|
|
46
|
-
const trimmed = prefix.trim();
|
|
47
|
-
if (trimmed) {
|
|
48
|
-
rules.push(trimmed);
|
|
49
|
-
}
|
|
50
33
|
}
|
|
51
|
-
|
|
52
|
-
if ((options?.scopeManifest || prefixesRaw.length > 0) && normalizedRules.length === 0) {
|
|
53
|
-
throw new Error('No valid scope rules provided.');
|
|
54
|
-
}
|
|
55
|
-
return normalizedRules;
|
|
34
|
+
return resolveScopeRulesFromInput(manifestRules, normalizeScopePrefixes(options?.scopePrefix), Boolean(options?.scopeManifest));
|
|
56
35
|
}
|
|
57
36
|
function parseScopePrefixCount(scopePrefix) {
|
|
58
37
|
if (Array.isArray(scopePrefix))
|
|
@@ -62,26 +41,36 @@ function parseScopePrefixCount(scopePrefix) {
|
|
|
62
41
|
return 0;
|
|
63
42
|
}
|
|
64
43
|
export async function resolveEffectiveAnalyzeOptions(options, stored) {
|
|
44
|
+
const manifestConfig = options?.scopeManifest
|
|
45
|
+
? await readScopeManifestConfig(path.resolve(options.scopeManifest))
|
|
46
|
+
: undefined;
|
|
65
47
|
const includeExtensionsFromCli = parseExtensionList(options?.extensions);
|
|
66
|
-
const scopeRulesFromCli =
|
|
67
|
-
scopeManifest: options?.scopeManifest,
|
|
68
|
-
scopePrefix: options?.scopePrefix,
|
|
69
|
-
});
|
|
48
|
+
const scopeRulesFromCli = resolveScopeRulesFromInput(manifestConfig?.scopeRules || [], normalizeScopePrefixes(options?.scopePrefix), Boolean(options?.scopeManifest));
|
|
70
49
|
const repoAliasFromCli = normalizeRepoAlias(options?.repoAlias);
|
|
50
|
+
const manifestExtensions = manifestConfig?.directives.extensions;
|
|
51
|
+
const manifestRepoAlias = manifestConfig?.directives.repoAlias;
|
|
52
|
+
const manifestEmbeddings = manifestConfig?.directives.embeddings;
|
|
71
53
|
const hasCliExtensions = options?.extensions !== undefined;
|
|
72
54
|
const hasCliScope = Boolean(options?.scopeManifest) || parseScopePrefixCount(options?.scopePrefix) > 0;
|
|
73
55
|
const hasCliRepoAlias = options?.repoAlias !== undefined;
|
|
74
56
|
const canReuse = options?.reuseOptions !== false;
|
|
75
57
|
const includeExtensions = hasCliExtensions
|
|
76
58
|
? includeExtensionsFromCli
|
|
77
|
-
: (
|
|
59
|
+
: (manifestExtensions !== undefined
|
|
60
|
+
? parseExtensionList(manifestExtensions)
|
|
61
|
+
: (canReuse ? (stored?.includeExtensions || []) : []));
|
|
78
62
|
const scopeRules = hasCliScope
|
|
79
63
|
? scopeRulesFromCli
|
|
80
64
|
: (canReuse ? (stored?.scopeRules || []) : []);
|
|
81
65
|
const repoAlias = hasCliRepoAlias
|
|
82
66
|
? repoAliasFromCli
|
|
83
|
-
: (
|
|
84
|
-
|
|
67
|
+
: (manifestRepoAlias !== undefined
|
|
68
|
+
? normalizeRepoAlias(manifestRepoAlias)
|
|
69
|
+
: (canReuse ? normalizeRepoAlias(stored?.repoAlias) : undefined));
|
|
70
|
+
const embeddings = options?.embeddings
|
|
71
|
+
?? (manifestEmbeddings !== undefined
|
|
72
|
+
? parseManifestEmbeddings(manifestEmbeddings)
|
|
73
|
+
: (canReuse ? Boolean(stored?.embeddings) : false));
|
|
85
74
|
return {
|
|
86
75
|
includeExtensions: [...includeExtensions],
|
|
87
76
|
scopeRules: [...scopeRules],
|
|
@@ -89,3 +78,38 @@ export async function resolveEffectiveAnalyzeOptions(options, stored) {
|
|
|
89
78
|
embeddings,
|
|
90
79
|
};
|
|
91
80
|
}
|
|
81
|
+
function normalizeScopePrefixes(scopePrefix) {
|
|
82
|
+
const prefixesRaw = Array.isArray(scopePrefix)
|
|
83
|
+
? scopePrefix || []
|
|
84
|
+
: scopePrefix
|
|
85
|
+
? [scopePrefix]
|
|
86
|
+
: [];
|
|
87
|
+
return prefixesRaw
|
|
88
|
+
.map((prefix) => prefix.trim())
|
|
89
|
+
.filter(Boolean);
|
|
90
|
+
}
|
|
91
|
+
function resolveScopeRulesFromInput(manifestRules, prefixes, hasScopeManifest) {
|
|
92
|
+
const normalizedRules = normalizeScopeRules([...manifestRules, ...prefixes]);
|
|
93
|
+
if ((hasScopeManifest || prefixes.length > 0) && normalizedRules.length === 0) {
|
|
94
|
+
throw new Error('No valid scope rules provided.');
|
|
95
|
+
}
|
|
96
|
+
return normalizedRules;
|
|
97
|
+
}
|
|
98
|
+
async function readScopeManifestConfig(manifestPath) {
|
|
99
|
+
let content;
|
|
100
|
+
try {
|
|
101
|
+
content = await fs.readFile(manifestPath, 'utf-8');
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
throw new Error(`Scope manifest not found: ${manifestPath}`);
|
|
105
|
+
}
|
|
106
|
+
return parseScopeManifestConfig(content);
|
|
107
|
+
}
|
|
108
|
+
function parseManifestEmbeddings(raw) {
|
|
109
|
+
const normalized = raw.trim().toLowerCase();
|
|
110
|
+
if (normalized === 'true')
|
|
111
|
+
return true;
|
|
112
|
+
if (normalized === 'false')
|
|
113
|
+
return false;
|
|
114
|
+
throw new Error(`Invalid @embeddings directive value: ${raw}. Expected true or false.`);
|
|
115
|
+
}
|
|
@@ -4,6 +4,7 @@ import fs from 'node:fs/promises';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { normalizeRepoAlias, parseExtensionList, resolveAnalyzeScopeRules, resolveEffectiveAnalyzeOptions, } from './analyze-options.js';
|
|
7
|
+
import { parseScopeManifestConfig } from './scope-manifest-config.js';
|
|
7
8
|
test('parseExtensionList normalizes dot prefixes', () => {
|
|
8
9
|
const exts = parseExtensionList('cs,.ts, go ');
|
|
9
10
|
assert.deepEqual(exts, ['.cs', '.ts', '.go']);
|
|
@@ -75,3 +76,59 @@ test('resolveEffectiveAnalyzeOptions prefers explicit CLI values over stored set
|
|
|
75
76
|
assert.equal(resolved.repoAlias, 'new-alias');
|
|
76
77
|
assert.equal(resolved.embeddings, false);
|
|
77
78
|
});
|
|
79
|
+
test('resolveEffectiveAnalyzeOptions reads @extensions/@repoAlias/@embeddings from manifest', async () => {
|
|
80
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-manifest-directives-'));
|
|
81
|
+
const manifestPath = path.join(tmpDir, 'sync-manifest.txt');
|
|
82
|
+
await fs.writeFile(manifestPath, ['Assets/', '@extensions=.cs,.meta', '@repoAlias=neonspark-core', '@embeddings=false'].join('\n'), 'utf-8');
|
|
83
|
+
const resolved = await resolveEffectiveAnalyzeOptions({ scopeManifest: manifestPath }, {
|
|
84
|
+
includeExtensions: ['.ts'],
|
|
85
|
+
scopeRules: ['src'],
|
|
86
|
+
repoAlias: 'stored-alias',
|
|
87
|
+
embeddings: true,
|
|
88
|
+
});
|
|
89
|
+
assert.deepEqual(resolved.scopeRules, ['Assets']);
|
|
90
|
+
assert.deepEqual(resolved.includeExtensions, ['.cs', '.meta']);
|
|
91
|
+
assert.equal(resolved.repoAlias, 'neonspark-core');
|
|
92
|
+
assert.equal(resolved.embeddings, false);
|
|
93
|
+
});
|
|
94
|
+
test('resolveEffectiveAnalyzeOptions enforces precedence CLI > manifest > meta', async () => {
|
|
95
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-manifest-precedence-'));
|
|
96
|
+
const manifestPath = path.join(tmpDir, 'sync-manifest.txt');
|
|
97
|
+
await fs.writeFile(manifestPath, ['Assets/', '@extensions=.cs,.meta', '@repoAlias=manifest-alias', '@embeddings=false'].join('\n'), 'utf-8');
|
|
98
|
+
const resolved = await resolveEffectiveAnalyzeOptions({
|
|
99
|
+
scopeManifest: manifestPath,
|
|
100
|
+
extensions: '.ts',
|
|
101
|
+
}, {
|
|
102
|
+
includeExtensions: ['.js'],
|
|
103
|
+
scopeRules: ['tools'],
|
|
104
|
+
repoAlias: 'meta-alias',
|
|
105
|
+
embeddings: true,
|
|
106
|
+
});
|
|
107
|
+
assert.deepEqual(resolved.scopeRules, ['Assets']);
|
|
108
|
+
assert.deepEqual(resolved.includeExtensions, ['.ts']);
|
|
109
|
+
assert.equal(resolved.repoAlias, 'manifest-alias');
|
|
110
|
+
assert.equal(resolved.embeddings, false);
|
|
111
|
+
});
|
|
112
|
+
test('resolveEffectiveAnalyzeOptions rejects unknown manifest directives', async () => {
|
|
113
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-manifest-unknown-'));
|
|
114
|
+
const manifestPath = path.join(tmpDir, 'sync-manifest.txt');
|
|
115
|
+
await fs.writeFile(manifestPath, ['Assets/', '@foo=bar'].join('\n'), 'utf-8');
|
|
116
|
+
await assert.rejects(resolveEffectiveAnalyzeOptions({ scopeManifest: manifestPath }), /unknown manifest directive/i);
|
|
117
|
+
});
|
|
118
|
+
test('parseScopeManifestConfig splits scope rules and directives', () => {
|
|
119
|
+
const parsed = parseScopeManifestConfig([
|
|
120
|
+
'# comment',
|
|
121
|
+
'Assets/',
|
|
122
|
+
'Packages/com.veewo.*',
|
|
123
|
+
'',
|
|
124
|
+
'@extensions=.cs,.meta',
|
|
125
|
+
'@repoAlias=demo-repo',
|
|
126
|
+
'@embeddings=false',
|
|
127
|
+
].join('\n'));
|
|
128
|
+
assert.deepEqual(parsed.scopeRules, ['Assets', 'Packages/com.veewo.*']);
|
|
129
|
+
assert.deepEqual(parsed.directives, {
|
|
130
|
+
extensions: '.cs,.meta',
|
|
131
|
+
repoAlias: 'demo-repo',
|
|
132
|
+
embeddings: 'false',
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -4,6 +4,8 @@ export function toPipelineRuntimeSummary(input) {
|
|
|
4
4
|
communityResult: input.communityResult,
|
|
5
5
|
processResult: input.processResult,
|
|
6
6
|
unityResult: input.unityResult,
|
|
7
|
+
unityRuleBindingResult: input.unityRuleBindingResult,
|
|
7
8
|
scopeDiagnostics: input.scopeDiagnostics,
|
|
9
|
+
csharpPreprocDiagnostics: input.csharpPreprocDiagnostics,
|
|
8
10
|
};
|
|
9
11
|
}
|
|
@@ -7,8 +7,20 @@ test('toPipelineRuntimeSummary drops graph reference and preserves reporting fie
|
|
|
7
7
|
communityResult: { stats: { totalCommunities: 3 } },
|
|
8
8
|
processResult: { stats: { totalProcesses: 2 } },
|
|
9
9
|
unityResult: { diagnostics: ['scanContext: scripts=1'] },
|
|
10
|
+
unityRuleBindingResult: { edgesInjected: 1, ruleResults: [], diagnostics: { summary: [] } },
|
|
11
|
+
csharpPreprocDiagnostics: {
|
|
12
|
+
enabled: true,
|
|
13
|
+
defineSymbolCount: 2,
|
|
14
|
+
normalizedFiles: 1,
|
|
15
|
+
fallbackFiles: 0,
|
|
16
|
+
skippedFiles: 3,
|
|
17
|
+
expressionErrors: 0,
|
|
18
|
+
undefinedSymbols: [],
|
|
19
|
+
},
|
|
10
20
|
});
|
|
11
21
|
assert.equal('graph' in out, false);
|
|
12
22
|
assert.equal(out.totalFileCount, 12);
|
|
13
23
|
assert.equal(out.communityResult?.stats.totalCommunities, 3);
|
|
24
|
+
assert.equal(out.unityRuleBindingResult?.edgesInjected, 1);
|
|
25
|
+
assert.equal(out.csharpPreprocDiagnostics?.normalizedFiles, 1);
|
|
14
26
|
});
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import type { CSharpPreprocDiagnostics } from '../types/pipeline.js';
|
|
2
|
+
import type { UnityRuntimeBindingResult } from '../core/ingestion/unity-runtime-binding-rules.js';
|
|
1
3
|
export interface FallbackInsertStats {
|
|
2
4
|
attempted: number;
|
|
3
5
|
succeeded: number;
|
|
4
6
|
failed: number;
|
|
5
7
|
}
|
|
8
|
+
export declare function formatCSharpPreprocDiagnosticsSummary(diagnostics: CSharpPreprocDiagnostics | undefined, previewLimit?: number): string[];
|
|
6
9
|
export declare function formatUnityDiagnosticsSummary(diagnostics: string[] | undefined, previewLimit?: number): string[];
|
|
10
|
+
export declare function formatUnityRuleBindingSummary(result: UnityRuntimeBindingResult | undefined, previewLimit?: number): string[];
|
|
7
11
|
export declare function formatFallbackSummary(warnings: string[] | undefined, stats: FallbackInsertStats | undefined, previewLimit?: number): string[];
|
|
8
12
|
export declare function resolveFallbackStats(warnings: string[] | undefined, stats: FallbackInsertStats | undefined): FallbackInsertStats;
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
export function formatCSharpPreprocDiagnosticsSummary(diagnostics, previewLimit = 5) {
|
|
2
|
+
if (!diagnostics?.enabled)
|
|
3
|
+
return [];
|
|
4
|
+
const lines = [
|
|
5
|
+
`CSharp Preproc: defines=${diagnostics.defineSymbolCount}, normalized=${diagnostics.normalizedFiles}, fallback=${diagnostics.fallbackFiles}, skipped=${diagnostics.skippedFiles}, exprErrors=${diagnostics.expressionErrors}`,
|
|
6
|
+
];
|
|
7
|
+
if (diagnostics.sourcePath) {
|
|
8
|
+
lines.push(`- source: ${diagnostics.sourcePath}`);
|
|
9
|
+
}
|
|
10
|
+
if (diagnostics.undefinedSymbols.length > 0) {
|
|
11
|
+
const limit = previewLimit > 0 ? previewLimit : diagnostics.undefinedSymbols.length;
|
|
12
|
+
const preview = diagnostics.undefinedSymbols.slice(0, limit);
|
|
13
|
+
lines.push(`- undefined symbols: ${preview.join(', ')}`);
|
|
14
|
+
if (diagnostics.undefinedSymbols.length > preview.length) {
|
|
15
|
+
lines.push(`... ${diagnostics.undefinedSymbols.length - preview.length} more`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return lines;
|
|
19
|
+
}
|
|
1
20
|
export function formatUnityDiagnosticsSummary(diagnostics, previewLimit = 3) {
|
|
2
21
|
if (!diagnostics || diagnostics.length === 0) {
|
|
3
22
|
return [];
|
|
@@ -13,6 +32,30 @@ export function formatUnityDiagnosticsSummary(diagnostics, previewLimit = 3) {
|
|
|
13
32
|
}
|
|
14
33
|
return lines;
|
|
15
34
|
}
|
|
35
|
+
export function formatUnityRuleBindingSummary(result, previewLimit = 3) {
|
|
36
|
+
if (!result)
|
|
37
|
+
return [];
|
|
38
|
+
const diagnostics = result.diagnostics;
|
|
39
|
+
const lines = ['Unity Rule Binding Diagnostics:'];
|
|
40
|
+
for (const message of diagnostics.summary) {
|
|
41
|
+
if (!message.startsWith('rule_binding.anomaly:')) {
|
|
42
|
+
lines.push(`- ${message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const anomalies = diagnostics.anomalies;
|
|
46
|
+
if (anomalies.length === 0) {
|
|
47
|
+
return lines;
|
|
48
|
+
}
|
|
49
|
+
lines.push(`- rule_binding.anomalies: count=${anomalies.length}`);
|
|
50
|
+
const limit = previewLimit > 0 ? previewLimit : anomalies.length;
|
|
51
|
+
for (const message of anomalies.slice(0, limit)) {
|
|
52
|
+
lines.push(`- rule_binding.anomaly: ${message}`);
|
|
53
|
+
}
|
|
54
|
+
if (anomalies.length > limit) {
|
|
55
|
+
lines.push(`- rule_binding.anomaly: ... ${anomalies.length - limit} more`);
|
|
56
|
+
}
|
|
57
|
+
return lines;
|
|
58
|
+
}
|
|
16
59
|
export function formatFallbackSummary(warnings, stats, previewLimit = 5) {
|
|
17
60
|
if (!warnings || warnings.length === 0) {
|
|
18
61
|
return [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { formatFallbackSummary, formatUnityDiagnosticsSummary, resolveFallbackStats } from './analyze-summary.js';
|
|
3
|
+
import { formatFallbackSummary, formatUnityDiagnosticsSummary, formatUnityRuleBindingSummary, resolveFallbackStats, } from './analyze-summary.js';
|
|
4
4
|
test('formatUnityDiagnosticsSummary returns empty when diagnostics are missing', () => {
|
|
5
5
|
const lines = formatUnityDiagnosticsSummary([]);
|
|
6
6
|
assert.deepEqual(lines, []);
|
|
@@ -29,6 +29,70 @@ test('formatUnityDiagnosticsSummary truncates output after max preview items', (
|
|
|
29
29
|
'... 1 more',
|
|
30
30
|
]);
|
|
31
31
|
});
|
|
32
|
+
test('formatUnityRuleBindingSummary renders diagnostics and agent report status', () => {
|
|
33
|
+
const lines = formatUnityRuleBindingSummary({
|
|
34
|
+
edgesInjected: 3,
|
|
35
|
+
ruleResults: [{ ruleId: 'unity.global-init', edgesInjected: 3 }],
|
|
36
|
+
diagnostics: {
|
|
37
|
+
rulesEvaluated: 1,
|
|
38
|
+
bindingsEvaluated: 1,
|
|
39
|
+
bindingsByKind: { method_triggers_scene_load: 1 },
|
|
40
|
+
methodLookupCalls: 5,
|
|
41
|
+
methodLookupCacheHits: 4,
|
|
42
|
+
sceneRuntimeTraversalCalls: 3,
|
|
43
|
+
sceneRuntimeTraversalCacheHits: 2,
|
|
44
|
+
sceneRuntimeResourcesVisited: 6,
|
|
45
|
+
anomalies: [],
|
|
46
|
+
shouldAgentReport: false,
|
|
47
|
+
agentReportReason: 'no anomalies detected',
|
|
48
|
+
summary: [
|
|
49
|
+
'rule_binding.summary: rules=1, bindings=1, edges=3',
|
|
50
|
+
'rule_binding.lookup: method_calls=5, cache_hits=4',
|
|
51
|
+
'rule_binding.agent_report: should_report=false reason="no anomalies detected"',
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
assert.deepEqual(lines, [
|
|
56
|
+
'Unity Rule Binding Diagnostics:',
|
|
57
|
+
'- rule_binding.summary: rules=1, bindings=1, edges=3',
|
|
58
|
+
'- rule_binding.lookup: method_calls=5, cache_hits=4',
|
|
59
|
+
'- rule_binding.agent_report: should_report=false reason="no anomalies detected"',
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
test('formatUnityRuleBindingSummary renders anomaly preview', () => {
|
|
63
|
+
const lines = formatUnityRuleBindingSummary({
|
|
64
|
+
edgesInjected: 0,
|
|
65
|
+
ruleResults: [],
|
|
66
|
+
diagnostics: {
|
|
67
|
+
rulesEvaluated: 1,
|
|
68
|
+
bindingsEvaluated: 1,
|
|
69
|
+
bindingsByKind: { method_triggers_scene_load: 1 },
|
|
70
|
+
methodLookupCalls: 0,
|
|
71
|
+
methodLookupCacheHits: 0,
|
|
72
|
+
sceneRuntimeTraversalCalls: 0,
|
|
73
|
+
sceneRuntimeTraversalCacheHits: 0,
|
|
74
|
+
sceneRuntimeResourcesVisited: 0,
|
|
75
|
+
anomalies: [
|
|
76
|
+
'rule=unity.global-init: scene "Global" not found in File(.unity) index',
|
|
77
|
+
'rule=unity.global-init: method_triggers_scene_load missing host_class_pattern, loader_methods, or scene_name',
|
|
78
|
+
],
|
|
79
|
+
shouldAgentReport: true,
|
|
80
|
+
agentReportReason: 'rule-binding anomalies detected',
|
|
81
|
+
summary: [
|
|
82
|
+
'rule_binding.summary: rules=1, bindings=1, edges=0',
|
|
83
|
+
'rule_binding.agent_report: should_report=true reason="rule-binding anomalies detected"',
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
}, 1);
|
|
87
|
+
assert.deepEqual(lines, [
|
|
88
|
+
'Unity Rule Binding Diagnostics:',
|
|
89
|
+
'- rule_binding.summary: rules=1, bindings=1, edges=0',
|
|
90
|
+
'- rule_binding.agent_report: should_report=true reason="rule-binding anomalies detected"',
|
|
91
|
+
'- rule_binding.anomalies: count=2',
|
|
92
|
+
'- rule_binding.anomaly: rule=unity.global-init: scene "Global" not found in File(.unity) index',
|
|
93
|
+
'- rule_binding.anomaly: ... 1 more',
|
|
94
|
+
]);
|
|
95
|
+
});
|
|
32
96
|
test('formatFallbackSummary returns empty when no warnings exist', () => {
|
|
33
97
|
const lines = formatFallbackSummary([], {
|
|
34
98
|
attempted: 0,
|
package/dist/cli/analyze.d.ts
CHANGED
|
@@ -3,15 +3,26 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Indexes a repository and stores the knowledge graph in .gitnexus/
|
|
5
5
|
*/
|
|
6
|
+
import { type SyncManifestPolicy } from './sync-manifest.js';
|
|
6
7
|
export interface AnalyzeOptions {
|
|
7
8
|
force?: boolean;
|
|
8
9
|
embeddings?: boolean;
|
|
9
10
|
extensions?: string;
|
|
10
11
|
repoAlias?: string;
|
|
12
|
+
csharpDefineCsproj?: string;
|
|
11
13
|
scopeManifest?: string;
|
|
12
14
|
scopePrefix?: string[];
|
|
15
|
+
syncManifestPolicy?: SyncManifestPolicy;
|
|
13
16
|
reuseOptions?: boolean;
|
|
14
17
|
skills?: boolean;
|
|
15
18
|
verbose?: boolean;
|
|
16
19
|
}
|
|
17
20
|
export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptions) => Promise<void>;
|
|
21
|
+
export declare function buildPipelineRunOptionsForAnalyze(resolvedOptions: {
|
|
22
|
+
includeExtensions: string[];
|
|
23
|
+
scopeRules: string[];
|
|
24
|
+
}, options?: AnalyzeOptions): {
|
|
25
|
+
includeExtensions: string[];
|
|
26
|
+
scopeRules: string[];
|
|
27
|
+
csharpDefineCsproj?: string;
|
|
28
|
+
};
|
package/dist/cli/analyze.js
CHANGED
|
@@ -19,10 +19,10 @@ import { generateAIContextFiles } from './ai-context.js';
|
|
|
19
19
|
import { generateSkillFiles } from './skill-gen.js';
|
|
20
20
|
import fs from 'fs/promises';
|
|
21
21
|
import { resolveEffectiveAnalyzeOptions } from './analyze-options.js';
|
|
22
|
-
import { formatFallbackSummary, formatUnityDiagnosticsSummary, resolveFallbackStats } from './analyze-summary.js';
|
|
22
|
+
import { formatCSharpPreprocDiagnosticsSummary, formatFallbackSummary, formatUnityDiagnosticsSummary, formatUnityRuleBindingSummary, resolveFallbackStats, } from './analyze-summary.js';
|
|
23
23
|
import { resolveChildProcessExit } from './exit-code.js';
|
|
24
24
|
import { toPipelineRuntimeSummary } from './analyze-runtime-summary.js';
|
|
25
|
-
import {
|
|
25
|
+
import { enforceSyncManifestConsistency, resolveScopeManifestForAnalyze } from './sync-manifest.js';
|
|
26
26
|
const HEAP_MB = 8192;
|
|
27
27
|
const HEAP_FLAG = `--max-old-space-size=${HEAP_MB}`;
|
|
28
28
|
/** Re-exec the process with an 8GB heap if we're currently below that. */
|
|
@@ -112,9 +112,21 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
112
112
|
let repoAlias;
|
|
113
113
|
let embeddingsEnabled = false;
|
|
114
114
|
try {
|
|
115
|
+
const scopeManifest = await resolveScopeManifestForAnalyze(repoPath, {
|
|
116
|
+
scopeManifest: options?.scopeManifest,
|
|
117
|
+
scopePrefix: options?.scopePrefix,
|
|
118
|
+
});
|
|
119
|
+
await enforceSyncManifestConsistency({
|
|
120
|
+
manifestPath: scopeManifest,
|
|
121
|
+
extensions: options?.extensions,
|
|
122
|
+
repoAlias: options?.repoAlias,
|
|
123
|
+
embeddings: options?.embeddings,
|
|
124
|
+
policy: options?.syncManifestPolicy,
|
|
125
|
+
stdinIsTTY: Boolean(process.stdin.isTTY),
|
|
126
|
+
});
|
|
115
127
|
const effectiveOptions = await resolveEffectiveAnalyzeOptions({
|
|
116
128
|
extensions: options?.extensions,
|
|
117
|
-
scopeManifest
|
|
129
|
+
scopeManifest,
|
|
118
130
|
scopePrefix: options?.scopePrefix,
|
|
119
131
|
repoAlias: options?.repoAlias,
|
|
120
132
|
embeddings: options?.embeddings,
|
|
@@ -241,11 +253,12 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
241
253
|
// ── Phase 1: Full Pipeline (0–60%) ─────────────────────────────────
|
|
242
254
|
let pipelineResult;
|
|
243
255
|
try {
|
|
256
|
+
const pipelineRunOptions = buildPipelineRunOptionsForAnalyze({ includeExtensions, scopeRules }, options);
|
|
244
257
|
pipelineResult = await runPipelineFromRepo(repoPath, (progress) => {
|
|
245
258
|
const phaseLabel = PHASE_LABELS[progress.phase] || progress.phase;
|
|
246
259
|
const scaled = Math.round(progress.percent * 0.6);
|
|
247
260
|
updateBar(scaled, phaseLabel);
|
|
248
|
-
},
|
|
261
|
+
}, pipelineRunOptions);
|
|
249
262
|
}
|
|
250
263
|
catch (error) {
|
|
251
264
|
clearInterval(elapsedTimer);
|
|
@@ -391,7 +404,6 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
391
404
|
processes: pipelineRuntime.processResult?.stats.totalProcesses,
|
|
392
405
|
}, {
|
|
393
406
|
skillScope: (cliConfig.setupScope === 'global') ? 'global' : 'project',
|
|
394
|
-
cliPackageSpec: resolveCliSpec({ config: cliConfig }).packageSpec,
|
|
395
407
|
}, generatedSkills);
|
|
396
408
|
await closeLbug();
|
|
397
409
|
// Note: we intentionally do NOT call disposeEmbedder() here.
|
|
@@ -432,6 +444,14 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
432
444
|
for (const line of unitySummaryLines) {
|
|
433
445
|
console.log(` ${line}`);
|
|
434
446
|
}
|
|
447
|
+
const unityRuleBindingSummaryLines = formatUnityRuleBindingSummary(pipelineRuntime.unityRuleBindingResult);
|
|
448
|
+
for (const line of unityRuleBindingSummaryLines) {
|
|
449
|
+
console.log(` ${line}`);
|
|
450
|
+
}
|
|
451
|
+
const csharpPreprocSummaryLines = formatCSharpPreprocDiagnosticsSummary(pipelineRuntime.csharpPreprocDiagnostics);
|
|
452
|
+
for (const line of csharpPreprocSummaryLines) {
|
|
453
|
+
console.log(` ${line}`);
|
|
454
|
+
}
|
|
435
455
|
console.log(` ${stats.nodes.toLocaleString()} nodes | ${stats.edges.toLocaleString()} edges | ${pipelineRuntime.communityResult?.stats.totalCommunities || 0} clusters | ${pipelineRuntime.processResult?.stats.totalProcesses || 0} flows`);
|
|
436
456
|
console.log(` LadybugDB ${lbugTime}s | FTS ${ftsTime}s | Embeddings ${embeddingSkipped ? embeddingSkipReason : embeddingTime + 's'}`);
|
|
437
457
|
if (includeExtensions.length > 0) {
|
|
@@ -460,6 +480,15 @@ export const analyzeCommand = async (inputPath, options) => {
|
|
|
460
480
|
// platforms (#38, #40). Force-exit to ensure clean termination.
|
|
461
481
|
process.exit(0);
|
|
462
482
|
};
|
|
483
|
+
export function buildPipelineRunOptionsForAnalyze(resolvedOptions, options) {
|
|
484
|
+
return {
|
|
485
|
+
includeExtensions: resolvedOptions.includeExtensions,
|
|
486
|
+
scopeRules: resolvedOptions.scopeRules,
|
|
487
|
+
...(options?.csharpDefineCsproj
|
|
488
|
+
? { csharpDefineCsproj: options.csharpDefineCsproj }
|
|
489
|
+
: {}),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
463
492
|
async function persistUnityParitySeed(storagePath, seed) {
|
|
464
493
|
const seedPath = path.join(storagePath, 'unity-parity-seed.json');
|
|
465
494
|
if (!seed) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|