@veewo/gitnexus 1.5.5 → 1.5.7

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 (41) 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/analyze-options.d.ts +19 -6
  9. package/dist/cli/analyze-options.js +76 -71
  10. package/dist/cli/analyze-options.test.js +78 -73
  11. package/dist/cli/analyze.d.ts +2 -4
  12. package/dist/cli/analyze.js +13 -25
  13. package/dist/cli/analyze.test.js +9 -15
  14. package/dist/cli/benchmark-agent-context.d.ts +0 -2
  15. package/dist/cli/benchmark-agent-context.js +0 -2
  16. package/dist/cli/benchmark-agent-safe-query-context.d.ts +0 -2
  17. package/dist/cli/benchmark-agent-safe-query-context.js +0 -2
  18. package/dist/cli/benchmark-unity.d.ts +0 -2
  19. package/dist/cli/benchmark-unity.js +0 -2
  20. package/dist/cli/clean.d.ts +2 -3
  21. package/dist/cli/clean.js +4 -25
  22. package/dist/cli/index.js +1 -10
  23. package/dist/core/ingestion/pipeline.js +1 -1
  24. package/dist/storage/repo-manager.d.ts +1 -0
  25. package/package.json +2 -2
  26. package/skills/gitnexus-cli.md +62 -38
  27. package/vendor/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
  28. package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +122 -0
  29. package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +126 -0
  30. package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +122 -0
  31. package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +122 -0
  32. package/vendor/tree-sitter-dart/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
  33. package/vendor/tree-sitter-dart/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
  34. package/vendor/tree-sitter-proto/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
  35. package/vendor/tree-sitter-proto/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
  36. package/dist/cli/scope-manifest-config.d.ts +0 -9
  37. package/dist/cli/scope-manifest-config.js +0 -37
  38. package/dist/cli/sync-manifest.d.ts +0 -27
  39. package/dist/cli/sync-manifest.js +0 -200
  40. package/dist/cli/sync-manifest.test.d.ts +0 -1
  41. package/dist/cli/sync-manifest.test.js +0 -88
@@ -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,
@@ -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
  });
@@ -3,16 +3,13 @@
3
3
  *
4
4
  * Indexes a repository and stores the knowledge graph in .gitnexus/
5
5
  */
6
- import { type SyncManifestPolicy } from './sync-manifest.js';
7
6
  export interface AnalyzeOptions {
8
7
  force?: boolean;
9
8
  embeddings?: boolean;
10
9
  extensions?: string;
10
+ scope?: string;
11
11
  repoAlias?: string;
12
12
  csharpDefineCsproj?: string;
13
- scopeManifest?: string;
14
- scopePrefix?: string[];
15
- syncManifestPolicy?: SyncManifestPolicy;
16
13
  reuseOptions?: boolean;
17
14
  skills?: boolean;
18
15
  verbose?: boolean;
@@ -21,6 +18,7 @@ export declare const analyzeCommand: (inputPath?: string, options?: AnalyzeOptio
21
18
  export declare function buildPipelineRunOptionsForAnalyze(resolvedOptions: {
22
19
  includeExtensions: string[];
23
20
  scopeRules: string[];
21
+ csharpDefineCsproj?: string;
24
22
  }, options?: AnalyzeOptions): {
25
23
  includeExtensions: string[];
26
24
  scopeRules: string[];