@veewo/gitnexus 1.5.7 → 1.5.9

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.
Files changed (77) hide show
  1. package/dist/cli/ai-context.js +1 -7
  2. package/dist/cli/analyze-options.d.ts +4 -0
  3. package/dist/cli/analyze-options.js +14 -1
  4. package/dist/cli/analyze-options.test.js +23 -0
  5. package/dist/cli/analyze-runtime-summary.js +0 -1
  6. package/dist/cli/analyze-runtime-summary.test.js +0 -2
  7. package/dist/cli/analyze-summary.d.ts +0 -2
  8. package/dist/cli/analyze-summary.js +0 -24
  9. package/dist/cli/analyze-summary.test.js +1 -65
  10. package/dist/cli/analyze.d.ts +1 -0
  11. package/dist/cli/analyze.js +26 -18
  12. package/dist/cli/clean.js +23 -2
  13. package/dist/cli/index.js +3 -3
  14. package/dist/cli/repo-manager-alias.test.js +2 -0
  15. package/dist/core/ingestion/pipeline.js +0 -43
  16. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
  17. package/dist/core/ingestion/tree-sitter-queries.js +3 -3
  18. package/dist/mcp/local/agent-safe-response.js +1 -1
  19. package/dist/mcp/local/local-backend.d.ts +0 -23
  20. package/dist/mcp/local/local-backend.js +69 -248
  21. package/dist/mcp/local/runtime-chain-verify.test.js +0 -49
  22. package/dist/mcp/local/runtime-claim-rule-registry.d.ts +0 -11
  23. package/dist/mcp/local/runtime-claim-rule-registry.js +0 -159
  24. package/dist/mcp/local/runtime-claim-rule-registry.test.js +67 -214
  25. package/dist/mcp/tools.js +0 -70
  26. package/dist/storage/repo-manager.d.ts +1 -0
  27. package/dist/types/pipeline.d.ts +0 -3
  28. package/package.json +4 -4
  29. package/skills/gitnexus-cli.md +5 -2
  30. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +0 -60
  31. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +0 -395
  32. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.d.ts +0 -1
  33. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +0 -41
  34. package/dist/cli/rule-lab.d.ts +0 -38
  35. package/dist/cli/rule-lab.js +0 -148
  36. package/dist/cli/rule-lab.test.d.ts +0 -1
  37. package/dist/cli/rule-lab.test.js +0 -31
  38. package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +0 -26
  39. package/dist/core/ingestion/unity-runtime-binding-rules.js +0 -408
  40. package/dist/rule-lab/analyze.d.ts +0 -13
  41. package/dist/rule-lab/analyze.js +0 -125
  42. package/dist/rule-lab/analyze.test.d.ts +0 -1
  43. package/dist/rule-lab/analyze.test.js +0 -246
  44. package/dist/rule-lab/compile.d.ts +0 -5
  45. package/dist/rule-lab/compile.js +0 -51
  46. package/dist/rule-lab/compiled-bundles.d.ts +0 -30
  47. package/dist/rule-lab/compiled-bundles.js +0 -36
  48. package/dist/rule-lab/curate.d.ts +0 -33
  49. package/dist/rule-lab/curate.js +0 -155
  50. package/dist/rule-lab/curate.test.d.ts +0 -1
  51. package/dist/rule-lab/curate.test.js +0 -137
  52. package/dist/rule-lab/curation-input-builder.d.ts +0 -45
  53. package/dist/rule-lab/curation-input-builder.js +0 -133
  54. package/dist/rule-lab/discover.d.ts +0 -13
  55. package/dist/rule-lab/discover.js +0 -74
  56. package/dist/rule-lab/discover.test.d.ts +0 -1
  57. package/dist/rule-lab/discover.test.js +0 -42
  58. package/dist/rule-lab/paths.d.ts +0 -21
  59. package/dist/rule-lab/paths.js +0 -37
  60. package/dist/rule-lab/paths.test.d.ts +0 -1
  61. package/dist/rule-lab/paths.test.js +0 -46
  62. package/dist/rule-lab/promote.d.ts +0 -26
  63. package/dist/rule-lab/promote.js +0 -387
  64. package/dist/rule-lab/promote.test.d.ts +0 -1
  65. package/dist/rule-lab/promote.test.js +0 -314
  66. package/dist/rule-lab/regress.d.ts +0 -60
  67. package/dist/rule-lab/regress.js +0 -122
  68. package/dist/rule-lab/regress.test.d.ts +0 -1
  69. package/dist/rule-lab/regress.test.js +0 -68
  70. package/dist/rule-lab/review-pack.d.ts +0 -34
  71. package/dist/rule-lab/review-pack.js +0 -165
  72. package/dist/rule-lab/review-pack.test.d.ts +0 -1
  73. package/dist/rule-lab/review-pack.test.js +0 -116
  74. package/dist/rule-lab/types.d.ts +0 -135
  75. package/dist/rule-lab/types.js +0 -1
  76. package/skills/_shared/unity-rule-authoring-contract.md +0 -64
  77. package/skills/gitnexus-unity-rule-gen.md +0 -107
