@veewo/gitnexus 1.5.6 → 1.5.8

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 (104) hide show
  1. package/dist/benchmark/analyze-runner.d.ts +0 -2
  2. package/dist/benchmark/analyze-runner.js +0 -6
  3. package/dist/benchmark/analyze-runner.test.js +1 -10
  4. package/dist/benchmark/runner.d.ts +0 -2
  5. package/dist/benchmark/runner.js +0 -2
  6. package/dist/benchmark/u2-e2e/neonspark-full-e2e.js +0 -11
  7. package/dist/benchmark/u2-performance-sampler.js +3 -16
  8. package/dist/cli/ai-context.js +1 -7
  9. package/dist/cli/analyze-options.d.ts +19 -6
  10. package/dist/cli/analyze-options.js +76 -71
  11. package/dist/cli/analyze-options.test.js +78 -73
  12. package/dist/cli/analyze-runtime-summary.js +0 -1
  13. package/dist/cli/analyze-runtime-summary.test.js +0 -2
  14. package/dist/cli/analyze-summary.d.ts +0 -2
  15. package/dist/cli/analyze-summary.js +0 -24
  16. package/dist/cli/analyze-summary.test.js +1 -65
  17. package/dist/cli/analyze.d.ts +2 -4
  18. package/dist/cli/analyze.js +14 -30
  19. package/dist/cli/analyze.test.js +9 -15
  20. package/dist/cli/benchmark-agent-context.d.ts +0 -2
  21. package/dist/cli/benchmark-agent-context.js +0 -2
  22. package/dist/cli/benchmark-agent-safe-query-context.d.ts +0 -2
  23. package/dist/cli/benchmark-agent-safe-query-context.js +0 -2
  24. package/dist/cli/benchmark-unity.d.ts +0 -2
  25. package/dist/cli/benchmark-unity.js +0 -2
  26. package/dist/cli/clean.d.ts +2 -3
  27. package/dist/cli/clean.js +4 -25
  28. package/dist/cli/index.js +1 -12
  29. package/dist/core/ingestion/pipeline.js +1 -44
  30. package/dist/mcp/local/agent-safe-response.js +1 -1
  31. package/dist/mcp/local/local-backend.d.ts +0 -23
  32. package/dist/mcp/local/local-backend.js +69 -248
  33. package/dist/mcp/local/runtime-chain-verify.test.js +0 -49
  34. package/dist/mcp/local/runtime-claim-rule-registry.d.ts +0 -11
  35. package/dist/mcp/local/runtime-claim-rule-registry.js +0 -159
  36. package/dist/mcp/local/runtime-claim-rule-registry.test.js +67 -214
  37. package/dist/mcp/tools.js +0 -70
  38. package/dist/storage/repo-manager.d.ts +1 -0
  39. package/dist/types/pipeline.d.ts +0 -3
  40. package/package.json +1 -1
  41. package/skills/gitnexus-cli.md +62 -38
  42. package/vendor/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
  43. package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +122 -0
  44. package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +126 -0
  45. package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +122 -0
  46. package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +122 -0
  47. package/vendor/tree-sitter-dart/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
  48. package/vendor/tree-sitter-dart/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
  49. package/vendor/tree-sitter-proto/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
  50. package/vendor/tree-sitter-proto/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
  51. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +0 -60
  52. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +0 -395
  53. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.d.ts +0 -1
  54. package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +0 -41
  55. package/dist/cli/rule-lab.d.ts +0 -38
  56. package/dist/cli/rule-lab.js +0 -148
  57. package/dist/cli/rule-lab.test.d.ts +0 -1
  58. package/dist/cli/rule-lab.test.js +0 -31
  59. package/dist/cli/scope-manifest-config.d.ts +0 -9
  60. package/dist/cli/scope-manifest-config.js +0 -37
  61. package/dist/cli/sync-manifest.d.ts +0 -27
  62. package/dist/cli/sync-manifest.js +0 -200
  63. package/dist/cli/sync-manifest.test.d.ts +0 -1
  64. package/dist/cli/sync-manifest.test.js +0 -88
  65. package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +0 -26
  66. package/dist/core/ingestion/unity-runtime-binding-rules.js +0 -408
  67. package/dist/rule-lab/analyze.d.ts +0 -13
  68. package/dist/rule-lab/analyze.js +0 -125
  69. package/dist/rule-lab/analyze.test.d.ts +0 -1
  70. package/dist/rule-lab/analyze.test.js +0 -246
  71. package/dist/rule-lab/compile.d.ts +0 -5
  72. package/dist/rule-lab/compile.js +0 -51
  73. package/dist/rule-lab/compiled-bundles.d.ts +0 -30
  74. package/dist/rule-lab/compiled-bundles.js +0 -36
  75. package/dist/rule-lab/curate.d.ts +0 -33
  76. package/dist/rule-lab/curate.js +0 -155
  77. package/dist/rule-lab/curate.test.d.ts +0 -1
  78. package/dist/rule-lab/curate.test.js +0 -137
  79. package/dist/rule-lab/curation-input-builder.d.ts +0 -45
  80. package/dist/rule-lab/curation-input-builder.js +0 -133
  81. package/dist/rule-lab/discover.d.ts +0 -13
  82. package/dist/rule-lab/discover.js +0 -74
  83. package/dist/rule-lab/discover.test.d.ts +0 -1
  84. package/dist/rule-lab/discover.test.js +0 -42
  85. package/dist/rule-lab/paths.d.ts +0 -21
  86. package/dist/rule-lab/paths.js +0 -37
  87. package/dist/rule-lab/paths.test.d.ts +0 -1
  88. package/dist/rule-lab/paths.test.js +0 -46
  89. package/dist/rule-lab/promote.d.ts +0 -26
  90. package/dist/rule-lab/promote.js +0 -387
  91. package/dist/rule-lab/promote.test.d.ts +0 -1
  92. package/dist/rule-lab/promote.test.js +0 -314
  93. package/dist/rule-lab/regress.d.ts +0 -60
  94. package/dist/rule-lab/regress.js +0 -122
  95. package/dist/rule-lab/regress.test.d.ts +0 -1
  96. package/dist/rule-lab/regress.test.js +0 -68
  97. package/dist/rule-lab/review-pack.d.ts +0 -34
  98. package/dist/rule-lab/review-pack.js +0 -165
  99. package/dist/rule-lab/review-pack.test.d.ts +0 -1
  100. package/dist/rule-lab/review-pack.test.js +0 -116
  101. package/dist/rule-lab/types.d.ts +0 -135
  102. package/dist/rule-lab/types.js +0 -1
  103. package/skills/_shared/unity-rule-authoring-contract.md +0 -64
  104. package/skills/gitnexus-unity-rule-gen.md +0 -107
