@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.
- package/dist/benchmark/analyze-runner.d.ts +0 -2
- package/dist/benchmark/analyze-runner.js +0 -6
- package/dist/benchmark/analyze-runner.test.js +1 -10
- package/dist/benchmark/runner.d.ts +0 -2
- package/dist/benchmark/runner.js +0 -2
- package/dist/benchmark/u2-e2e/neonspark-full-e2e.js +0 -11
- package/dist/benchmark/u2-performance-sampler.js +3 -16
- package/dist/cli/ai-context.js +1 -7
- package/dist/cli/analyze-options.d.ts +19 -6
- package/dist/cli/analyze-options.js +76 -71
- package/dist/cli/analyze-options.test.js +78 -73
- package/dist/cli/analyze-runtime-summary.js +0 -1
- package/dist/cli/analyze-runtime-summary.test.js +0 -2
- package/dist/cli/analyze-summary.d.ts +0 -2
- package/dist/cli/analyze-summary.js +0 -24
- package/dist/cli/analyze-summary.test.js +1 -65
- package/dist/cli/analyze.d.ts +2 -4
- package/dist/cli/analyze.js +14 -30
- package/dist/cli/analyze.test.js +9 -15
- package/dist/cli/benchmark-agent-context.d.ts +0 -2
- package/dist/cli/benchmark-agent-context.js +0 -2
- package/dist/cli/benchmark-agent-safe-query-context.d.ts +0 -2
- package/dist/cli/benchmark-agent-safe-query-context.js +0 -2
- package/dist/cli/benchmark-unity.d.ts +0 -2
- package/dist/cli/benchmark-unity.js +0 -2
- package/dist/cli/clean.d.ts +2 -3
- package/dist/cli/clean.js +4 -25
- package/dist/cli/index.js +1 -12
- package/dist/core/ingestion/pipeline.js +1 -44
- package/dist/mcp/local/agent-safe-response.js +1 -1
- package/dist/mcp/local/local-backend.d.ts +0 -23
- package/dist/mcp/local/local-backend.js +69 -248
- package/dist/mcp/local/runtime-chain-verify.test.js +0 -49
- package/dist/mcp/local/runtime-claim-rule-registry.d.ts +0 -11
- package/dist/mcp/local/runtime-claim-rule-registry.js +0 -159
- package/dist/mcp/local/runtime-claim-rule-registry.test.js +67 -214
- package/dist/mcp/tools.js +0 -70
- package/dist/storage/repo-manager.d.ts +1 -0
- package/dist/types/pipeline.d.ts +0 -3
- package/package.json +1 -1
- package/skills/gitnexus-cli.md +62 -38
- package/vendor/node_modules/node-addon-api/node_addon_api.Makefile +6 -0
- package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +122 -0
- package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +126 -0
- package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +122 -0
- package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +122 -0
- package/vendor/tree-sitter-dart/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
- package/vendor/tree-sitter-dart/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
- package/vendor/tree-sitter-proto/build/Release/.deps/node_modules/node-addon-api/node_addon_api_except.stamp.d +1 -0
- package/vendor/tree-sitter-proto/build/node_modules/node-addon-api/node_addon_api_except.stamp +0 -0
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.d.ts +0 -60
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.js +0 -395
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.d.ts +0 -1
- package/dist/benchmark/u2-e2e/phase5-rule-lab-acceptance-runner.test.js +0 -41
- package/dist/cli/rule-lab.d.ts +0 -38
- package/dist/cli/rule-lab.js +0 -148
- package/dist/cli/rule-lab.test.d.ts +0 -1
- package/dist/cli/rule-lab.test.js +0 -31
- package/dist/cli/scope-manifest-config.d.ts +0 -9
- package/dist/cli/scope-manifest-config.js +0 -37
- package/dist/cli/sync-manifest.d.ts +0 -27
- package/dist/cli/sync-manifest.js +0 -200
- package/dist/cli/sync-manifest.test.d.ts +0 -1
- package/dist/cli/sync-manifest.test.js +0 -88
- package/dist/core/ingestion/unity-runtime-binding-rules.d.ts +0 -26
- package/dist/core/ingestion/unity-runtime-binding-rules.js +0 -408
- package/dist/rule-lab/analyze.d.ts +0 -13
- package/dist/rule-lab/analyze.js +0 -125
- package/dist/rule-lab/analyze.test.d.ts +0 -1
- package/dist/rule-lab/analyze.test.js +0 -246
- package/dist/rule-lab/compile.d.ts +0 -5
- package/dist/rule-lab/compile.js +0 -51
- package/dist/rule-lab/compiled-bundles.d.ts +0 -30
- package/dist/rule-lab/compiled-bundles.js +0 -36
- package/dist/rule-lab/curate.d.ts +0 -33
- package/dist/rule-lab/curate.js +0 -155
- package/dist/rule-lab/curate.test.d.ts +0 -1
- package/dist/rule-lab/curate.test.js +0 -137
- package/dist/rule-lab/curation-input-builder.d.ts +0 -45
- package/dist/rule-lab/curation-input-builder.js +0 -133
- package/dist/rule-lab/discover.d.ts +0 -13
- package/dist/rule-lab/discover.js +0 -74
- package/dist/rule-lab/discover.test.d.ts +0 -1
- package/dist/rule-lab/discover.test.js +0 -42
- package/dist/rule-lab/paths.d.ts +0 -21
- package/dist/rule-lab/paths.js +0 -37
- package/dist/rule-lab/paths.test.d.ts +0 -1
- package/dist/rule-lab/paths.test.js +0 -46
- package/dist/rule-lab/promote.d.ts +0 -26
- package/dist/rule-lab/promote.js +0 -387
- package/dist/rule-lab/promote.test.d.ts +0 -1
- package/dist/rule-lab/promote.test.js +0 -314
- package/dist/rule-lab/regress.d.ts +0 -60
- package/dist/rule-lab/regress.js +0 -122
- package/dist/rule-lab/regress.test.d.ts +0 -1
- package/dist/rule-lab/regress.test.js +0 -68
- package/dist/rule-lab/review-pack.d.ts +0 -34
- package/dist/rule-lab/review-pack.js +0 -165
- package/dist/rule-lab/review-pack.test.d.ts +0 -1
- package/dist/rule-lab/review-pack.test.js +0 -116
- package/dist/rule-lab/types.d.ts +0 -135
- package/dist/rule-lab/types.js +0 -1
- package/skills/_shared/unity-rule-authoring-contract.md +0 -64
- package/skills/gitnexus-unity-rule-gen.md +0 -107
|
@@ -24,12 +24,6 @@ import { adjustRuntimeClaimForPolicy } from './runtime-claim.js';
|
|
|
24
24
|
// git utilities available if needed
|
|
25
25
|
// import { isGitRepo, getCurrentCommit, getGitRoot } from '../../storage/git.js';
|
|
26
26
|
import { listRegisteredRepos, cleanupOldKuzuFiles, } from '../../storage/repo-manager.js';
|
|
27
|
-
import { analyzeRuleLabSlice } from '../../rule-lab/analyze.js';
|
|
28
|
-
import { buildReviewPack } from '../../rule-lab/review-pack.js';
|
|
29
|
-
import { curateRuleLabSlice } from '../../rule-lab/curate.js';
|
|
30
|
-
import { promoteCuratedRules } from '../../rule-lab/promote.js';
|
|
31
|
-
import { runRuleLabRegress } from '../../rule-lab/regress.js';
|
|
32
|
-
import { loadCompiledRuleBundle } from '../../rule-lab/compiled-bundles.js';
|
|
33
27
|
// AI context generation is CLI-only (gitnexus analyze)
|
|
34
28
|
// import { generateAIContextFiles } from '../../cli/ai-context.js';
|
|
35
29
|
/**
|
|
@@ -246,10 +240,28 @@ async function loadSeedUnityResourceChains(input) {
|
|
|
246
240
|
}
|
|
247
241
|
catch (e) {
|
|
248
242
|
logQueryError('unity-resource-chains:seed-second-hop', e);
|
|
249
|
-
|
|
243
|
+
}
|
|
244
|
+
// One-hop query: direct UNITY_GRAPH_NODE_SCRIPT_REF from seed to Class
|
|
245
|
+
let oneHopRows = [];
|
|
246
|
+
try {
|
|
247
|
+
oneHopRows = await executeParameterized(input.repoId, `
|
|
248
|
+
MATCH (source:File {filePath: $seedPath})-[r:CodeRelation {type: 'UNITY_GRAPH_NODE_SCRIPT_REF'}]->(target)
|
|
249
|
+
WHERE labels(target)[0] = 'Class'
|
|
250
|
+
RETURN source.filePath AS sourceResourcePath,
|
|
251
|
+
r.type AS relationType,
|
|
252
|
+
r.reason AS relationReason,
|
|
253
|
+
target.id AS targetUid,
|
|
254
|
+
target.name AS targetName,
|
|
255
|
+
labels(target)[0] AS targetKind,
|
|
256
|
+
target.filePath AS targetFilePath
|
|
257
|
+
LIMIT 200
|
|
258
|
+
`, { seedPath });
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
logQueryError('unity-resource-chains:seed-one-hop', e);
|
|
250
262
|
}
|
|
251
263
|
const seen = new Set();
|
|
252
|
-
const
|
|
264
|
+
const twoHopEntries = rows
|
|
253
265
|
.map((row, index) => {
|
|
254
266
|
const targetUid = String(row?.targetUid || row?.[6] || '').trim();
|
|
255
267
|
const targetName = String(row?.targetName || row?.[7] || '').trim();
|
|
@@ -268,18 +280,35 @@ async function loadSeedUnityResourceChains(input) {
|
|
|
268
280
|
...(targetFilePath ? { filePath: targetFilePath } : {}),
|
|
269
281
|
},
|
|
270
282
|
};
|
|
271
|
-
return {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
283
|
+
return { chain, index, score: scoreUnityResourceChainTarget(chain, targetSymbols) };
|
|
284
|
+
});
|
|
285
|
+
const oneHopEntries = oneHopRows
|
|
286
|
+
.map((row, index) => {
|
|
287
|
+
const targetUid = String(row?.targetUid || row?.[4] || '').trim();
|
|
288
|
+
const targetName = String(row?.targetName || row?.[5] || '').trim();
|
|
289
|
+
const targetFilePath = normalizePath(String(row?.targetFilePath || row?.[7] || '').trim());
|
|
290
|
+
const chain = {
|
|
291
|
+
sourceResourcePath: normalizePath(String(row?.sourceResourcePath || row?.[0] || '').trim()),
|
|
292
|
+
relationType: 'UNITY_GRAPH_NODE_SCRIPT_REF',
|
|
293
|
+
relationReason: String(row?.relationReason || row?.[2] || '').trim() || undefined,
|
|
294
|
+
targetSymbol: {
|
|
295
|
+
...(targetUid ? { uid: targetUid } : {}),
|
|
296
|
+
...(targetName ? { name: targetName } : {}),
|
|
297
|
+
kind: String(row?.targetKind || row?.[6] || '').trim() || undefined,
|
|
298
|
+
...(targetFilePath ? { filePath: targetFilePath } : {}),
|
|
299
|
+
},
|
|
275
300
|
};
|
|
276
|
-
|
|
301
|
+
return { chain, index: rows.length + index, score: scoreUnityResourceChainTarget(chain, targetSymbols) };
|
|
302
|
+
});
|
|
303
|
+
const chains = [...twoHopEntries, ...oneHopEntries]
|
|
277
304
|
.filter(({ chain, score }) => {
|
|
278
|
-
if (!chain.sourceResourcePath || !chain.
|
|
305
|
+
if (!chain.sourceResourcePath || !chain.targetSymbol?.name)
|
|
279
306
|
return false;
|
|
280
307
|
if (targetSymbols.length > 0 && score <= 0)
|
|
281
308
|
return false;
|
|
282
|
-
const key =
|
|
309
|
+
const key = chain.intermediateResourcePath
|
|
310
|
+
? `${chain.sourceResourcePath}->${chain.intermediateResourcePath}->${chain.targetSymbol.uid || chain.targetSymbol.name}`
|
|
311
|
+
: `${chain.sourceResourcePath}->${chain.targetSymbol.uid || chain.targetSymbol.name}`;
|
|
283
312
|
if (seen.has(key))
|
|
284
313
|
return false;
|
|
285
314
|
seen.add(key);
|
|
@@ -473,14 +502,7 @@ export function buildNextHops(input) {
|
|
|
473
502
|
const mappedRemainder = input.mappedSeedTargets
|
|
474
503
|
.map((value) => normalizePath(value))
|
|
475
504
|
.filter((value) => value && !bindingSet.has(value));
|
|
476
|
-
const
|
|
477
|
-
.map((value) => String(value || '').trim().toLowerCase())
|
|
478
|
-
.filter(Boolean);
|
|
479
|
-
const currentSymbolMatchesRetrievalScope = retrievalHostScope.length === 0
|
|
480
|
-
|| retrievalHostScope.includes(String(input.symbolName || '').trim().toLowerCase());
|
|
481
|
-
const shouldSuppressRawResourceHops = !input.seedPath
|
|
482
|
-
&& mappedIntersectBindings.length === 0
|
|
483
|
-
&& currentSymbolMatchesRetrievalScope === false;
|
|
505
|
+
const shouldSuppressRawResourceHops = false;
|
|
484
506
|
const candidateResources = shouldSuppressRawResourceHops ? [] : rankCandidateResources([
|
|
485
507
|
...(input.seedPath ? [{ target: normalizePath(input.seedPath), bucket: 0 }] : []),
|
|
486
508
|
...mappedIntersectBindings.map((target) => ({ target, bucket: 1 })),
|
|
@@ -506,14 +528,6 @@ export function buildNextHops(input) {
|
|
|
506
528
|
next_command: `gitnexus query${repoArg} --unity-resources on --unity-hydration parity --resource-path-prefix "${target}" "${input.queryForSymbol}"`,
|
|
507
529
|
});
|
|
508
530
|
}
|
|
509
|
-
if (input.retrievalRule?.next_action) {
|
|
510
|
-
addHop({
|
|
511
|
-
kind: 'verify',
|
|
512
|
-
target: input.seedPath || input.symbolName,
|
|
513
|
-
why: `Retrieval rule ${input.retrievalRule.id} configured this follow-up action.`,
|
|
514
|
-
next_command: withRepoInCommand(input.retrievalRule.next_action),
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
531
|
if (input.verificationHint?.target
|
|
518
532
|
&& !(shouldSuppressRawResourceHops && isUnityResourcePathLike(String(input.verificationHint.target)))) {
|
|
519
533
|
addHop({
|
|
@@ -579,69 +593,6 @@ function scoreResourcePathNoise(resourcePath) {
|
|
|
579
593
|
penalty += 6;
|
|
580
594
|
return penalty;
|
|
581
595
|
}
|
|
582
|
-
async function resolveRetrievalRuleHint(input) {
|
|
583
|
-
const bundle = await loadCompiledRuleBundle(input.repoPath, 'retrieval_rules');
|
|
584
|
-
if (!bundle)
|
|
585
|
-
return undefined;
|
|
586
|
-
return pickRetrievalRuleHintFromBundle({
|
|
587
|
-
queryText: input.queryText,
|
|
588
|
-
symbolName: input.symbolName,
|
|
589
|
-
seedPath: input.seedPath,
|
|
590
|
-
rules: bundle.rules,
|
|
591
|
-
});
|
|
592
|
-
}
|
|
593
|
-
export function pickRetrievalRuleHintFromBundle(input) {
|
|
594
|
-
const haystack = [
|
|
595
|
-
String(input.queryText || ''),
|
|
596
|
-
String(input.symbolName || ''),
|
|
597
|
-
String(input.seedPath || ''),
|
|
598
|
-
].join(' ').toLowerCase();
|
|
599
|
-
const rank = (rule) => {
|
|
600
|
-
let score = 0;
|
|
601
|
-
let matchedTrigger = false;
|
|
602
|
-
let matchedEvidence = false;
|
|
603
|
-
for (const token of rule.trigger_tokens || []) {
|
|
604
|
-
const normalized = String(token || '').trim().toLowerCase();
|
|
605
|
-
if (!normalized)
|
|
606
|
-
continue;
|
|
607
|
-
if (haystack.includes(normalized)) {
|
|
608
|
-
matchedTrigger = true;
|
|
609
|
-
matchedEvidence = true;
|
|
610
|
-
score += 10 + normalized.length;
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
for (const token of rule.host_base_type || []) {
|
|
614
|
-
const normalized = String(token || '').trim().toLowerCase();
|
|
615
|
-
if (normalized && haystack.includes(normalized)) {
|
|
616
|
-
matchedEvidence = true;
|
|
617
|
-
score += 20 + normalized.length;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
for (const token of rule.resource_types || []) {
|
|
621
|
-
const normalized = String(token || '').trim().toLowerCase();
|
|
622
|
-
if (normalized && haystack.includes(normalized)) {
|
|
623
|
-
matchedEvidence = true;
|
|
624
|
-
score += 4 + normalized.length;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
if (!matchedEvidence)
|
|
628
|
-
return Number.NEGATIVE_INFINITY;
|
|
629
|
-
if (!matchedTrigger)
|
|
630
|
-
score -= 3;
|
|
631
|
-
return score;
|
|
632
|
-
};
|
|
633
|
-
const matched = [...input.rules]
|
|
634
|
-
.map((rule) => ({ rule, score: rank(rule) }))
|
|
635
|
-
.filter((entry) => Number.isFinite(entry.score))
|
|
636
|
-
.sort((a, b) => (b.score - a.score) || a.rule.id.localeCompare(b.rule.id))[0]?.rule;
|
|
637
|
-
if (!matched || !String(matched.next_action || '').trim())
|
|
638
|
-
return undefined;
|
|
639
|
-
return {
|
|
640
|
-
id: matched.id,
|
|
641
|
-
next_action: matched.next_action,
|
|
642
|
-
host_base_type: matched.host_base_type,
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
596
|
export async function resolveSeedTargetsFromResourceFile(repoPath, seedPath) {
|
|
646
597
|
if (!isUnityResourcePath(seedPath))
|
|
647
598
|
return [];
|
|
@@ -1072,16 +1023,6 @@ export class LocalBackend {
|
|
|
1072
1023
|
return this.detectChanges(repo, params);
|
|
1073
1024
|
case 'rename':
|
|
1074
1025
|
return this.rename(repo, params);
|
|
1075
|
-
case 'rule_lab_analyze':
|
|
1076
|
-
return this.ruleLabAnalyze(repo, params);
|
|
1077
|
-
case 'rule_lab_review_pack':
|
|
1078
|
-
return this.ruleLabReviewPack(repo, params);
|
|
1079
|
-
case 'rule_lab_curate':
|
|
1080
|
-
return this.ruleLabCurate(repo, params);
|
|
1081
|
-
case 'rule_lab_promote':
|
|
1082
|
-
return this.ruleLabPromote(repo, params);
|
|
1083
|
-
case 'rule_lab_regress':
|
|
1084
|
-
return this.ruleLabRegress(repo, params);
|
|
1085
1026
|
// Legacy aliases for backwards compatibility
|
|
1086
1027
|
case 'search':
|
|
1087
1028
|
return this.query(repo, params);
|
|
@@ -1118,136 +1059,6 @@ export class LocalBackend {
|
|
|
1118
1059
|
return { error: err?.message || 'unity_ui_trace failed' };
|
|
1119
1060
|
}
|
|
1120
1061
|
}
|
|
1121
|
-
async ruleLabAnalyze(repo, params) {
|
|
1122
|
-
const runId = String(params?.run_id || params?.runId || '').trim();
|
|
1123
|
-
const sliceId = String(params?.slice_id || params?.sliceId || '').trim();
|
|
1124
|
-
if (!runId || !sliceId) {
|
|
1125
|
-
return { error: 'run_id and slice_id are required for rule_lab_analyze' };
|
|
1126
|
-
}
|
|
1127
|
-
try {
|
|
1128
|
-
const out = await analyzeRuleLabSlice({
|
|
1129
|
-
repoPath: repo.repoPath,
|
|
1130
|
-
runId,
|
|
1131
|
-
sliceId,
|
|
1132
|
-
});
|
|
1133
|
-
return {
|
|
1134
|
-
...out,
|
|
1135
|
-
artifact_paths: {
|
|
1136
|
-
candidates: out.paths.candidatesPath,
|
|
1137
|
-
},
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
catch (err) {
|
|
1141
|
-
return { error: err?.message || 'rule_lab_analyze failed' };
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
async ruleLabReviewPack(repo, params) {
|
|
1145
|
-
const runId = String(params?.run_id || params?.runId || '').trim();
|
|
1146
|
-
const sliceId = String(params?.slice_id || params?.sliceId || '').trim();
|
|
1147
|
-
if (!runId || !sliceId) {
|
|
1148
|
-
return { error: 'run_id and slice_id are required for rule_lab_review_pack' };
|
|
1149
|
-
}
|
|
1150
|
-
const maxTokens = Number.isFinite(Number(params?.max_tokens ?? params?.maxTokens))
|
|
1151
|
-
? Number(params?.max_tokens ?? params?.maxTokens)
|
|
1152
|
-
: 6000;
|
|
1153
|
-
try {
|
|
1154
|
-
const out = await buildReviewPack({
|
|
1155
|
-
repoPath: repo.repoPath,
|
|
1156
|
-
runId,
|
|
1157
|
-
sliceId,
|
|
1158
|
-
maxTokens,
|
|
1159
|
-
});
|
|
1160
|
-
return {
|
|
1161
|
-
...out,
|
|
1162
|
-
artifact_paths: {
|
|
1163
|
-
review_pack: out.paths.reviewCardsPath,
|
|
1164
|
-
},
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
|
-
catch (err) {
|
|
1168
|
-
return { error: err?.message || 'rule_lab_review_pack failed' };
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
async ruleLabCurate(repo, params) {
|
|
1172
|
-
const runId = String(params?.run_id || params?.runId || '').trim();
|
|
1173
|
-
const sliceId = String(params?.slice_id || params?.sliceId || '').trim();
|
|
1174
|
-
const inputPath = String(params?.input_path || params?.inputPath || '').trim();
|
|
1175
|
-
if (!runId || !sliceId || !inputPath) {
|
|
1176
|
-
return { error: 'run_id, slice_id, and input_path are required for rule_lab_curate' };
|
|
1177
|
-
}
|
|
1178
|
-
try {
|
|
1179
|
-
const out = await curateRuleLabSlice({
|
|
1180
|
-
repoPath: repo.repoPath,
|
|
1181
|
-
runId,
|
|
1182
|
-
sliceId,
|
|
1183
|
-
inputPath,
|
|
1184
|
-
});
|
|
1185
|
-
return {
|
|
1186
|
-
...out,
|
|
1187
|
-
artifact_paths: {
|
|
1188
|
-
curated: out.paths.curatedPath,
|
|
1189
|
-
},
|
|
1190
|
-
};
|
|
1191
|
-
}
|
|
1192
|
-
catch (err) {
|
|
1193
|
-
return { error: err?.message || 'rule_lab_curate failed' };
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
async ruleLabPromote(repo, params) {
|
|
1197
|
-
const runId = String(params?.run_id || params?.runId || '').trim();
|
|
1198
|
-
const sliceId = String(params?.slice_id || params?.sliceId || '').trim();
|
|
1199
|
-
if (!runId || !sliceId) {
|
|
1200
|
-
return { error: 'run_id and slice_id are required for rule_lab_promote' };
|
|
1201
|
-
}
|
|
1202
|
-
try {
|
|
1203
|
-
const out = await promoteCuratedRules({
|
|
1204
|
-
repoPath: repo.repoPath,
|
|
1205
|
-
runId,
|
|
1206
|
-
sliceId,
|
|
1207
|
-
version: typeof params?.version === 'string' ? params.version : undefined,
|
|
1208
|
-
});
|
|
1209
|
-
return {
|
|
1210
|
-
...out,
|
|
1211
|
-
artifact_paths: {
|
|
1212
|
-
catalog: path.join(out.paths.rulesRoot, 'catalog.json'),
|
|
1213
|
-
promoted_files: out.promotedFiles,
|
|
1214
|
-
compiled_bundles: out.compiledPaths,
|
|
1215
|
-
},
|
|
1216
|
-
};
|
|
1217
|
-
}
|
|
1218
|
-
catch (err) {
|
|
1219
|
-
return { error: err?.message || 'rule_lab_promote failed' };
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
async ruleLabRegress(repo, params) {
|
|
1223
|
-
const precision = Number(params?.precision);
|
|
1224
|
-
const coverage = Number(params?.coverage);
|
|
1225
|
-
if (!Number.isFinite(precision) || !Number.isFinite(coverage)) {
|
|
1226
|
-
return { error: 'precision and coverage are required numeric fields for rule_lab_regress' };
|
|
1227
|
-
}
|
|
1228
|
-
try {
|
|
1229
|
-
let probes = Array.isArray(params?.probes) ? params.probes : undefined;
|
|
1230
|
-
const probesPath = String(params?.probes_path || params?.probesPath || '').trim();
|
|
1231
|
-
if (!probes && probesPath) {
|
|
1232
|
-
const raw = await fs.readFile(path.isAbsolute(probesPath) ? probesPath : path.join(repo.repoPath, probesPath), 'utf-8');
|
|
1233
|
-
probes = JSON.parse(raw);
|
|
1234
|
-
}
|
|
1235
|
-
const out = await runRuleLabRegress({
|
|
1236
|
-
precision,
|
|
1237
|
-
coverage,
|
|
1238
|
-
probes,
|
|
1239
|
-
repoPath: repo.repoPath,
|
|
1240
|
-
runId: String(params?.run_id || params?.runId || '').trim() || undefined,
|
|
1241
|
-
});
|
|
1242
|
-
return {
|
|
1243
|
-
...out,
|
|
1244
|
-
artifact_paths: out.reportPath ? { report: out.reportPath } : {},
|
|
1245
|
-
};
|
|
1246
|
-
}
|
|
1247
|
-
catch (err) {
|
|
1248
|
-
return { error: err?.message || 'rule_lab_regress failed' };
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
1062
|
// ─── Tool Implementations ────────────────────────────────────────
|
|
1252
1063
|
/**
|
|
1253
1064
|
* Query tool — process-grouped search.
|
|
@@ -1692,18 +1503,23 @@ export class LocalBackend {
|
|
|
1692
1503
|
const firstResourceBindings = Array.isArray(firstSymbolForHops?.resourceBindings)
|
|
1693
1504
|
? firstSymbolForHops.resourceBindings
|
|
1694
1505
|
: [];
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1506
|
+
// Bindings→chains fallback: if resource_chains is empty but bindings exist, generate one-hop chains
|
|
1507
|
+
if ((!result.resource_chains || result.resource_chains.length === 0) && firstResourceBindings.length > 0) {
|
|
1508
|
+
result.resource_chains = firstResourceBindings.slice(0, 20).map((binding) => ({
|
|
1509
|
+
sourceResourcePath: binding.resourcePath,
|
|
1510
|
+
relationType: 'UNITY_GRAPH_NODE_SCRIPT_REF',
|
|
1511
|
+
targetSymbol: {
|
|
1512
|
+
uid: firstSymbolForHops?.id || undefined,
|
|
1513
|
+
name: firstSymbolForHops?.name || undefined,
|
|
1514
|
+
filePath: firstSymbolForHops?.filePath || undefined,
|
|
1515
|
+
},
|
|
1516
|
+
}));
|
|
1517
|
+
}
|
|
1701
1518
|
result.next_hops = buildNextHops({
|
|
1702
1519
|
seedPath,
|
|
1703
1520
|
mappedSeedTargets,
|
|
1704
1521
|
resourceBindings: firstResourceBindings,
|
|
1705
1522
|
verificationHint: firstVerificationHint,
|
|
1706
|
-
retrievalRule,
|
|
1707
1523
|
repoName: repo.name,
|
|
1708
1524
|
symbolName: String(firstSymbolForHops?.name || searchQuery),
|
|
1709
1525
|
queryForSymbol: String(firstSymbolForHops?.name || searchQuery),
|
|
@@ -2462,18 +2278,23 @@ export class LocalBackend {
|
|
|
2462
2278
|
}],
|
|
2463
2279
|
});
|
|
2464
2280
|
}
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2281
|
+
// Bindings→chains fallback: if resource_chains is empty but bindings exist, generate one-hop chains
|
|
2282
|
+
if ((!result.resource_chains || result.resource_chains.length === 0) && contextResourceBindings.length > 0) {
|
|
2283
|
+
result.resource_chains = contextResourceBindings.slice(0, 20).map((binding) => ({
|
|
2284
|
+
sourceResourcePath: binding.resourcePath,
|
|
2285
|
+
relationType: 'UNITY_GRAPH_NODE_SCRIPT_REF',
|
|
2286
|
+
targetSymbol: {
|
|
2287
|
+
uid: symNodeId || undefined,
|
|
2288
|
+
name: symName || undefined,
|
|
2289
|
+
filePath: symFilePath || undefined,
|
|
2290
|
+
},
|
|
2291
|
+
}));
|
|
2292
|
+
}
|
|
2471
2293
|
result.next_hops = buildNextHops({
|
|
2472
2294
|
seedPath,
|
|
2473
2295
|
mappedSeedTargets,
|
|
2474
2296
|
resourceBindings: contextResourceBindings,
|
|
2475
2297
|
verificationHint: topVerificationHint,
|
|
2476
|
-
retrievalRule,
|
|
2477
2298
|
repoName: repo.name,
|
|
2478
2299
|
symbolName: symName || String(name || uid || ''),
|
|
2479
2300
|
queryForSymbol: symName || String(name || uid || ''),
|
|
@@ -3,7 +3,6 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { describe, expect, it } from 'vitest';
|
|
5
5
|
import { verifyRuntimeChainOnDemand, verifyRuntimeClaimOnDemand } from './runtime-chain-verify.js';
|
|
6
|
-
import { promoteCuratedRules } from '../../rule-lab/promote.js';
|
|
7
6
|
async function makeTempRepo() {
|
|
8
7
|
const repoPath = await fs.mkdtemp(path.join(os.tmpdir(), 'runtime-chain-verify-'));
|
|
9
8
|
await fs.mkdir(path.join(repoPath, 'Assets/NEON/DataAssets/Powerups/1_newWeapon/0_pick/法器_Orb'), { recursive: true });
|
|
@@ -590,52 +589,4 @@ describe('runtime chain verify', () => {
|
|
|
590
589
|
expect(String(out.next_action || '')).toContain('Reload runtime start sequence');
|
|
591
590
|
await fs.rm(repoPath, { recursive: true, force: true });
|
|
592
591
|
});
|
|
593
|
-
it('phase5 promote artifacts do not alter query-time graph-only rule identity', async () => {
|
|
594
|
-
const repoPath = await fs.mkdtemp(path.join(os.tmpdir(), 'runtime-chain-rule-lab-promote-'));
|
|
595
|
-
const sliceDir = path.join(repoPath, '.gitnexus', 'rules', 'lab', 'runs', 'run-x', 'slices', 'slice-a');
|
|
596
|
-
await fs.mkdir(sliceDir, { recursive: true });
|
|
597
|
-
await fs.writeFile(path.join(sliceDir, 'curated.json'), JSON.stringify({
|
|
598
|
-
run_id: 'run-x',
|
|
599
|
-
slice_id: 'slice-a',
|
|
600
|
-
curated: [
|
|
601
|
-
{
|
|
602
|
-
id: 'candidate-startup-1',
|
|
603
|
-
rule_id: 'demo.startup.v1',
|
|
604
|
-
title: 'startup startup graph',
|
|
605
|
-
confirmed_chain: {
|
|
606
|
-
steps: [{ hop_type: 'resource', anchor: 'Assets/Rules/startup.asset:1', snippet: 'Startup Graph Trigger' }],
|
|
607
|
-
},
|
|
608
|
-
guarantees: ['startup trigger matching is confirmed'],
|
|
609
|
-
non_guarantees: ['does not prove full runtime ordering'],
|
|
610
|
-
},
|
|
611
|
-
],
|
|
612
|
-
}, null, 2), 'utf-8');
|
|
613
|
-
await promoteCuratedRules({ repoPath, runId: 'run-x', sliceId: 'slice-a', version: '1.0.0' });
|
|
614
|
-
const out = await verifyRuntimeClaimOnDemand({
|
|
615
|
-
repoPath,
|
|
616
|
-
queryText: 'Startup Graph Trigger',
|
|
617
|
-
symbolName: 'StartupNode',
|
|
618
|
-
symbolFilePath: 'Assets/Rules/StartupNode.cs',
|
|
619
|
-
resourceSeedPath: 'Assets/Rules/startup.asset',
|
|
620
|
-
executeParameterized: async (query) => {
|
|
621
|
-
if (String(query || '').includes('WHERE n.name IN $symbolNames')) {
|
|
622
|
-
return [{
|
|
623
|
-
id: 'Class:Assets/Rules/StartupNode.cs:StartupNode',
|
|
624
|
-
name: 'StartupNode',
|
|
625
|
-
type: 'Class',
|
|
626
|
-
filePath: 'Assets/Rules/StartupNode.cs',
|
|
627
|
-
startLine: 1,
|
|
628
|
-
}];
|
|
629
|
-
}
|
|
630
|
-
return [];
|
|
631
|
-
},
|
|
632
|
-
resourceBindings: [{ resourcePath: 'Assets/Rules/startup.asset' }],
|
|
633
|
-
});
|
|
634
|
-
expect(out.rule_id).toBe('graph-only.runtime-closure.v1');
|
|
635
|
-
expect(out.rule_id).not.toBe('demo.startup.v1');
|
|
636
|
-
expect(out.status).toBe('verified_partial');
|
|
637
|
-
expect(out.evidence_level).toBe('verified_segment');
|
|
638
|
-
expect(out.reason).toBeUndefined();
|
|
639
|
-
await fs.rm(repoPath, { recursive: true, force: true });
|
|
640
|
-
});
|
|
641
592
|
});
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import type { UnityResourceBinding, LifecycleOverrides } from '../../rule-lab/types.js';
|
|
2
1
|
export interface RuntimeClaimRuleCatalogEntry {
|
|
3
2
|
id: string;
|
|
4
3
|
version: string;
|
|
5
4
|
file?: string;
|
|
6
5
|
enabled?: boolean;
|
|
7
|
-
family?: 'analyze_rules' | 'verification_rules';
|
|
8
6
|
}
|
|
9
7
|
export interface RuntimeClaimRule {
|
|
10
8
|
id: string;
|
|
@@ -24,9 +22,6 @@ export interface RuntimeClaimRule {
|
|
|
24
22
|
guarantees: string[];
|
|
25
23
|
non_guarantees: string[];
|
|
26
24
|
next_action?: string;
|
|
27
|
-
family?: 'analyze_rules' | 'verification_rules';
|
|
28
|
-
resource_bindings?: UnityResourceBinding[];
|
|
29
|
-
lifecycle_overrides?: LifecycleOverrides;
|
|
30
25
|
file_path: string;
|
|
31
26
|
topology?: Array<{
|
|
32
27
|
hop: string;
|
|
@@ -60,9 +55,3 @@ export declare class RuleRegistryLoadError extends Error {
|
|
|
60
55
|
constructor(code: RuleRegistryLoadErrorCode, message: string, details?: Record<string, string>);
|
|
61
56
|
}
|
|
62
57
|
export declare function parseRuleYaml(raw: string, filePath: string): RuntimeClaimRule;
|
|
63
|
-
/**
|
|
64
|
-
* Runtime claim rule registry remains the source for analyze-time synthetic-edge production
|
|
65
|
-
* and offline governance/report workflows. Query-time runtime closure verification is graph-only.
|
|
66
|
-
*/
|
|
67
|
-
export declare function loadRuleRegistry(repoPath: string, rulesRoot?: string): Promise<RuntimeClaimRuleRegistry>;
|
|
68
|
-
export declare function loadAnalyzeRules(repoPath: string, rulesRoot?: string): Promise<RuntimeClaimRule[]>;
|
|
@@ -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
|
-
}
|