@@ -44,7 +44,6 @@ function generateGitNexusContent(projectName, stats, skillScope, generatedSkills
44
44
 
45
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
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
47
 
49
48
  ## Skills
50
49
 
@@ -55,8 +54,7 @@ function generateGitNexusContent(projectName, stats, skillScope, generatedSkills
55
54
  | Trace bugs / "Why is X failing?" | \`${skillRoot}/gitnexus-debugging/SKILL.md\` |
56
55
  | Rename / extract / split / refactor | \`${skillRoot}/gitnexus-refactoring/SKILL.md\` |
57
56
  | Tools, resources, schema reference | \`${skillRoot}/gitnexus-guide/SKILL.md\` |
58
- | Index, status, clean, wiki CLI commands | \`${skillRoot}/gitnexus-cli/SKILL.md\` |
59
- | Create Unity analyze_rules interactively | \`${skillRoot}/gitnexus-unity-rule-gen/SKILL.md\` |${generatedRows}
57
+ | Index, status, clean, wiki CLI commands | \`${skillRoot}/gitnexus-cli/SKILL.md\` |${generatedRows}
60
58
 
61
59
  ${GITNEXUS_END_MARKER}`;
62
60
  }
@@ -135,10 +133,6 @@ async function installSkills(repoPath) {
135
133
  name: 'gitnexus-cli',
136
134
  description: 'Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: "Index this repo", "Reanalyze the codebase", "Generate a wiki"',
137
135
  },
138
- {
139
- name: 'gitnexus-unity-rule-gen',
140
- description: 'Use when the user wants to create Unity analyze_rules for a Unity project repo — interactively collecting chain clues, exploring the graph, generating rule YAML, compiling, and verifying. Examples: "Create unity rules", "Generate analyze rules", "Add resource binding rules for this Unity project"',
141
- },
142
136
  ];