@@ -1,8 +1,6 @@
1
1
  export interface AnalyzeRunOptions {
2
2
  extensions?: string;
3
3
  repoAlias?: string;
4
- scopeManifest?: string;
5
- scopePrefix?: string[];
6
4
  }
7
5
  export declare function parseAnalyzeSummary(output: string): {
8
6
  totalSeconds: number;
@@ -21,12 +21,6 @@ export function buildAnalyzeArgs(repoPath, options) {
21
21
  if (options.repoAlias) {
22
22
  args.push('--repo-alias', options.repoAlias);
23
23
  }
24
- if (options.scopeManifest) {
25
- args.push('--scope-manifest', options.scopeManifest);
26
- }
27
- for (const prefix of options.scopePrefix || []) {
28
- args.push('--scope-prefix', prefix);
29
- }
30
24
  return args;
31
25
  }
32
26
  export async function runAnalyze(repoPath, options) {
@@ -11,12 +11,10 @@ Repository indexed successfully (42.3s)
11
11
  assert.equal(parsed.nodes, 51172);
12
12
  assert.equal(parsed.edges, 108578);
13
13
  });
14
- test('buildAnalyzeArgs forwards alias and scope options', () => {
14
+ test('buildAnalyzeArgs forwards alias and extensions', () => {
15
15
  const args = buildAnalyzeArgs('/repo/path', {
16
16
  extensions: '.cs,.ts',
17
17
  repoAlias: 'neonspark-v1-subset',
18
- scopeManifest: '/tmp/scope-manifest.txt',
19
- scopePrefix: ['Assets/NEON/Code', 'Packages/com.veewo.*'],
20
18
  });
21
19
  assert.deepEqual(args, [
22
20
  'dist/cli/index.js',
@@ -27,18 +25,11 @@ test('buildAnalyzeArgs forwards alias and scope options', () => {
27
25
  '/repo/path',
28
26
  '--repo-alias',
29
27
  'neonspark-v1-subset',
30
- '--scope-manifest',
31
- '/tmp/scope-manifest.txt',
32
- '--scope-prefix',
33
- 'Assets/NEON/Code',
34
- '--scope-prefix',
35
- 'Packages/com.veewo.*',
36
28
  ]);
37
29
  });
38
30
  test('buildAnalyzeArgs omits --extensions when not explicitly provided', () => {
39
31
  const args = buildAnalyzeArgs('/repo/path', {
40
32
  repoAlias: 'neonspark-v1-subset',
41
- scopeManifest: '/tmp/scope-manifest.txt',
42
33
  });
43
34
  assert.equal(args.includes('--extensions'), false);
44
35
  });
@@ -16,8 +16,6 @@ interface RunBenchmarkOptions {
16
16
  profile: ProfileConfig;
17
17
  reportDir?: string;
18
18
  extensions: string;
19
- scopeManifest?: string;
20
- scopePrefix?: string[];
21
19
  skipAnalyze: boolean;
22
20
  }
23
21
  export interface BenchmarkResult {
@@ -198,8 +198,6 @@ export async function runBenchmark(ds, options) {
198
198
  const analyze = await runAnalyze(path.resolve(options.targetPath), {
199
199
  extensions: options.extensions,
200
200
  repoAlias: options.repoAlias,
201
- scopeManifest: options.scopeManifest,
202
- scopePrefix: options.scopePrefix,
203
201
  });
204
202
  analyzeSummary = parseAnalyzeSummary(`${analyze.stdout}\n${analyze.stderr}`);
205
203
  }
@@ -79,13 +79,6 @@ async function execCommand(command, args, cwd) {
79
79
  });
80
80
  });
81
81
  }
82
- function scopePrefixArgs(prefixes) {
83
- const out = [];
84
- for (const prefix of prefixes) {
85
- out.push('--scope-prefix', prefix);
86
- }
87
- return out;
88
- }
89
82
  async function runRequiredCommand(command, args, cwd) {
90
83
  const result = await execCommand(command, args, cwd);
91
84
  if (result.code !== 0) {
@@ -282,7 +275,6 @@ export async function runNeonsparkU2E2E(options) {
282
275
  },
283
276
  'pipeline-profile': async () => {
284
277
  const reportPath = path.join(reportDir, 'pipeline-profile.json');
285
- const scopeArgs = scopePrefixArgs(config.scope.scriptPrefixes || []);
286
278
  await runRequiredCommand('npm', [
287
279
  '--prefix',
288
280
  'gitnexus',
@@ -295,14 +287,12 @@ export async function runNeonsparkU2E2E(options) {
295
287
  '1',
296
288
  '--report',
297
289
  reportPath,
298
- ...scopeArgs,
299
290
  ], repoRoot);
300
291
  const raw = await fs.readFile(reportPath, 'utf-8');
301
292
  state.pipelineProfile = JSON.parse(raw);
302
293
  return state.pipelineProfile;
303
294
  },
304
295
  analyze: async () => {
305
- const scopeArgs = scopePrefixArgs(config.scope.scriptPrefixes || []);
306
296
  const analyze = await runRequiredCommand('/usr/bin/time', [
307
297
  '-p',
308
298
  'node',
@@ -313,7 +303,6 @@ export async function runNeonsparkU2E2E(options) {
313
303
  '.cs',
314
304
  '--repo-alias',
315
305
  repoAlias,
316
- ...scopeArgs,
317
306
  config.targetPath,
318
307
  ], gitnexusRoot);
319
308
  const logPath = path.join(reportDir, 'analyze.log');
@@ -2,7 +2,6 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { performance } from 'node:perf_hooks';
4
4
  import { runPipelineFromRepo } from '../core/ingestion/pipeline.js';
5
- import { resolveAnalyzeScopeRules } from '../cli/analyze-options.js';
6
5
  export function computeNumericStats(values) {
7
6
  if (values.length === 0) {
8
7
  return { mean: 0, median: 0, min: 0, max: 0, spread: 0 };
@@ -96,32 +95,22 @@ function parseArgs(argv) {
96
95
  const targetPath = get('--target-path');
97
96
  const reportPath = get('--report');
98
97
  const thresholdsPath = get('--thresholds');
99
- const scopeManifest = get('--scope-manifest');
100
98
  const runs = Number(get('--runs') || '3');
101
- const scopePrefix = [];
102
- for (let i = 0; i < argv.length; i += 1) {
103
- if (argv[i] === '--scope-prefix' && i + 1 < argv.length) {
104
- scopePrefix.push(argv[i + 1]);
105
- i += 1;
106
- }
107
- }
108
99
  if (!targetPath)
109
100
  throw new Error('Missing required arg: --target-path <path>');
110
101
  if (!reportPath)
111
102
  throw new Error('Missing required arg: --report <path>');
112
103
  if (!Number.isFinite(runs) || runs <= 0)
113
104
  throw new Error('Invalid --runs, must be positive integer');
114
- return { targetPath: path.resolve(targetPath), runs: Math.floor(runs), reportPath: path.resolve(reportPath), thresholdsPath: thresholdsPath ? path.resolve(thresholdsPath) : undefined, scopeManifest, scopePrefix };
105
+ return { targetPath: path.resolve(targetPath), runs: Math.floor(runs), reportPath: path.resolve(reportPath), thresholdsPath: thresholdsPath ? path.resolve(thresholdsPath) : undefined };
115
106
  }
116
107
  function round1(value) {
117
108
  return Number(value.toFixed(1));
118
109
  }
119
110
  async function main() {
120
111
  const args = parseArgs(process.argv.slice(2));
121
- const scopeRules = await resolveAnalyzeScopeRules({
122
- scopeManifest: args.scopeManifest,
123
- scopePrefix: args.scopePrefix,
124
- });
112
+ // No scope rules — full repo scan
113
+ const scopeRules = [];
125
114
  const runs = [];
126
115
  for (let run = 1; run <= args.runs; run += 1) {
127
116
  console.log(`[u2-sampler] run ${run}/${args.runs} ...`);
@@ -145,8 +134,6 @@ async function main() {
145
134
  targetPath: args.targetPath,
146
135
  runs: args.runs,
147
136
  scope: {
148
- scopeManifest: args.scopeManifest || null,
149
- scopePrefix: args.scopePrefix,
150
137
  scopeRuleCount: scopeRules.length,
151
138
  },
152
139
  samples: runs,
@@ -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);
@@ -1,26 +1,39 @@
1
- export interface AnalyzeScopeOptions {
2
- scopeManifest?: string;
3
- scopePrefix?: string[] | string;
4
- }
5
1
  export interface StoredAnalyzeOptions {
6
2
  includeExtensions?: string[];
7
3
  scopeRules?: string[];
8
4
  repoAlias?: string;
9
5
  embeddings?: boolean;
6
+ csharpDefineCsproj?: string;
10
7
  }
11
- export interface ResolveAnalyzeOptionsInput extends AnalyzeScopeOptions {
8
+ export interface ResolveAnalyzeOptionsInput {
12
9
  extensions?: string;
10
+ scope?: string;
13
11
  repoAlias?: string;
14
12
  embeddings?: boolean;
15
13
  reuseOptions?: boolean;
14
+ csharpDefineCsproj?: string;
16
15
  }
17
16
  export interface EffectiveAnalyzeOptions {
18
17
  includeExtensions: string[];
19
18
  scopeRules: string[];
20
19
  repoAlias?: string;
21
20
  embeddings: boolean;
21
+ csharpDefineCsproj?: string;
22
22
  }
23
23
  export declare function parseExtensionList(rawExtensions?: string): string[];
24
+ /** Parse comma-separated scope rules (e.g. "Assets/,Packages/com.veewo.*"). */
25
+ export declare function parseScopeList(rawScope?: string): string[];
24
26
  export declare function normalizeRepoAlias(repoAlias?: string): string | undefined;
25
- export declare function resolveAnalyzeScopeRules(options?: AnalyzeScopeOptions): Promise<string[]>;
27
+ export interface ValidatedStoredOptions {
28
+ includeExtensions: string[];
29
+ scopeRules: string[];
30
+ repoAlias?: string;
31
+ embeddings: boolean;
32
+ csharpDefineCsproj?: string;
33
+ }
34
+ /**
35
+ * Validate stored options from meta.json.analyzeOptions before reusing them.
36
+ * Invalid fields are filtered out or set to undefined, with console.warn output.
37
+ */
38
+ export declare function validateStoredOptions(stored: StoredAnalyzeOptions | undefined, repoPath: string): Promise<ValidatedStoredOptions>;
26
39
  export declare function resolveEffectiveAnalyzeOptions(options?: ResolveAnalyzeOptionsInput, stored?: StoredAnalyzeOptions): Promise<EffectiveAnalyzeOptions>;
@@ -1,7 +1,5 @@
1
1
  import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
2
  import { normalizeScopeRules } from '../core/ingestion/scope-filter.js';
4
- import { parseScopeManifestConfig } from './scope-manifest-config.js';
5
3
  const REPO_ALIAS_REGEX = /^[a-zA-Z0-9._-]{3,64}$/;
6
4
  export function parseExtensionList(rawExtensions) {
7
5
  return (rawExtensions || '')
@@ -10,6 +8,16 @@ export function parseExtensionList(rawExtensions) {
10
8
  .filter(Boolean)
11
9
  .map((ext) => (ext.startsWith('.') ? ext : `.${ext}`));
12
10
  }
11
+ /** Parse comma-separated scope rules (e.g. "Assets/,Packages/com.veewo.*"). */
12
+ export function parseScopeList(rawScope) {
13
+ if (!rawScope)
14
+ return [];
15
+ const rules = rawScope
16
+ .split(',')
17
+ .map((rule) => rule.trim())
18
+ .filter(Boolean);
19
+ return normalizeScopeRules(rules);
20
+ }
13
21
  export function normalizeRepoAlias(repoAlias) {
14
22
  if (!repoAlias)
15
23
  return undefined;
@@ -21,95 +29,92 @@ export function normalizeRepoAlias(repoAlias) {
21
29
  }
22
30
  return normalized;
23
31
  }
24
- export async function resolveAnalyzeScopeRules(options) {
25
- let manifestRules = [];
26
- if (options?.scopeManifest) {
27
- const manifestPath = path.resolve(options.scopeManifest);
28
- const manifest = await readScopeManifestConfig(manifestPath);
29
- manifestRules = manifest.scopeRules;
30
- if (manifestRules.length === 0) {
31
- throw new Error(`Scope manifest has no valid scope rules: ${manifestPath}`);
32
+ /**
33
+ * Validate stored options from meta.json.analyzeOptions before reusing them.
34
+ * Invalid fields are filtered out or set to undefined, with console.warn output.
35
+ */
36
+ export async function validateStoredOptions(stored, repoPath) {
37
+ if (!stored) {
38
+ return { includeExtensions: [], scopeRules: [], repoAlias: undefined, embeddings: false, csharpDefineCsproj: undefined };
39
+ }
40
+ // Validate repoAlias
41
+ let repoAlias;
42
+ if (stored.repoAlias) {
43
+ const trimmed = stored.repoAlias.trim();
44
+ if (trimmed && REPO_ALIAS_REGEX.test(trimmed)) {
45
+ repoAlias = trimmed;
46
+ }
47
+ else {
48
+ console.warn(` Warning: stored repoAlias "${stored.repoAlias}" is invalid, resetting to undefined.`);
32
49
  }
33
50
  }
34
- return resolveScopeRulesFromInput(manifestRules, normalizeScopePrefixes(options?.scopePrefix), Boolean(options?.scopeManifest));
35
- }
36
- function parseScopePrefixCount(scopePrefix) {
37
- if (Array.isArray(scopePrefix))
38
- return scopePrefix.length;
39
- if (typeof scopePrefix === 'string')
40
- return scopePrefix.trim() ? 1 : 0;
41
- return 0;
51
+ // Validate includeExtensions (must start with '.')
52
+ let includeExtensions = [];
53
+ if (stored.includeExtensions) {
54
+ const valid = [];
55
+ for (const ext of stored.includeExtensions) {
56
+ if (typeof ext === 'string' && ext.startsWith('.')) {
57
+ valid.push(ext);
58
+ }
59
+ else {
60
+ console.warn(` Warning: stored includeExtensions entry "${ext}" does not start with '.', filtering out.`);
61
+ }
62
+ }
63
+ includeExtensions = valid;
64
+ }
65
+ // Validate scopeRules (filter empty/whitespace-only)
66
+ let scopeRules = [];
67
+ if (stored.scopeRules) {
68
+ scopeRules = stored.scopeRules.filter((rule) => typeof rule === 'string' && rule.trim().length > 0);
69
+ }
70
+ // Validate csharpDefineCsproj (file must exist)
71
+ let csharpDefineCsproj;
72
+ if (stored.csharpDefineCsproj) {
73
+ try {
74
+ await fs.stat(stored.csharpDefineCsproj);
75
+ csharpDefineCsproj = stored.csharpDefineCsproj;
76
+ }
77
+ catch {
78
+ console.warn(` Warning: stored csharpDefineCsproj "${stored.csharpDefineCsproj}" not found on disk, resetting to undefined.`);
79
+ }
80
+ }
81
+ return {
82
+ includeExtensions,
83
+ scopeRules,
84
+ repoAlias,
85
+ embeddings: Boolean(stored.embeddings),
86
+ csharpDefineCsproj,
87
+ };
42
88
  }
43
89
  export async function resolveEffectiveAnalyzeOptions(options, stored) {
44
- const manifestConfig = options?.scopeManifest
45
- ? await readScopeManifestConfig(path.resolve(options.scopeManifest))
46
- : undefined;
47
90
  const includeExtensionsFromCli = parseExtensionList(options?.extensions);
48
- const scopeRulesFromCli = resolveScopeRulesFromInput(manifestConfig?.scopeRules || [], normalizeScopePrefixes(options?.scopePrefix), Boolean(options?.scopeManifest));
91
+ const scopeRulesFromCli = parseScopeList(options?.scope);
49
92
  const repoAliasFromCli = normalizeRepoAlias(options?.repoAlias);
50
- const manifestExtensions = manifestConfig?.directives.extensions;
51
- const manifestRepoAlias = manifestConfig?.directives.repoAlias;
52
- const manifestEmbeddings = manifestConfig?.directives.embeddings;
53
93
  const hasCliExtensions = options?.extensions !== undefined;
54
- const hasCliScope = Boolean(options?.scopeManifest) || parseScopePrefixCount(options?.scopePrefix) > 0;
94
+ const hasCliScope = options?.scope !== undefined;
55
95
  const hasCliRepoAlias = options?.repoAlias !== undefined;
96
+ const hasCliCsproj = options?.csharpDefineCsproj !== undefined;
56
97
  const canReuse = options?.reuseOptions !== false;
57
98
  const includeExtensions = hasCliExtensions
58
99
  ? includeExtensionsFromCli
59
- : (manifestExtensions !== undefined
60
- ? parseExtensionList(manifestExtensions)
61
- : (canReuse ? (stored?.includeExtensions || []) : []));
100
+ : (canReuse ? (stored?.includeExtensions || []) : []);
62
101
  const scopeRules = hasCliScope
63
102
  ? scopeRulesFromCli
64
103
  : (canReuse ? (stored?.scopeRules || []) : []);
65
104
  const repoAlias = hasCliRepoAlias
66
105
  ? repoAliasFromCli
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));
106
+ : (canReuse ? normalizeRepoAlias(stored?.repoAlias) : undefined);
107
+ const embeddings = options?.embeddings !== undefined
108
+ ? options.embeddings
109
+ : (canReuse ? Boolean(stored?.embeddings) : false);
110
+ const csharpDefineCsproj = hasCliCsproj
111
+ ? options.csharpDefineCsproj
112
+ : (canReuse ? stored?.csharpDefineCsproj : undefined);
74
113
  return {
75
114
  includeExtensions: [...includeExtensions],
76
115
  scopeRules: [...scopeRules],
77
116
  repoAlias,
78
117
  embeddings,
118
+ csharpDefineCsproj,
79
119
  };
80
120
  }
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
- }
@@ -3,8 +3,7 @@ import assert from 'node:assert/strict';
3
3
  import fs from 'node:fs/promises';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
- import { normalizeRepoAlias, parseExtensionList, resolveAnalyzeScopeRules, resolveEffectiveAnalyzeOptions, } from './analyze-options.js';
7
- import { parseScopeManifestConfig } from './scope-manifest-config.js';
6
+ import { normalizeRepoAlias, parseExtensionList, resolveEffectiveAnalyzeOptions, validateStoredOptions, } from './analyze-options.js';
8
7
  test('parseExtensionList normalizes dot prefixes', () => {
9
8
  const exts = parseExtensionList('cs,.ts, go ');
10
9
  assert.deepEqual(exts, ['.cs', '.ts', '.go']);
@@ -15,26 +14,6 @@ test('normalizeRepoAlias validates format', () => {
15
14
  assert.throws(() => normalizeRepoAlias('ab'), /repo alias/i);
16
15
  assert.throws(() => normalizeRepoAlias('bad alias'), /repo alias/i);
17
16
  });
18
- test('resolveAnalyzeScopeRules combines manifest and repeated prefixes', async () => {
19
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-scope-test-'));
20
- const manifestPath = path.join(tmpDir, 'scope.txt');
21
- await fs.writeFile(manifestPath, '# comment\nAssets/NEON/Code\n\nPackages/com.veewo.*\n', 'utf-8');
22
- const rules = await resolveAnalyzeScopeRules({
23
- scopeManifest: manifestPath,
24
- scopePrefix: ['Packages/com.neonspark.*'],
25
- });
26
- assert.deepEqual(rules, [
27
- 'Assets/NEON/Code',
28
- 'Packages/com.veewo.*',
29
- 'Packages/com.neonspark.*',
30
- ]);
31
- });
32
- test('resolveAnalyzeScopeRules fails when manifest has no usable rule', async () => {
33
- const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-scope-test-'));
34
- const manifestPath = path.join(tmpDir, 'empty-scope.txt');
35
- await fs.writeFile(manifestPath, '# only comments\n\n', 'utf-8');
36
- await assert.rejects(resolveAnalyzeScopeRules({ scopeManifest: manifestPath }), /no valid scope rules/i);
37
- });
38
17
  test('resolveEffectiveAnalyzeOptions reuses stored settings when CLI omits them', async () => {
39
18
  const resolved = await resolveEffectiveAnalyzeOptions({}, {
40
19
  includeExtensions: ['.cs'],
@@ -53,16 +32,18 @@ test('resolveEffectiveAnalyzeOptions disables reuse via reuseOptions=false', asy
53
32
  scopeRules: ['Assets/NEON/Code'],
54
33
  repoAlias: 'neonspark-v1-subset',
55
34
  embeddings: true,
35
+ csharpDefineCsproj: '/tmp/Assembly-CSharp.csproj',
56
36
  });
57
37
  assert.deepEqual(resolved.includeExtensions, []);
58
38
  assert.deepEqual(resolved.scopeRules, []);
59
39
  assert.equal(resolved.repoAlias, undefined);
60
40
  assert.equal(resolved.embeddings, false);
41
+ assert.equal(resolved.csharpDefineCsproj, undefined);
61
42
  });
62
43
  test('resolveEffectiveAnalyzeOptions prefers explicit CLI values over stored settings', async () => {
63
44
  const resolved = await resolveEffectiveAnalyzeOptions({
64
45
  extensions: '.ts',
65
- scopePrefix: ['src'],
46
+ scope: 'src/',
66
47
  repoAlias: 'new-alias',
67
48
  embeddings: false,
68
49
  }, {
@@ -76,59 +57,83 @@ test('resolveEffectiveAnalyzeOptions prefers explicit CLI values over stored set
76
57
  assert.equal(resolved.repoAlias, 'new-alias');
77
58
  assert.equal(resolved.embeddings, false);
78
59
  });
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');
60
+ test('resolveEffectiveAnalyzeOptions no stored uses defaults', async () => {
61
+ const resolved = await resolveEffectiveAnalyzeOptions({}, undefined);
62
+ assert.deepEqual(resolved.includeExtensions, []);
63
+ assert.deepEqual(resolved.scopeRules, []);
64
+ assert.equal(resolved.repoAlias, undefined);
92
65
  assert.equal(resolved.embeddings, false);
66
+ assert.equal(resolved.csharpDefineCsproj, undefined);
93
67
  });
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,
68
+ test('resolveEffectiveAnalyzeOptions reuses stored scopeRules when CLI scope is omitted', async () => {
69
+ const resolved = await resolveEffectiveAnalyzeOptions({ extensions: '.ts' }, {
70
+ scopeRules: ['Assets/', 'Packages/com.veewo.*'],
106
71
  });
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);
72
+ assert.deepEqual(resolved.scopeRules, ['Assets/', 'Packages/com.veewo.*']);
73
+ });
74
+ test('resolveEffectiveAnalyzeOptions parses --scope comma-separated rules', async () => {
75
+ const resolved = await resolveEffectiveAnalyzeOptions({ scope: 'Assets/,Packages/com.veewo.*' }, undefined);
76
+ assert.deepEqual(resolved.scopeRules, ['Assets/', 'Packages/com.veewo.*']);
77
+ });
78
+ test('resolveEffectiveAnalyzeOptions reuses stored csharpDefineCsproj', async () => {
79
+ const resolved = await resolveEffectiveAnalyzeOptions({}, {
80
+ csharpDefineCsproj: '/path/to/Assembly-CSharp.csproj',
81
+ });
82
+ assert.equal(resolved.csharpDefineCsproj, '/path/to/Assembly-CSharp.csproj');
111
83
  });
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',
84
+ test('resolveEffectiveAnalyzeOptions CLI csharpDefineCsproj overrides stored', async () => {
85
+ const resolved = await resolveEffectiveAnalyzeOptions({ csharpDefineCsproj: '/new/path.csproj' }, {
86
+ csharpDefineCsproj: '/old/path.csproj',
133
87
  });
88
+ assert.equal(resolved.csharpDefineCsproj, '/new/path.csproj');
89
+ });
90
+ // ─── validateStoredOptions tests ────────────────────────────────────
91
+ test('validateStoredOptions returns defaults when stored is undefined', async () => {
92
+ const result = await validateStoredOptions(undefined, '/tmp');
93
+ assert.deepEqual(result.includeExtensions, []);
94
+ assert.deepEqual(result.scopeRules, []);
95
+ assert.equal(result.repoAlias, undefined);
96
+ assert.equal(result.embeddings, false);
97
+ assert.equal(result.csharpDefineCsproj, undefined);
98
+ });
99
+ test('validateStoredOptions passes valid options unchanged', async () => {
100
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-validate-'));
101
+ const csprojPath = path.join(tmpDir, 'Assembly-CSharp.csproj');
102
+ await fs.writeFile(csprojPath, '<Project />', 'utf-8');
103
+ const result = await validateStoredOptions({
104
+ includeExtensions: ['.cs', '.ts'],
105
+ scopeRules: ['Assets/'],
106
+ repoAlias: 'my-repo',
107
+ csharpDefineCsproj: csprojPath,
108
+ embeddings: true,
109
+ }, tmpDir);
110
+ assert.deepEqual(result.includeExtensions, ['.cs', '.ts']);
111
+ assert.deepEqual(result.scopeRules, ['Assets/']);
112
+ assert.equal(result.repoAlias, 'my-repo');
113
+ assert.equal(result.csharpDefineCsproj, csprojPath);
114
+ assert.equal(result.embeddings, true);
115
+ });
116
+ test('validateStoredOptions warns on invalid repoAlias and falls back', async () => {
117
+ const result = await validateStoredOptions({
118
+ repoAlias: 'bad alias!',
119
+ }, '/tmp');
120
+ assert.equal(result.repoAlias, undefined);
121
+ });
122
+ test('validateStoredOptions filters invalid extensions', async () => {
123
+ const result = await validateStoredOptions({
124
+ includeExtensions: ['cs', '.ts', '', '.go'],
125
+ }, '/tmp');
126
+ assert.deepEqual(result.includeExtensions, ['.ts', '.go']);
127
+ });
128
+ test('validateStoredOptions filters empty scopeRules', async () => {
129
+ const result = await validateStoredOptions({
130
+ scopeRules: ['', ' ', 'Assets/', 'Packages/com.veewo.*'],
131
+ }, '/tmp');
132
+ assert.deepEqual(result.scopeRules, ['Assets/', 'Packages/com.veewo.*']);
133
+ });
134
+ test('validateStoredOptions warns on missing csharpDefineCsproj file', async () => {
135
+ const result = await validateStoredOptions({
136
+ csharpDefineCsproj: '/nonexistent/path.csproj',
137
+ }, '/tmp');
138
+ assert.equal(result.csharpDefineCsproj, undefined);
134
139
  });
@@ -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
  });