@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
@@ -1,6 +1,3 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { loadCompiledRuleBundle } from '../../rule-lab/compiled-bundles.js';
4
1
  export class RuleRegistryLoadError extends Error {
5
2
  code;
6
3
  details;
@@ -139,59 +136,6 @@ export function parseRuleYaml(raw, filePath) {
139
136
  const legacyGuarantees = readList(raw, 'guarantees');
140
137
  const legacyNonGuarantees = readList(raw, 'non_guarantees');
141
138
  const legacyNextAction = readScalar(raw, 'next_action');
142
- // Parse resource_bindings
143
- const rbLines = readSectionLines(raw, 'resource_bindings');
144
- let resource_bindings;
145
- if (rbLines.length > 0) {
146
- resource_bindings = [];
147
- const joined = rbLines.map((l) => l.replace(/^\s{2}/, '')).join('\n');
148
- const entries = joined.split(/(?=^\s*- kind:)/m).filter((s) => s.trim());
149
- for (const entry of entries) {
150
- const kindMatch = entry.match(/- kind:\s*(.+)/);
151
- if (!kindMatch)
152
- continue;
153
- const binding = { kind: decodeYamlScalar(kindMatch[1]) };
154
- const scalar = (k) => {
155
- const m = entry.match(new RegExp(`^\\s+${k}:\\s*(.+)$`, 'm'));
156
- return m ? decodeYamlScalar(m[1]) : undefined;
157
- };
158
- const list = (k) => {
159
- const lines = entry.split('\n');
160
- const idx = lines.findIndex((l) => new RegExp(`^\\s+${k}:\\s*$`).test(l));
161
- if (idx < 0)
162
- return undefined;
163
- const out = [];
164
- for (let i = idx + 1; i < lines.length; i++) {
165
- if (!/^\s+-\s+/.test(lines[i]))
166
- break;
167
- out.push(decodeYamlScalar(lines[i].replace(/^\s+-\s+/, '')));
168
- }
169
- return out.length > 0 ? out : undefined;
170
- };
171
- binding.ref_field_pattern = scalar('ref_field_pattern');
172
- binding.target_entry_points = list('target_entry_points');
173
- binding.host_class_pattern = scalar('host_class_pattern');
174
- binding.field_name = scalar('field_name');
175
- binding.loader_methods = list('loader_methods');
176
- binding.scene_name = scalar('scene_name');
177
- binding.source_class_pattern = scalar('source_class_pattern');
178
- binding.source_method = scalar('source_method');
179
- binding.target_class_pattern = scalar('target_class_pattern');
180
- binding.target_method = scalar('target_method');
181
- resource_bindings.push(binding);
182
- }
183
- if (resource_bindings.length === 0)
184
- resource_bindings = undefined;
185
- }
186
- // Parse lifecycle_overrides
187
- const loEntryPoints = readNestedList(raw, 'lifecycle_overrides', 'additional_entry_points');
188
- const loScope = readNestedScalar(raw, 'lifecycle_overrides', 'scope');
189
- const lifecycle_overrides = loEntryPoints.length > 0 || loScope
190
- ? {
191
- ...(loEntryPoints.length > 0 ? { additional_entry_points: loEntryPoints } : {}),
192
- ...(loScope ? { scope: loScope } : {}),
193
- }
194
- : undefined;
195
139
  return {
196
140
  id,
197
141
  version,
@@ -209,109 +153,6 @@ export function parseRuleYaml(raw, filePath) {
209
153
  guarantees: claimGuarantees.length > 0 ? claimGuarantees : legacyGuarantees,
210
154
  non_guarantees: claimNonGuarantees.length > 0 ? claimNonGuarantees : legacyNonGuarantees,
211
155
  next_action: claimNextAction || legacyNextAction,
212
- family: readScalar(raw, 'family') || 'verification_rules',
213
- resource_bindings,
214
- lifecycle_overrides,
215
156
  file_path: filePath,
216
157
  };
217
158
  }
218
- /**
219
- * Runtime claim rule registry remains the source for analyze-time synthetic-edge production
220
- * and offline governance/report workflows. Query-time runtime closure verification is graph-only.
221
- */
222
- export async function loadRuleRegistry(repoPath, rulesRoot) {
223
- const normalizedRepoPath = path.resolve(repoPath);
224
- const root = rulesRoot
225
- ? path.resolve(rulesRoot)
226
- : path.join(normalizedRepoPath, '.gitnexus', 'rules');
227
- const compiledVerificationBundle = await loadCompiledRuleBundle(normalizedRepoPath, 'verification_rules', root);
228
- if (compiledVerificationBundle && compiledVerificationBundle.rules.length > 0) {
229
- return {
230
- repoPath: normalizedRepoPath,
231
- rulesRoot: root,
232
- catalogPath: path.join(root, 'compiled', 'verification_rules.v2.json'),
233
- activeRules: compiledVerificationBundle.rules.map((rule) => ({
234
- id: rule.id,
235
- version: rule.version,
236
- trigger_family: rule.trigger_family,
237
- resource_types: rule.resource_types,
238
- host_base_type: rule.host_base_type,
239
- match: rule.match,
240
- required_hops: rule.required_hops,
241
- guarantees: rule.guarantees,
242
- non_guarantees: rule.non_guarantees,
243
- next_action: rule.next_action,
244
- file_path: rule.file_path,
245
- topology: rule.topology,
246
- closure: rule.closure,
247
- claims: rule.claims,
248
- })),
249
- };
250
- }
251
- const catalogPath = path.join(root, 'catalog.json');
252
- let catalogRaw;
253
- try {
254
- catalogRaw = await fs.readFile(catalogPath, 'utf-8');
255
- }
256
- catch (error) {
257
- if (error?.code === 'ENOENT') {
258
- throw new RuleRegistryLoadError('rule_catalog_missing', `Runtime claim rule catalog not found: ${catalogPath}`, { repoPath: normalizedRepoPath, rulesRoot: root, catalogPath });
259
- }
260
- throw error;
261
- }
262
- let catalog;
263
- try {
264
- catalog = JSON.parse(catalogRaw);
265
- }
266
- catch {
267
- throw new RuleRegistryLoadError('rule_catalog_invalid', `Runtime claim rule catalog is invalid JSON: ${catalogPath}`, { repoPath: normalizedRepoPath, rulesRoot: root, catalogPath });
268
- }
269
- const catalogRules = Array.isArray(catalog.rules) ? catalog.rules : [];
270
- const activeRules = [];
271
- for (const entry of catalogRules) {
272
- if (entry.enabled === false)
273
- continue;
274
- const relativeRulePath = String(entry.file || path.join('approved', `${entry.id}.yaml`));
275
- const rulePath = path.join(root, relativeRulePath);
276
- let raw;
277
- try {
278
- raw = await fs.readFile(rulePath, 'utf-8');
279
- }
280
- catch (error) {
281
- if (error?.code === 'ENOENT') {
282
- throw new RuleRegistryLoadError('rule_file_missing', `Runtime claim rule file not found: ${rulePath}`, { repoPath: normalizedRepoPath, rulesRoot: root, catalogPath, rulePath, ruleId: entry.id });
283
- }
284
- throw error;
285
- }
286
- const parsed = parseRuleYaml(raw, rulePath);
287
- if (parsed.id !== entry.id) {
288
- throw new Error(`Rule id mismatch between catalog and yaml: ${entry.id} vs ${parsed.id}`);
289
- }
290
- activeRules.push({
291
- ...parsed,
292
- version: entry.version || parsed.version,
293
- family: entry.family || parsed.family || 'verification_rules',
294
- });
295
- }
296
- return {
297
- repoPath: normalizedRepoPath,
298
- rulesRoot: root,
299
- catalogPath,
300
- activeRules,
301
- };
302
- }
303
- export async function loadAnalyzeRules(repoPath, rulesRoot) {
304
- const normalizedRepoPath = path.resolve(repoPath);
305
- const root = rulesRoot
306
- ? path.resolve(rulesRoot)
307
- : path.join(normalizedRepoPath, '.gitnexus', 'rules');
308
- const analyzeBundle = await loadCompiledRuleBundle(normalizedRepoPath, 'analyze_rules', root);
309
- if (analyzeBundle && analyzeBundle.rules.length > 0) {
310
- return analyzeBundle.rules.map((rule) => ({
311
- ...rule,
312
- family: 'analyze_rules',
313
- }));
314
- }
315
- const registry = await loadRuleRegistry(repoPath, rulesRoot);
316
- return registry.activeRules.filter((r) => r.family === 'analyze_rules');
317
- }
@@ -1,24 +1,14 @@
1
1
  import assert from 'node:assert/strict';
2
- import fs from 'node:fs/promises';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
2
  import { test } from 'vitest';
6
- import { RuleRegistryLoadError, loadRuleRegistry } from './runtime-claim-rule-registry.js';
7
- test('loads active runtime claim rules from project catalog', async () => {
8
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-runtime-claim-rules-'));
9
- const repoPath = path.join(tempRoot, 'repo');
10
- const rulesRoot = path.join(repoPath, '.gitnexus', 'rules');
11
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
12
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({
13
- rules: [
14
- {
15
- id: 'demo.reload.rule.v1',
16
- version: '1.2.3',
17
- file: 'approved/demo.reload.rule.v1.yaml',
18
- },
19
- ],
20
- }), 'utf-8');
21
- await fs.writeFile(path.join(rulesRoot, 'approved', 'demo.reload.rule.v1.yaml'), [
3
+ import { RuleRegistryLoadError, parseRuleYaml } from './runtime-claim-rule-registry.js';
4
+ test('RuleRegistryLoadError has code and context', () => {
5
+ const err = new RuleRegistryLoadError('rule_catalog_missing', 'Catalog not found', { repoPath: '/tmp/repo' });
6
+ assert.ok(err instanceof RuleRegistryLoadError);
7
+ assert.equal(err.code, 'rule_catalog_missing');
8
+ assert.match(String(err.message || ''), /Catalog not found/);
9
+ });
10
+ test('parseRuleYaml parses basic fields', () => {
11
+ const yaml = [
22
12
  'id: demo.reload.rule.v1',
23
13
  'version: 1.2.3',
24
14
  'trigger_family: reload',
@@ -33,106 +23,37 @@ test('loads active runtime claim rules from project catalog', async () => {
33
23
  'non_guarantees:',
34
24
  ' - no_runtime_execution',
35
25
  'next_action: gitnexus query "reload"',
36
- ].join('\n'), 'utf-8');
37
- try {
38
- const registry = await loadRuleRegistry(repoPath);
39
- assert.equal(registry.activeRules[0].id, 'demo.reload.rule.v1');
40
- assert.equal(registry.activeRules[0].version, '1.2.3');
41
- }
42
- finally {
43
- await fs.rm(tempRoot, { recursive: true, force: true });
44
- }
45
- });
46
- test('throws rule_catalog_missing when target repo has no catalog (no ancestor fallback)', async () => {
47
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-runtime-claim-rules-'));
48
- const workspaceRoot = path.join(tempRoot, 'workspace');
49
- const nestedCwd = path.join(workspaceRoot, 'packages', 'app');
50
- const rulesRoot = path.join(workspaceRoot, '.gitnexus', 'rules');
51
- const catalogPath = path.join(rulesRoot, 'catalog.json');
52
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
53
- await fs.mkdir(nestedCwd, { recursive: true });
54
- await fs.writeFile(catalogPath, JSON.stringify({
55
- rules: [
56
- {
57
- id: 'demo.reload.rule.v1',
58
- version: '1.2.3',
59
- file: 'approved/demo.reload.rule.v1.yaml',
60
- },
61
- ],
62
- }), 'utf-8');
63
- await fs.writeFile(path.join(rulesRoot, 'approved', 'demo.reload.rule.v1.yaml'), ['id: demo.reload.rule.v1', 'version: 1.2.3', 'trigger_family: reload'].join('\n'), 'utf-8');
64
- const originalCwd = process.cwd();
65
- process.chdir(nestedCwd);
66
- try {
67
- await assert.rejects(() => loadRuleRegistry(path.join(tempRoot, 'does-not-exist')), (error) => {
68
- assert.ok(error instanceof RuleRegistryLoadError);
69
- assert.equal(error.code, 'rule_catalog_missing');
70
- assert.match(String(error.message || ''), /catalog not found/i);
71
- return true;
72
- });
73
- }
74
- finally {
75
- process.chdir(originalCwd);
76
- await fs.rm(tempRoot, { recursive: true, force: true });
77
- }
78
- });
79
- test('throws rule_catalog_missing when rulesRoot exists but catalog.json is missing', async () => {
80
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-runtime-claim-rules-'));
81
- const repoPath = path.join(tempRoot, 'repo');
82
- const rulesRoot = path.join(repoPath, '.gitnexus', 'rules');
83
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
84
- try {
85
- await assert.rejects(() => loadRuleRegistry(repoPath), (error) => {
86
- assert.ok(error instanceof RuleRegistryLoadError);
87
- assert.equal(error.code, 'rule_catalog_missing');
88
- return true;
89
- });
90
- }
91
- finally {
92
- await fs.rm(tempRoot, { recursive: true, force: true });
93
- }
94
- });
95
- test('throws rule_file_missing when catalog entry points to missing yaml file', async () => {
96
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-runtime-claim-rules-'));
97
- const repoPath = path.join(tempRoot, 'repo');
98
- const rulesRoot = path.join(repoPath, '.gitnexus', 'rules');
99
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
100
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({
101
- rules: [
102
- {
103
- id: 'demo.reload.rule.v1',
104
- version: '1.0.0',
105
- file: 'approved/demo.reload.rule.v1.yaml',
106
- },
107
- ],
108
- }), 'utf-8');
109
- try {
110
- await assert.rejects(() => loadRuleRegistry(repoPath), (error) => {
111
- assert.ok(error instanceof RuleRegistryLoadError);
112
- assert.equal(error.code, 'rule_file_missing');
113
- assert.match(String(error.message || ''), /rule file not found/i);
114
- return true;
115
- });
116
- }
117
- finally {
118
- await fs.rm(tempRoot, { recursive: true, force: true });
119
- }
26
+ 'match:',
27
+ ' trigger_tokens:',
28
+ ' - reload',
29
+ 'closure:',
30
+ ' required_hops:',
31
+ ' - resource',
32
+ 'claims:',
33
+ ' guarantees:',
34
+ ' - reload_chain_closed',
35
+ ' non_guarantees:',
36
+ ' - no_runtime_execution',
37
+ ' next_action: gitnexus query "reload"',
38
+ 'topology:',
39
+ ' - hop: resource',
40
+ ' from:',
41
+ ' entity: resource',
42
+ ' to:',
43
+ ' entity: script',
44
+ ' edge:',
45
+ ' kind: binds_script',
46
+ ].join('\n');
47
+ const rule = parseRuleYaml(yaml, 'test.yaml');
48
+ assert.equal(rule.id, 'demo.reload.rule.v1');
49
+ assert.equal(rule.version, '1.2.3');
50
+ assert.equal(rule.trigger_family, 'reload');
51
+ assert.deepEqual(rule.resource_types, ['asset']);
52
+ assert.deepEqual(rule.host_base_type, ['ReloadBase']);
53
+ assert.equal(rule.file_path, 'test.yaml');
120
54
  });
121
- test('parses scalar/list values with spaces, quotes, and escapes without truncation', async () => {
122
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-runtime-claim-rules-'));
123
- const repoPath = path.join(tempRoot, 'repo');
124
- const rulesRoot = path.join(repoPath, '.gitnexus', 'rules');
125
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
126
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({
127
- rules: [
128
- {
129
- id: 'demo.scalar-parser.v1',
130
- version: '1.0.0',
131
- file: 'approved/demo.scalar-parser.v1.yaml',
132
- },
133
- ],
134
- }), 'utf-8');
135
- await fs.writeFile(path.join(rulesRoot, 'approved', 'demo.scalar-parser.v1.yaml'), [
55
+ test('parseRuleYaml parses scalar/list values with spaces, quotes, and escapes', () => {
56
+ const yaml = [
136
57
  'id: demo.scalar-parser.v1',
137
58
  'version: 1.0.0',
138
59
  'trigger_family: reload',
@@ -149,100 +70,32 @@ test('parses scalar/list values with spaces, quotes, and escapes without truncat
149
70
  ' - "double-quote \\"inside\\""',
150
71
  " - 'single-quote ''inside'''",
151
72
  'next_action: node gitnexus/dist/cli/index.js query --runtime-chain-verify on-demand "Reload NEON.Game.Graph.Nodes.Reloads"',
152
- ].join('\n'), 'utf-8');
153
- try {
154
- const registry = await loadRuleRegistry(repoPath);
155
- const rule = registry.activeRules[0];
156
- assert.equal(rule.id, 'demo.scalar-parser.v1');
157
- assert.deepEqual(rule.resource_types, ['asset ref', 'prefab ref']);
158
- assert.deepEqual(rule.guarantees, ['guarantee with spaces']);
159
- assert.deepEqual(rule.non_guarantees, ['double-quote "inside"', "single-quote 'inside'"]);
160
- assert.equal(rule.next_action, 'node gitnexus/dist/cli/index.js query --runtime-chain-verify on-demand "Reload NEON.Game.Graph.Nodes.Reloads"');
161
- }
162
- finally {
163
- await fs.rm(tempRoot, { recursive: true, force: true });
164
- }
165
- });
166
- test('rejects rule yaml when topology/closure/claims are missing', async () => {
167
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-runtime-claim-rules-'));
168
- const repoPath = path.join(tempRoot, 'repo');
169
- const rulesRoot = path.join(repoPath, '.gitnexus', 'rules');
170
- await fs.mkdir(path.join(rulesRoot, 'approved'), { recursive: true });
171
- await fs.writeFile(path.join(rulesRoot, 'catalog.json'), JSON.stringify({
172
- rules: [
173
- {
174
- id: 'demo.reload.rule.v2',
175
- version: '2.0.0',
176
- file: 'approved/demo.reload.rule.v2.yaml',
177
- },
178
- ],
179
- }), 'utf-8');
180
- await fs.writeFile(path.join(rulesRoot, 'approved', 'demo.reload.rule.v2.yaml'), [
181
- 'id: demo.reload.rule.v2',
182
- 'version: 2.0.0',
183
- 'trigger_family: reload',
184
- 'resource_types:',
185
- ' - asset',
186
- 'host_base_type:',
187
- ' - ReloadBase',
188
- 'required_hops:',
189
- ' - resource',
190
- 'guarantees:',
191
- ' - reload_chain_closed',
192
- 'non_guarantees:',
193
- ' - no_runtime_execution_guarantee',
194
- ].join('\n'), 'utf-8');
195
- try {
196
- await assert.rejects(() => loadRuleRegistry(repoPath), /topology|closure|claims/i);
197
- }
198
- finally {
199
- await fs.rm(tempRoot, { recursive: true, force: true });
200
- }
201
- });
202
- test('loads v2 verification bundle from explicit compiled path without catalog fallback', async () => {
203
- const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'gitnexus-runtime-claim-rules-'));
204
- const repoPath = path.join(tempRoot, 'repo');
205
- const compiledRoot = path.join(repoPath, '.gitnexus', 'rules', 'compiled');
206
- await fs.mkdir(compiledRoot, { recursive: true });
207
- await fs.writeFile(path.join(compiledRoot, 'verification_rules.v2.json'), JSON.stringify({
208
- bundle_version: '2.0.0',
209
- family: 'verification_rules',
210
- generated_at: new Date().toISOString(),
211
- rules: [
212
- {
213
- id: 'demo.bundle.rule.v2',
214
- version: '2.0.0',
215
- trigger_family: 'reload',
216
- resource_types: ['asset'],
217
- host_base_type: ['ReloadBase'],
218
- required_hops: ['resource', 'code_runtime'],
219
- guarantees: ['reload_chain_closed'],
220
- non_guarantees: ['no_runtime_execution'],
221
- next_action: 'gitnexus query "reload"',
222
- file_path: '.gitnexus/rules/compiled/verification_rules.v2.json',
223
- match: { trigger_tokens: ['reload'] },
224
- topology: [
225
- { hop: 'resource', from: { entity: 'resource' }, to: { entity: 'script' }, edge: { kind: 'binds_script' } },
226
- ],
227
- closure: {
228
- required_hops: ['resource', 'code_runtime'],
229
- failure_map: { missing_evidence: 'rule_matched_but_evidence_missing' },
230
- },
231
- claims: {
232
- guarantees: ['reload_chain_closed'],
233
- non_guarantees: ['no_runtime_execution'],
234
- next_action: 'gitnexus query "reload"',
235
- },
236
- },
237
- ],
238
- }, null, 2), 'utf-8');
239
- try {
240
- const registry = await loadRuleRegistry(repoPath);
241
- assert.equal(registry.activeRules[0].id, 'demo.bundle.rule.v2');
242
- assert.equal(registry.activeRules[0].version, '2.0.0');
243
- assert.deepEqual(registry.activeRules[0].required_hops, ['resource', 'code_runtime']);
244
- }
245
- finally {
246
- await fs.rm(tempRoot, { recursive: true, force: true });
247
- }
73
+ 'match:',
74
+ ' trigger_tokens:',
75
+ ' - reload',
76
+ 'closure:',
77
+ ' required_hops:',
78
+ ' - resource',
79
+ 'claims:',
80
+ ' guarantees:',
81
+ " - 'guarantee with spaces'",
82
+ ' non_guarantees:',
83
+ ' - "double-quote \\"inside\\""',
84
+ " - 'single-quote ''inside'''",
85
+ ' next_action: query "reload"',
86
+ 'topology:',
87
+ ' - hop: resource',
88
+ ' from:',
89
+ ' entity: resource',
90
+ ' to:',
91
+ ' entity: script',
92
+ ' edge:',
93
+ ' kind: binds_script',
94
+ ].join('\n');
95
+ const rule = parseRuleYaml(yaml, 'scalar.yaml');
96
+ assert.equal(rule.id, 'demo.scalar-parser.v1');
97
+ assert.deepEqual(rule.resource_types, ['asset ref', 'prefab ref']);
98
+ assert.deepEqual(rule.guarantees, ['guarantee with spaces']);
99
+ assert.deepEqual(rule.non_guarantees, ['double-quote "inside"', "single-quote 'inside'"]);
100
+ assert.equal(rule.next_action, 'query "reload"');
248
101
  });
package/dist/mcp/tools.js CHANGED
@@ -383,76 +383,6 @@ Output enforces unique-result policy and includes path+line evidence hops.`,
383
383
  required: ['target', 'goal'],
384
384
  },
385
385
  },
386
- {
387
- name: 'rule_lab_analyze',
388
- description: `Analyze one Rule Lab slice and emit anchor-backed candidates.jsonl.`,
389
- inputSchema: {
390
- type: 'object',
391
- properties: {
392
- run_id: { type: 'string', description: 'Rule Lab run id' },
393
- slice_id: { type: 'string', description: 'Rule Lab slice id' },
394
- repo: { type: 'string', description: 'Repository name or path. Omit if only one repo is indexed.' },
395
- },
396
- required: ['run_id', 'slice_id'],
397
- },
398
- },
399
- {
400
- name: 'rule_lab_review_pack',
401
- description: `Pack analyzed candidates into review cards with token budget enforcement.`,
402
- inputSchema: {
403
- type: 'object',
404
- properties: {
405
- run_id: { type: 'string', description: 'Rule Lab run id' },
406
- slice_id: { type: 'string', description: 'Rule Lab slice id' },
407
- max_tokens: { type: 'number', description: 'Token budget cap (default: 6000)', default: 6000 },
408
- repo: { type: 'string', description: 'Repository name or path. Omit if only one repo is indexed.' },
409
- },
410
- required: ['run_id', 'slice_id'],
411
- },
412
- },
413
- {
414
- name: 'rule_lab_curate',
415
- description: `Validate human-curated semantic closure input and persist curated artifacts for promotion.`,
416
- inputSchema: {
417
- type: 'object',
418
- properties: {
419
- run_id: { type: 'string', description: 'Rule Lab run id' },
420
- slice_id: { type: 'string', description: 'Rule Lab slice id' },
421
- input_path: { type: 'string', description: 'Absolute or repo-relative path to curation input JSON' },
422
- repo: { type: 'string', description: 'Repository name or path. Omit if only one repo is indexed.' },
423
- },
424
- required: ['run_id', 'slice_id', 'input_path'],
425
- },
426
- },
427
- {
428
- name: 'rule_lab_promote',
429
- description: `Promote curated candidates into approved YAML rules and upsert catalog.json entries.`,
430
- inputSchema: {
431
- type: 'object',
432
- properties: {
433
- run_id: { type: 'string', description: 'Rule Lab run id' },
434
- slice_id: { type: 'string', description: 'Rule Lab slice id' },
435
- version: { type: 'string', description: 'Promoted rule version (default: 1.0.0)', default: '1.0.0' },
436
- repo: { type: 'string', description: 'Repository name or path. Omit if only one repo is indexed.' },
437
- },
438
- required: ['run_id', 'slice_id'],
439
- },
440
- },
441
- {
442
- name: 'rule_lab_regress',
443
- description: `Evaluate Rule Lab precision/coverage gates and optionally persist a regression report.`,
444
- inputSchema: {
445
- type: 'object',
446
- properties: {
447
- precision: { type: 'number', description: 'Observed precision metric' },
448
- coverage: { type: 'number', description: 'Observed coverage metric' },
449
- probes_path: { type: 'string', description: 'Optional path to a JSON array of regression probes with bucket metadata' },
450
- run_id: { type: 'string', description: 'Optional run id for report naming' },
451
- repo: { type: 'string', description: 'Repository name or path. Omit if only one repo is indexed.' },
452
- },
453
- required: ['precision', 'coverage'],
454
- },
455
- },
456
386
  {
457
387
  name: 'impact',
458
388
  description: `Analyze the blast radius of changing a code symbol.
@@ -16,6 +16,7 @@ export interface RepoMeta {
16
16
  repoAlias?: string;
17
17
  embeddings?: boolean;
18
18
  csharpDefineCsproj?: string;
19
+ aiContext?: boolean;
19
20
  };
20
21
  stats?: {
21
22
  files?: number;
@@ -2,7 +2,6 @@ import { GraphNode, GraphRelationship, KnowledgeGraph } from '../core/graph/type
2
2
  import { CommunityDetectionResult } from '../core/ingestion/community-processor.js';
3
3
  import { ProcessDetectionResult } from '../core/ingestion/process-processor.js';
4
4
  import type { UnityResourceProcessingResult } from '../core/ingestion/unity-resource-processor.js';
5
- import type { UnityRuntimeBindingResult } from '../core/ingestion/unity-runtime-binding-rules.js';
6
5
  import type { ScopeSelectionDiagnostics } from '../core/ingestion/scope-filter.js';
7
6
  export type PipelinePhase = 'idle' | 'extracting' | 'structure' | 'parsing' | 'imports' | 'calls' | 'heritage' | 'communities' | 'processes' | 'enriching' | 'complete' | 'error';
8
7
  export interface PipelineProgress {
@@ -40,7 +39,6 @@ export interface PipelineResult {
40
39
  communityResult?: CommunityDetectionResult;
41
40
  processResult?: ProcessDetectionResult;
42
41
  unityResult?: UnityResourceProcessingResult;
43
- unityRuleBindingResult?: UnityRuntimeBindingResult;
44
42
  scopeDiagnostics?: ScopeSelectionDiagnostics;
45
43
  csharpPreprocDiagnostics?: CSharpPreprocDiagnostics;
46
44
  }
@@ -49,7 +47,6 @@ export interface PipelineRuntimeSummary {
49
47
  communityResult?: CommunityDetectionResult;
50
48
  processResult?: ProcessDetectionResult;
51
49
  unityResult?: UnityResourceProcessingResult;
52
- unityRuleBindingResult?: UnityRuntimeBindingResult;
53
50
  scopeDiagnostics?: ScopeSelectionDiagnostics;
54
51
  csharpPreprocDiagnostics?: CSharpPreprocDiagnostics;
55
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veewo/gitnexus",
3
- "version": "1.5.7",
3
+ "version": "1.5.9",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",
@@ -71,6 +71,7 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@huggingface/transformers": "^3.0.0",
74
+ "@ladybugdb/core": "0.15.1",
74
75
  "@modelcontextprotocol/sdk": "^1.0.0",
75
76
  "cli-progress": "^3.12.0",
76
77
  "commander": "^12.0.0",
@@ -80,7 +81,6 @@
80
81
  "graphology": "^0.25.4",
81
82
  "graphology-indices": "^0.17.0",
82
83
  "graphology-utils": "^2.3.0",
83
- "@ladybugdb/core": "0.15.1",
84
84
  "ignore": "^7.0.5",
85
85
  "lru-cache": "^11.0.0",
86
86
  "mnemonist": "^0.39.0",
@@ -100,9 +100,9 @@
100
100
  "uuid": "^13.0.0"
101
101
  },
102
102
  "optionalDependencies": {
103
+ "tree-sitter-gdscript": "^6.1.0",
103
104
  "tree-sitter-kotlin": "^0.3.8",
104
- "tree-sitter-swift": "^0.6.0",
105
- "tree-sitter-gdscript": "^6.1.0"
105
+ "tree-sitter-swift": "^0.6.0"
106
106
  },
107
107
  "devDependencies": {
108
108
  "@types/cli-progress": "^3.11.6",
@@ -58,7 +58,7 @@ fi
58
58
  $GN analyze
59
59
  ```
60
60
 
61
- Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.gitnexus/`, and generates CLAUDE.md / AGENTS.md context files.
61
+ Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.gitnexus/`, and generates CLAUDE.md / AGENTS.md context files unless `--no-ai-context` is set.
62
62
 
63
63
  Analyze options are resolved with two-layer priority: **CLI arguments** > **stored options** in `meta.json.analyzeOptions`. On first run, pass CLI flags; they are persisted automatically for subsequent runs.
64
64
 
@@ -66,6 +66,7 @@ Analyze options are resolved with two-layer priority: **CLI arguments** > **stor
66
66
  |------|--------|
67
67
  | `--force` | Force full re-index even if up to date |
68
68
  | `--no-reuse-options` | Do not reuse stored analyze options from previous index |
69
+ | `--no-ai-context` | Skip writing `AGENTS.md` / `CLAUDE.md` and installing repo-local GitNexus skills |
69
70
  | `--embeddings` | Enable embedding generation (off by default) |
70
71
  | `--extensions <list>` | Comma-separated file extensions (e.g. `.cs,.ts`) |
71
72
  | `--scope <rules>` | Comma-separated scope path-prefix rules (e.g. `Assets/,Packages/com.veewo.*`) |
@@ -74,7 +75,9 @@ Analyze options are resolved with two-layer priority: **CLI arguments** > **stor
74
75
  | `--skills` | Generate repo-specific skill files from detected communities |
75
76
  | `-v, --verbose` | Enable verbose ingestion warnings |
76
77
 
77
- **Option persistence:** `--extensions`, `--scope`, `--repo-alias`, `--embeddings`, and `--csharp-define-csproj` are automatically saved to `meta.json.analyzeOptions` after a successful run. On subsequent runs, these stored values are reused unless you pass new CLI flags or use `--no-reuse-options`.
78
+ **Option persistence:** `--extensions`, `--scope`, `--repo-alias`, `--embeddings`, `--csharp-define-csproj`, and the effective AI-context setting are automatically saved to `meta.json.analyzeOptions` after a successful run. On subsequent runs, these stored values are reused unless you pass new CLI flags or use `--no-reuse-options`.
79
+
80
+ When `--no-ai-context` is saved, later `analyze` runs continue skipping AGENTS/CLAUDE generation until you explicitly re-enable AI context (for example by passing `--ai-context` through a direct CLI invocation or clearing the stored setting with `--no-reuse-options`).
78
81
 
79
82
  #### Scope rules
80
83