143
137
  for (const skill of skills) {
144
138
  const skillDir = path.join(skillsDir, skill.name);
@@ -4,6 +4,7 @@ export interface StoredAnalyzeOptions {
4
4
  repoAlias?: string;
5
5
  embeddings?: boolean;
6
6
  csharpDefineCsproj?: string;
7
+ aiContext?: boolean;
7
8
  }
8
9
  export interface ResolveAnalyzeOptionsInput {
9
10
  extensions?: string;
@@ -12,6 +13,7 @@ export interface ResolveAnalyzeOptionsInput {
12
13
  embeddings?: boolean;
13
14
  reuseOptions?: boolean;
14
15
  csharpDefineCsproj?: string;
16
+ aiContext?: boolean;
15
17
  }
16
18
  export interface EffectiveAnalyzeOptions {
17
19
  includeExtensions: string[];
@@ -19,6 +21,7 @@ export interface EffectiveAnalyzeOptions {
19
21
  repoAlias?: string;
20
22
  embeddings: boolean;
21
23
  csharpDefineCsproj?: string;
24
+ aiContext: boolean;
22
25
  }
23
26
  export declare function parseExtensionList(rawExtensions?: string): string[];
24
27
  /** Parse comma-separated scope rules (e.g. "Assets/,Packages/com.veewo.*"). */
@@ -30,6 +33,7 @@ export interface ValidatedStoredOptions {
30
33
  repoAlias?: string;
31
34
  embeddings: boolean;
32
35
  csharpDefineCsproj?: string;
36
+ aiContext: boolean;
33
37
  }
34
38
  /**
35
39
  * Validate stored options from meta.json.analyzeOptions before reusing them.
@@ -35,7 +35,14 @@ export function normalizeRepoAlias(repoAlias) {
35
35
  */
36
36
  export async function validateStoredOptions(stored, repoPath) {
37
37
  if (!stored) {
38
- return { includeExtensions: [], scopeRules: [], repoAlias: undefined, embeddings: false, csharpDefineCsproj: undefined };
38
+ return {
39
+ includeExtensions: [],
40
+ scopeRules: [],
41
+ repoAlias: undefined,
42
+ embeddings: false,
43
+ csharpDefineCsproj: undefined,
44
+ aiContext: true,
45
+ };
39
46
  }
40
47
  // Validate repoAlias
41
48
  let repoAlias;
@@ -84,6 +91,7 @@ export async function validateStoredOptions(stored, repoPath) {
84
91
  repoAlias,
85
92
  embeddings: Boolean(stored.embeddings),
86
93
  csharpDefineCsproj,
94
+ aiContext: stored.aiContext !== false,
87
95
  };
88
96
  }
89
97
  export async function resolveEffectiveAnalyzeOptions(options, stored) {
@@ -94,6 +102,7 @@ export async function resolveEffectiveAnalyzeOptions(options, stored) {
94
102
  const hasCliScope = options?.scope !== undefined;
95
103
  const hasCliRepoAlias = options?.repoAlias !== undefined;
96
104
  const hasCliCsproj = options?.csharpDefineCsproj !== undefined;
105
+ const hasCliAiContext = options?.aiContext !== undefined;
97
106
  const canReuse = options?.reuseOptions !== false;
98
107
  const includeExtensions = hasCliExtensions
99
108
  ? includeExtensionsFromCli
@@ -110,11 +119,15 @@ export async function resolveEffectiveAnalyzeOptions(options, stored) {
110
119
  const csharpDefineCsproj = hasCliCsproj
111
120
  ? options.csharpDefineCsproj
112
121
  : (canReuse ? stored?.csharpDefineCsproj : undefined);
122
+ const aiContext = hasCliAiContext
123
+ ? options.aiContext !== false
124
+ : (canReuse ? stored?.aiContext !== false : true);
113
125
  return {
114
126
  includeExtensions: [...includeExtensions],
115
127
  scopeRules: [...scopeRules],
116
128
  repoAlias,
117
129
  embeddings,
118
130
  csharpDefineCsproj,
131
+ aiContext,
119
132
  };
120
133
  }
@@ -20,11 +20,13 @@ test('resolveEffectiveAnalyzeOptions reuses stored settings when CLI omits them'
20
20
  scopeRules: ['Assets/NEON/Code'],
21
21
  repoAlias: 'neonspark-v1-subset',
22
22
  embeddings: true,
23
+ aiContext: false,
23
24
  });
24
25
  assert.deepEqual(resolved.includeExtensions, ['.cs']);
25
26
  assert.deepEqual(resolved.scopeRules, ['Assets/NEON/Code']);
26
27
  assert.equal(resolved.repoAlias, 'neonspark-v1-subset');
27
28
  assert.equal(resolved.embeddings, true);
29
+ assert.equal(resolved.aiContext, false);
28
30
  });
29
31
  test('resolveEffectiveAnalyzeOptions disables reuse via reuseOptions=false', async () => {
30
32
  const resolved = await resolveEffectiveAnalyzeOptions({ reuseOptions: false }, {
@@ -33,12 +35,14 @@ test('resolveEffectiveAnalyzeOptions disables reuse via reuseOptions=false', asy
33
35
  repoAlias: 'neonspark-v1-subset',
34
36
  embeddings: true,
35
37
  csharpDefineCsproj: '/tmp/Assembly-CSharp.csproj',
38
+ aiContext: false,
36
39
  });
37
40
  assert.deepEqual(resolved.includeExtensions, []);
38
41
  assert.deepEqual(resolved.scopeRules, []);
39
42
  assert.equal(resolved.repoAlias, undefined);
40
43
  assert.equal(resolved.embeddings, false);
41
44
  assert.equal(resolved.csharpDefineCsproj, undefined);
45
+ assert.equal(resolved.aiContext, true);
42
46
  });
43
47
  test('resolveEffectiveAnalyzeOptions prefers explicit CLI values over stored settings', async () => {
44
48
  const resolved = await resolveEffectiveAnalyzeOptions({
@@ -46,16 +50,19 @@ test('resolveEffectiveAnalyzeOptions prefers explicit CLI values over stored set
46
50
  scope: 'src/',
47
51
  repoAlias: 'new-alias',
48
52
  embeddings: false,
53
+ aiContext: true,
49
54
  }, {
50
55
  includeExtensions: ['.cs'],
51
56
  scopeRules: ['Assets/NEON/Code'],
52
57
  repoAlias: 'old-alias',
53
58
  embeddings: true,
59
+ aiContext: false,
54
60
  });
55
61
  assert.deepEqual(resolved.includeExtensions, ['.ts']);
56
62
  assert.deepEqual(resolved.scopeRules, ['src']);
57
63
  assert.equal(resolved.repoAlias, 'new-alias');
58
64
  assert.equal(resolved.embeddings, false);
65
+ assert.equal(resolved.aiContext, true);
59
66
  });
60
67
  test('resolveEffectiveAnalyzeOptions no stored uses defaults', async () => {
61
68
  const resolved = await resolveEffectiveAnalyzeOptions({}, undefined);
@@ -64,6 +71,7 @@ test('resolveEffectiveAnalyzeOptions no stored uses defaults', async () => {
64
71
  assert.equal(resolved.repoAlias, undefined);
65
72
  assert.equal(resolved.embeddings, false);
66
73
  assert.equal(resolved.csharpDefineCsproj, undefined);
74
+ assert.equal(resolved.aiContext, true);
67
75
  });
68
76
  test('resolveEffectiveAnalyzeOptions reuses stored scopeRules when CLI scope is omitted', async () => {
69
77
  const resolved = await resolveEffectiveAnalyzeOptions({ extensions: '.ts' }, {
@@ -87,6 +95,18 @@ test('resolveEffectiveAnalyzeOptions CLI csharpDefineCsproj overrides stored', a
87
95
  });
88
96
  assert.equal(resolved.csharpDefineCsproj, '/new/path.csproj');
89
97
  });
98
+ test('resolveEffectiveAnalyzeOptions reuses stored aiContext=false', async () => {
99
+ const resolved = await resolveEffectiveAnalyzeOptions({}, {
100
+ aiContext: false,
101
+ });
102
+ assert.equal(resolved.aiContext, false);
103
+ });
104
+ test('resolveEffectiveAnalyzeOptions CLI aiContext=true overrides stored false', async () => {
105
+ const resolved = await resolveEffectiveAnalyzeOptions({ aiContext: true }, {
106
+ aiContext: false,
107
+ });
108
+ assert.equal(resolved.aiContext, true);
109
+ });
90
110
  // ─── validateStoredOptions tests ────────────────────────────────────
91
111
  test('validateStoredOptions returns defaults when stored is undefined', async () => {
92
112
  const result = await validateStoredOptions(undefined, '/tmp');
@@ -95,6 +115,7 @@ test('validateStoredOptions returns defaults when stored is undefined', async ()
95
115
  assert.equal(result.repoAlias, undefined);
96
116
  assert.equal(result.embeddings, false);
97
117
  assert.equal(result.csharpDefineCsproj, undefined);
118
+ assert.equal(result.aiContext, true);
98
119
  });
99
120
  test('validateStoredOptions passes valid options unchanged', async () => {
100
121
  const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-validate-'));
@@ -106,12 +127,14 @@ test('validateStoredOptions passes valid options unchanged', async () => {
106
127
  repoAlias: 'my-repo',
107
128
  csharpDefineCsproj: csprojPath,
108
129
  embeddings: true,
130
+ aiContext: false,
109
131
  }, tmpDir);
110
132
  assert.deepEqual(result.includeExtensions, ['.cs', '.ts']);
111
133
  assert.deepEqual(result.scopeRules, ['Assets/']);
112
134
  assert.equal(result.repoAlias, 'my-repo');
113
135
  assert.equal(result.csharpDefineCsproj, csprojPath);
114
136
  assert.equal(result.embeddings, true);
137
+ assert.equal(result.aiContext, false);
115
138
  });
116
139
  test('validateStoredOptions warns on invalid repoAlias and falls back', async () => {
117
140
  const result = await validateStoredOptions({
@@ -4,7 +4,6 @@ export function toPipelineRuntimeSummary(input) {
4
4
  communityResult: input.communityResult,
5
5
  processResult: input.processResult,
6
6
  unityResult: input.unityResult,
7
- unityRuleBindingResult: input.unityRuleBindingResult,
8
7
  scopeDiagnostics: input.scopeDiagnostics,
9
8
  csharpPreprocDiagnostics: input.csharpPreprocDiagnostics,
10
9
  };
@@ -7,7 +7,6 @@ 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
10
  csharpPreprocDiagnostics: {
12
11
  enabled: true,
13
12
  defineSymbolCount: 2,
@@ -21,6 +20,5 @@ test('toPipelineRuntimeSummary drops graph reference and preserves reporting fie
21
20
  assert.equal('graph' in out, false);
22
21
  assert.equal(out.totalFileCount, 12);
23
22
  assert.equal(out.communityResult?.stats.totalCommunities, 3);
24
- assert.equal(out.unityRuleBindingResult?.edgesInjected, 1);
25
23
  assert.equal(out.csharpPreprocDiagnostics?.normalizedFiles, 1);
26
24
  });
@@ -1,5 +1,4 @@
1
1
  import type { CSharpPreprocDiagnostics } from '../types/pipeline.js';
2
- import type { UnityRuntimeBindingResult } from '../core/ingestion/unity-runtime-binding-rules.js';
3
2
  export interface FallbackInsertStats {
4
3
  attempted: number;
5
4
  succeeded: number;
@@ -7,6 +6,5 @@ export interface FallbackInsertStats {
7
6
  }
8
7
  export declare function formatCSharpPreprocDiagnosticsSummary(diagnostics: CSharpPreprocDiagnostics | undefined, previewLimit?: number): string[];
9
8
  export declare function formatUnityDiagnosticsSummary(diagnostics: string[] | undefined, previewLimit?: number): string[];
10
- export declare function formatUnityRuleBindingSummary(result: UnityRuntimeBindingResult | undefined, previewLimit?: number): string[];
11
9
  export declare function formatFallbackSummary(warnings: string[] | undefined, stats: FallbackInsertStats | undefined, previewLimit?: number): string[];
12
10
  export declare function resolveFallbackStats(warnings: string[] | undefined, stats: FallbackInsertStats | undefined): FallbackInsertStats;
@@ -32,30 +32,6 @@ export function formatUnityDiagnosticsSummary(diagnostics, previewLimit = 3) {
32
32
  }
33
33
  return lines;
34
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
- }
59
35
  export function formatFallbackSummary(warnings, stats, previewLimit = 5) {
60
36
  if (!warnings || warnings.length === 0) {
61
37
  return [];
@@ -1,6 +1,6 @@
1
1
  import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
- import { formatFallbackSummary, formatUnityDiagnosticsSummary, formatUnityRuleBindingSummary, resolveFallbackStats, } from './analyze-summary.js';
3
+ import { formatFallbackSummary, formatUnityDiagnosticsSummary, 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,70 +29,6 @@ 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
- });
96
32
  test('formatFallbackSummary returns empty when no warnings exist', () => {
97
33
  const lines = formatFallbackSummary([], {
98
34
  attempted: 0,
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export interface AnalyzeOptions {
7
7
  force?: boolean;
8
+ aiContext?: boolean;
8
9
  embeddings?: boolean;
9
10
  extensions?: string;
10
11
  scope?: string;
@@ -19,7 +19,7 @@ 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, validateStoredOptions } from './analyze-options.js';
22
- import { formatCSharpPreprocDiagnosticsSummary, formatFallbackSummary, formatUnityDiagnosticsSummary, formatUnityRuleBindingSummary, resolveFallbackStats, } from './analyze-summary.js';
22
+ import { formatCSharpPreprocDiagnosticsSummary, formatFallbackSummary, formatUnityDiagnosticsSummary, resolveFallbackStats, } from './analyze-summary.js';
23
23
  import { resolveChildProcessExit } from './exit-code.js';
24
24
  import { toPipelineRuntimeSummary } from './analyze-runtime-summary.js';
25
25
  const HEAP_MB = 8192;
@@ -111,6 +111,7 @@ export const analyzeCommand = async (inputPath, options) => {
111
111
  let repoAlias;
112
112
  let embeddingsEnabled = false;
113
113
  let csharpDefineCsproj;
114
+ let aiContextEnabled = true;
114
115
  try {
115
116
  const validatedStored = await validateStoredOptions(options?.reuseOptions !== false ? existingMeta?.analyzeOptions : undefined, repoPath);
116
117
  const effectiveOptions = await resolveEffectiveAnalyzeOptions({
@@ -120,12 +121,14 @@ export const analyzeCommand = async (inputPath, options) => {
120
121
  embeddings: options?.embeddings,
121
122
  reuseOptions: options?.reuseOptions,
122
123
  csharpDefineCsproj: options?.csharpDefineCsproj,
124
+ aiContext: options?.aiContext,
123
125
  }, validatedStored);
124
126
  includeExtensions = effectiveOptions.includeExtensions;
125
127
  scopeRules = effectiveOptions.scopeRules;
126
128
  repoAlias = effectiveOptions.repoAlias;
127
129
  embeddingsEnabled = effectiveOptions.embeddings;
128
130
  csharpDefineCsproj = effectiveOptions.csharpDefineCsproj;
131
+ aiContextEnabled = effectiveOptions.aiContext;
129
132
  }
130
133
  catch (error) {
131
134
  console.log(` ${error?.message || String(error)}\n`);
@@ -141,6 +144,7 @@ export const analyzeCommand = async (inputPath, options) => {
141
144
  options?.repoAlias !== undefined ||
142
145
  options?.csharpDefineCsproj !== undefined ||
143
146
  options?.embeddings !== undefined ||
147
+ options?.aiContext !== undefined ||
144
148
  options?.reuseOptions === false;
145
149
  if (!hasCliOverrides) {
146
150
  console.log(' Already up to date\n');
@@ -150,7 +154,8 @@ export const analyzeCommand = async (inputPath, options) => {
150
154
  includeExtensions.length === 0 &&
151
155
  scopeRules.length === 0 &&
152
156
  !repoAlias &&
153
- !embeddingsEnabled) {
157
+ !embeddingsEnabled &&
158
+ aiContextEnabled) {
154
159
  console.log(' Already up to date\n');
155
160
  return;
156
161
  }
@@ -350,6 +355,7 @@ export const analyzeCommand = async (inputPath, options) => {
350
355
  scopeRules,
351
356
  repoAlias,
352
357
  embeddings: embeddingsEnabled,
358
+ aiContext: aiContextEnabled,
353
359
  ...(csharpDefineCsproj ? { csharpDefineCsproj } : {}),
354
360
  },
355
361
  stats: {
@@ -382,17 +388,20 @@ export const analyzeCommand = async (inputPath, options) => {
382
388
  const skillResult = await generateSkillFiles(repoPath, projectName, pipelineForSkills);
383
389
  generatedSkills = skillResult.skills;
384
390
  }
385
- const cliConfig = await loadCLIConfig();
386
- const aiContext = await generateAIContextFiles(repoPath, storagePath, projectName, {
387
- files: pipelineRuntime.totalFileCount,
388
- nodes: stats.nodes,
389
- edges: stats.edges,
390
- communities: pipelineRuntime.communityResult?.stats.totalCommunities,
391
- clusters: aggregatedClusterCount,
392
- processes: pipelineRuntime.processResult?.stats.totalProcesses,
393
- }, {
394
- skillScope: (cliConfig.setupScope === 'global') ? 'global' : 'project',
395
- }, generatedSkills);
391
+ let aiContext = { files: [] };
392
+ if (aiContextEnabled) {
393
+ const cliConfig = await loadCLIConfig();
394
+ aiContext = await generateAIContextFiles(repoPath, storagePath, projectName, {
395
+ files: pipelineRuntime.totalFileCount,
396
+ nodes: stats.nodes,
397
+ edges: stats.edges,
398
+ communities: pipelineRuntime.communityResult?.stats.totalCommunities,
399
+ clusters: aggregatedClusterCount,
400
+ processes: pipelineRuntime.processResult?.stats.totalProcesses,
401
+ }, {
402
+ skillScope: (cliConfig.setupScope === 'global') ? 'global' : 'project',
403
+ }, generatedSkills);
404
+ }
396
405
  await closeLbug();
397
406
  // Note: we intentionally do NOT call disposeEmbedder() here.
398
407
  // ONNX Runtime's native cleanup segfaults on macOS and some Linux configs.
@@ -432,10 +441,6 @@ export const analyzeCommand = async (inputPath, options) => {
432
441
  for (const line of unitySummaryLines) {
433
442
  console.log(` ${line}`);
434
443
  }
435
- const unityRuleBindingSummaryLines = formatUnityRuleBindingSummary(pipelineRuntime.unityRuleBindingResult);
436
- for (const line of unityRuleBindingSummaryLines) {
437
- console.log(` ${line}`);
438
- }
439
444
  const csharpPreprocSummaryLines = formatCSharpPreprocDiagnosticsSummary(pipelineRuntime.csharpPreprocDiagnostics);
440
445
  for (const line of csharpPreprocSummaryLines) {
441
446
  console.log(` ${line}`);
@@ -446,7 +451,10 @@ export const analyzeCommand = async (inputPath, options) => {
446
451
  console.log(` File filter: ${includeExtensions.join(', ')}`);
447
452
  }
448
453
  console.log(` ${repoPath}`);
449
- if (aiContext.files.length > 0) {
454
+ if (!aiContextEnabled) {
455
+ console.log(' Context: skipped (--no-ai-context)');
456
+ }
457
+ else if (aiContext.files.length > 0) {
450
458
  console.log(` Context: ${aiContext.files.join(', ')}`);
451
459
  }
452
460
  if (lbugWarnings.length > 0) {
package/dist/cli/clean.js CHANGED
@@ -5,7 +5,28 @@
5
5
  * Also unregisters the repo from the global registry.
6
6
  */
7
7
  import fs from 'fs/promises';
8
+ import path from 'path';
8
9
  import { findRepo, unregisterRepo, listRegisteredRepos } from '../storage/repo-manager.js';
10
+ /**
11
+ * Selectively delete contents of a .gitnexus directory, preserving config files.
12
+ * Preserves: meta.json, config.json, .gitnexusignore
13
+ */
14
+ const selectiveClean = async (storagePath) => {
15
+ const PRESERVE = new Set(['meta.json', 'config.json', '.gitnexusignore']);
16
+ let entries;
17
+ try {
18
+ entries = await fs.readdir(storagePath);
19
+ }
20
+ catch {
21
+ // Directory doesn't exist, nothing to clean
22
+ return;
23
+ }
24
+ for (const entry of entries) {
25
+ if (PRESERVE.has(entry))
26
+ continue;
27
+ await fs.rm(path.join(storagePath, entry), { recursive: true, force: true });
28
+ }
29
+ };
9
30
  export const cleanCommand = async (options) => {
10
31
  // --all flag: clean all indexed repos
11
32
  if (options?.all) {
@@ -25,7 +46,7 @@ export const cleanCommand = async (options) => {
25
46
  const entries = await listRegisteredRepos();
26
47
  for (const entry of entries) {
27
48
  try {
28
- await fs.rm(entry.storagePath, { recursive: true, force: true });
49
+ await selectiveClean(entry.storagePath);
29
50
  await unregisterRepo(entry.path);
30
51
  console.log(`Cleaned: ${entry.name} (${entry.storagePath})`);
31
52
  }
@@ -50,7 +71,7 @@ export const cleanCommand = async (options) => {
50
71
  return;
51
72
  }
52
73
  try {
53
- await fs.rm(repo.storagePath, { recursive: true, force: true });
74
+ await selectiveClean(repo.storagePath);
54
75
  await unregisterRepo(repo.repoPath);
55
76
  console.log(`Cleaned: ${repo.storagePath}`);
56
77
  }
package/dist/cli/index.js CHANGED
@@ -1,10 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  // Heap re-spawn removed — only analyze.ts needs the 8GB heap (via its own ensureHeap()).
3
3
  // Removing it from here improves MCP server startup time significantly.
4
- import { Command } from 'commander';
4
+ import { Command, Option } from 'commander';
5
5
  import { createRequire } from 'node:module';
6
6
  import { createLazyAction } from './lazy-action.js';
7
- import { attachRuleLabCommands } from './rule-lab.js';
8
7
  const _require = createRequire(import.meta.url);
9
8
  const pkg = _require('../../package.json');
10
9
  const program = new Command();
@@ -25,6 +24,8 @@ program
25
24
  .description('Index a repository (full analysis)')
26
25
  .option('-f, --force', 'Force full re-index even if up to date')
27
26
  .option('--no-reuse-options', 'Do not reuse stored analyze options from previous index')
27
+ .addOption(new Option('--ai-context', 'Write AGENTS.md/CLAUDE.md and install repo-local GitNexus skills').hideHelp())
28
+ .option('--no-ai-context', 'Skip writing AGENTS.md/CLAUDE.md and installing repo-local GitNexus skills')
28
29
  .option('--embeddings', 'Enable embedding generation for semantic search (off by default)')
29
30
  .option('--extensions <list>', 'Comma-separated file extensions to include (e.g. .cs,.ts)')
30
31
  .option('--repo-alias <name>', 'Override indexed repository name with a stable alias')
@@ -58,7 +59,6 @@ program
58
59
  .option('-f, --force', 'Skip confirmation prompt')
59
60
  .option('--all', 'Clean all indexed repos')
60
61
  .action(createLazyAction(() => import('./clean.js'), 'cleanCommand'));
61
- attachRuleLabCommands(program, (handlerName) => createLazyAction(() => import('./rule-lab.js'), handlerName));
62
62
  program
63
63
  .command('wiki [path]')
64
64
  .description('Generate repository wiki from knowledge graph')
@@ -51,6 +51,7 @@ test('saveMeta/loadMeta persists analyzeOptions for future re-index reuse', asyn
51
51
  scopeRules: ['Assets/NEON/Code'],
52
52
  repoAlias: 'neonspark-v1-subset',
53
53
  embeddings: true,
54
+ aiContext: false,
54
55
  },
55
56
  stats: { files: 1, nodes: 2, edges: 3 },
56
57
  });
@@ -60,6 +61,7 @@ test('saveMeta/loadMeta persists analyzeOptions for future re-index reuse', asyn
60
61
  scopeRules: ['Assets/NEON/Code'],
61
62
  repoAlias: 'neonspark-v1-subset',
62
63
  embeddings: true,
64
+ aiContext: false,
63
65
  });
64
66
  assert.equal(meta?.repoId, 'neonspark-v1-subset');
65
67
  });
@@ -9,11 +9,9 @@ import { processCommunities } from './community-processor.js';
9
9
  import { processProcesses } from './process-processor.js';
10
10
  import { processUnityResources } from './unity-resource-processor.js';
11
11
  import { applyUnityLifecycleSyntheticCalls } from './unity-lifecycle-synthetic-calls.js';
12
- import { applyUnityRuntimeBindingRules } from './unity-runtime-binding-rules.js';
13
12
  import { resolveUnityConfig } from '../config/unity-config.js';
14
13
  import { loadCSharpDefineProfileFromCsproj } from '../tree-sitter/csharp-define-profile.js';
15
14
  import { normalizeCSharpPreprocessorBranches } from '../tree-sitter/csharp-preproc-normalizer.js';
16
- import { loadAnalyzeRules } from '../../mcp/local/runtime-claim-rule-registry.js';
17
15
  import { createResolutionContext } from './resolution-context.js';
18
16
  import { createASTCache } from './ast-cache.js';
19
17
  import { walkRepositoryPaths, readFileContents, walkUnityResourcePaths } from './filesystem-walker.js';
@@ -341,7 +339,6 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
341
339
  let communityResult;
342
340
  let processResult;
343
341
  let unityResult;
344
- let unityRuleBindingResult;
345
342
  if (!options?.skipGraphPhases) {
346
343
  // ── Phase 4.5: Method Resolution Order ──────────────────────────────
347
344
  onProgress({
@@ -422,45 +419,6 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
422
419
  if (isDev && isUnityProject) {
423
420
  console.log(`[UnityLifecycle] auto-detected hosts=${unityLifecycleSyntheticResult.hostCount} syntheticEdges=${unityLifecycleSyntheticResult.syntheticEdgeCount} rejectedHosts=${unityLifecycleSyntheticResult.rejectedHostCount}`);
424
421
  }
425
- // Phase 5.7: rule-driven binding injection (Phase 3)
426
- try {
427
- const analyzeRules = await loadAnalyzeRules(repoPath);
428
- const bindingResult = applyUnityRuntimeBindingRules(graph, analyzeRules, unityConfig.config);
429
- unityRuleBindingResult = bindingResult;
430
- if (isDev && bindingResult.edgesInjected > 0) {
431
- console.log(`[UnityRuleBinding] injected ${bindingResult.edgesInjected} edges from ${analyzeRules.length} rule(s)`);
432
- }
433
- }
434
- catch (err) {
435
- // rule catalog missing or invalid — skip silently
436
- console.warn(`[UnityRuleBinding] failed to load or apply analyze rules: ${err instanceof Error ? err.message : String(err)}`);
437
- const reason = err instanceof Error ? err.message : String(err);
438
- unityRuleBindingResult = {
439
- edgesInjected: 0,
440
- ruleResults: [],
441
- diagnostics: {
442
- rulesEvaluated: 0,
443
- bindingsEvaluated: 0,
444
- bindingsByKind: {},
445
- methodLookupCalls: 0,
446
- methodLookupCacheHits: 0,
447
- sceneRuntimeTraversalCalls: 0,
448
- sceneRuntimeTraversalCacheHits: 0,
449
- sceneRuntimeResourcesVisited: 0,
450
- anomalies: [`failed to load/apply analyze rules: ${reason}`],
451
- shouldAgentReport: true,
452
- agentReportReason: 'failed to load/apply analyze rules',
453
- summary: [
454
- 'rule_binding.summary: rules=0, bindings=0, edges=0',
455
- 'rule_binding.bindings_by_kind: none',
456
- 'rule_binding.lookup: method_calls=0, cache_hits=0',
457
- 'rule_binding.scene_closure: traversals=0, cache_hits=0, visited_resources=0',
458
- 'rule_binding.agent_report: should_report=true reason="failed to load/apply analyze rules"',
459
- `rule_binding.anomaly: failed to load/apply analyze rules: ${reason}`,
460
- ],
461
- },
462
- };
463
- }
464
422
  // ── Phase 6: Processes ─────────────────────────────────────────────
465
423
  onProgress({
466
424
  phase: 'processes',
@@ -545,7 +503,6 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
545
503
  communityResult,
546
504
  processResult,
547
505
  unityResult,
548
- unityRuleBindingResult,
549
506
  scopeDiagnostics: scopeSelection.diagnostics,
550
507
  csharpPreprocDiagnostics,
551
508
  };