@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
package/dist/rule-lab/promote.js
DELETED
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { getRuleLabPaths } from './paths.js';
|
|
4
|
-
import { writeCompiledRuleBundle, loadCompiledRuleBundle } from './compiled-bundles.js';
|
|
5
|
-
function quoteYaml(value) {
|
|
6
|
-
const raw = String(value || '');
|
|
7
|
-
if (/^[a-zA-Z0-9._-]+$/.test(raw))
|
|
8
|
-
return raw;
|
|
9
|
-
return `'${raw.replace(/'/g, "''")}'`;
|
|
10
|
-
}
|
|
11
|
-
function inferTriggerFamily(item) {
|
|
12
|
-
const fromTitle = String(item.title || '').trim().split(/\s+/)[0];
|
|
13
|
-
if (fromTitle)
|
|
14
|
-
return fromTitle.toLowerCase();
|
|
15
|
-
return 'runtime';
|
|
16
|
-
}
|
|
17
|
-
function unique(values) {
|
|
18
|
-
return [...new Set(values.map((value) => String(value || '').trim()).filter(Boolean))];
|
|
19
|
-
}
|
|
20
|
-
function toComparableToken(value) {
|
|
21
|
-
return String(value || '').trim().toLowerCase();
|
|
22
|
-
}
|
|
23
|
-
function isForbiddenPlaceholder(value) {
|
|
24
|
-
const token = toComparableToken(value);
|
|
25
|
-
return token === 'unknown' || token === 'todo' || token === 'tbd' || /<[^>]+>/.test(token);
|
|
26
|
-
}
|
|
27
|
-
function hasPlaceholderText(value) {
|
|
28
|
-
const raw = String(value || '').trim();
|
|
29
|
-
return !raw || /TODO|TBD|placeholder|<[^>]+>/i.test(raw);
|
|
30
|
-
}
|
|
31
|
-
function assertNoPlaceholderScope(values, field) {
|
|
32
|
-
if (values.length === 0) {
|
|
33
|
-
throw new Error(`promote lint failed: ${field} must be non-empty`);
|
|
34
|
-
}
|
|
35
|
-
if (values.some((entry) => isForbiddenPlaceholder(entry))) {
|
|
36
|
-
throw new Error(`promote lint failed: unknown scope placeholder is forbidden (${field})`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function assertResolvedBindings(resourceBindings, ruleId) {
|
|
40
|
-
if (!Array.isArray(resourceBindings) || resourceBindings.length === 0)
|
|
41
|
-
return;
|
|
42
|
-
const raw = JSON.stringify(resourceBindings);
|
|
43
|
-
if (/UnknownClass|UnknownMethod|UnknownSource|UnknownTarget|TODO|TBD|placeholder|<[^>]+>/i.test(raw)) {
|
|
44
|
-
throw new Error(`binding_unresolved: placeholder binding values are forbidden for rule ${ruleId}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function requireEvidenceGuard(item, ruleId) {
|
|
48
|
-
const steps = Array.isArray(item.confirmed_chain?.steps) ? item.confirmed_chain.steps : [];
|
|
49
|
-
if (steps.length === 0) {
|
|
50
|
-
throw new Error(`evidence_guard_failed: confirmed_chain.steps must be non-empty for rule ${ruleId}`);
|
|
51
|
-
}
|
|
52
|
-
for (let index = 0; index < steps.length; index += 1) {
|
|
53
|
-
const step = steps[index];
|
|
54
|
-
if (hasPlaceholderText(step.anchor)) {
|
|
55
|
-
throw new Error(`evidence_guard_failed: confirmed_chain.steps[${index}].anchor invalid for rule ${ruleId}`);
|
|
56
|
-
}
|
|
57
|
-
if (hasPlaceholderText(step.snippet)) {
|
|
58
|
-
throw new Error(`evidence_guard_failed: confirmed_chain.steps[${index}].snippet invalid for rule ${ruleId}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function isExactPairEventDelegateItem(item) {
|
|
63
|
-
const triggerTokens = Array.isArray(item.match?.trigger_tokens)
|
|
64
|
-
? item.match?.trigger_tokens || []
|
|
65
|
-
: [];
|
|
66
|
-
const hasEventDelegateTrigger = triggerTokens.some((token) => toComparableToken(token) === 'event_delegate');
|
|
67
|
-
if (!hasEventDelegateTrigger)
|
|
68
|
-
return false;
|
|
69
|
-
const hasCodeRuntimeTopology = Array.isArray(item.topology)
|
|
70
|
-
&& item.topology.some((hop) => toComparableToken(hop.hop) === 'code_runtime');
|
|
71
|
-
const hasEvidence = Array.isArray(item.confirmed_chain?.steps) && item.confirmed_chain.steps.length > 0;
|
|
72
|
-
return hasCodeRuntimeTopology && hasEvidence;
|
|
73
|
-
}
|
|
74
|
-
function requireBindingGuard(item, ruleId) {
|
|
75
|
-
const bindings = item.resource_bindings;
|
|
76
|
-
if (isExactPairEventDelegateItem(item) && (!Array.isArray(bindings) || bindings.length === 0)) {
|
|
77
|
-
throw new Error(`binding_unresolved: exact_pair_binding_missing for rule ${ruleId}`);
|
|
78
|
-
}
|
|
79
|
-
assertResolvedBindings(bindings, ruleId);
|
|
80
|
-
}
|
|
81
|
-
function toDraftFromCurated(item) {
|
|
82
|
-
const triggerTokens = unique(item.match?.trigger_tokens || [inferTriggerFamily(item)]);
|
|
83
|
-
const topology = Array.isArray(item.topology) && item.topology.length > 0
|
|
84
|
-
? item.topology
|
|
85
|
-
: item.confirmed_chain.steps.map((step) => ({
|
|
86
|
-
hop: String(step.hop_type || 'resource'),
|
|
87
|
-
from: { entity: 'resource' },
|
|
88
|
-
to: { entity: 'script' },
|
|
89
|
-
edge: { kind: 'binds_script' },
|
|
90
|
-
}));
|
|
91
|
-
const requiredHops = unique(item.closure?.required_hops || topology.map((step) => step.hop));
|
|
92
|
-
const failureMap = item.closure?.failure_map && Object.keys(item.closure.failure_map).length > 0
|
|
93
|
-
? item.closure.failure_map
|
|
94
|
-
: { missing_evidence: 'rule_matched_but_evidence_missing' };
|
|
95
|
-
const guarantees = unique(item.claims?.guarantees || item.guarantees);
|
|
96
|
-
const nonGuarantees = unique(item.claims?.non_guarantees || item.non_guarantees);
|
|
97
|
-
const nextAction = String(item.claims?.next_action || '').trim() || 'gitnexus query "runtime"';
|
|
98
|
-
return {
|
|
99
|
-
id: String(item.rule_id || item.id || '').trim(),
|
|
100
|
-
version: '2.0.0',
|
|
101
|
-
match: {
|
|
102
|
-
trigger_tokens: triggerTokens,
|
|
103
|
-
symbol_kind: item.match?.symbol_kind || [],
|
|
104
|
-
module_scope: item.match?.module_scope || [],
|
|
105
|
-
resource_types: unique(item.match?.resource_types || []),
|
|
106
|
-
host_base_type: unique(item.match?.host_base_type || []),
|
|
107
|
-
},
|
|
108
|
-
topology,
|
|
109
|
-
closure: {
|
|
110
|
-
required_hops: requiredHops,
|
|
111
|
-
failure_map: failureMap,
|
|
112
|
-
},
|
|
113
|
-
claims: {
|
|
114
|
-
guarantees,
|
|
115
|
-
non_guarantees: nonGuarantees,
|
|
116
|
-
next_action: nextAction,
|
|
117
|
-
},
|
|
118
|
-
...(Array.isArray(item.resource_bindings)
|
|
119
|
-
? { resource_bindings: item.resource_bindings }
|
|
120
|
-
: {}),
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
function compileRule(ruleId, version, draft) {
|
|
124
|
-
const triggerFamily = String(draft.match.trigger_tokens[0] || '').trim() || 'runtime';
|
|
125
|
-
const resourceTypes = unique(draft.match.resource_types || []);
|
|
126
|
-
const hostBaseType = unique(draft.match.host_base_type || []);
|
|
127
|
-
if (resourceTypes.length === 0) {
|
|
128
|
-
resourceTypes.push('unspecified_resource');
|
|
129
|
-
}
|
|
130
|
-
if (hostBaseType.length === 0) {
|
|
131
|
-
hostBaseType.push('unspecified_host');
|
|
132
|
-
}
|
|
133
|
-
const requiredHops = unique(draft.closure.required_hops);
|
|
134
|
-
const guarantees = unique(draft.claims.guarantees);
|
|
135
|
-
const nonGuarantees = unique(draft.claims.non_guarantees);
|
|
136
|
-
const nextAction = String(draft.claims.next_action || '').trim() || 'gitnexus query "runtime"';
|
|
137
|
-
assertNoPlaceholderScope(resourceTypes, 'resource_types');
|
|
138
|
-
assertNoPlaceholderScope(hostBaseType, 'host_base_type');
|
|
139
|
-
assertResolvedBindings(draft.resource_bindings, ruleId);
|
|
140
|
-
return {
|
|
141
|
-
id: ruleId,
|
|
142
|
-
version,
|
|
143
|
-
trigger_family: triggerFamily,
|
|
144
|
-
resource_types: resourceTypes,
|
|
145
|
-
host_base_type: hostBaseType,
|
|
146
|
-
required_hops: requiredHops,
|
|
147
|
-
guarantees,
|
|
148
|
-
non_guarantees: nonGuarantees,
|
|
149
|
-
next_action: nextAction,
|
|
150
|
-
match: draft.match,
|
|
151
|
-
topology: draft.topology,
|
|
152
|
-
closure: draft.closure,
|
|
153
|
-
claims: draft.claims,
|
|
154
|
-
...(draft.resource_bindings ? { resource_bindings: draft.resource_bindings } : {}),
|
|
155
|
-
...(draft.lifecycle_overrides ? { lifecycle_overrides: draft.lifecycle_overrides } : {}),
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
function toStageAwareCompiledRule(rule, relativeFile) {
|
|
159
|
-
return {
|
|
160
|
-
id: rule.id,
|
|
161
|
-
version: rule.version,
|
|
162
|
-
trigger_family: rule.trigger_family,
|
|
163
|
-
trigger_tokens: [...rule.match.trigger_tokens],
|
|
164
|
-
resource_types: [...rule.resource_types],
|
|
165
|
-
host_base_type: [...rule.host_base_type],
|
|
166
|
-
required_hops: [...rule.required_hops],
|
|
167
|
-
guarantees: [...rule.guarantees],
|
|
168
|
-
non_guarantees: [...rule.non_guarantees],
|
|
169
|
-
next_action: rule.next_action,
|
|
170
|
-
file_path: relativeFile,
|
|
171
|
-
match: rule.match,
|
|
172
|
-
topology: rule.topology,
|
|
173
|
-
closure: rule.closure,
|
|
174
|
-
claims: rule.claims,
|
|
175
|
-
...(rule.resource_bindings ? { resource_bindings: rule.resource_bindings } : {}),
|
|
176
|
-
...(rule.lifecycle_overrides ? { lifecycle_overrides: rule.lifecycle_overrides } : {}),
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function pushList(lines, key, values, indent = '') {
|
|
180
|
-
lines.push(`${indent}${key}:`);
|
|
181
|
-
values.forEach((value) => lines.push(`${indent} - ${quoteYaml(value)}`));
|
|
182
|
-
}
|
|
183
|
-
function renderObjectLines(lines, object, indent = '') {
|
|
184
|
-
const entries = Object.entries(object || {});
|
|
185
|
-
for (const [key, value] of entries) {
|
|
186
|
-
const scalar = typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'
|
|
187
|
-
? String(value)
|
|
188
|
-
: JSON.stringify(value);
|
|
189
|
-
lines.push(`${indent}${key}: ${quoteYaml(scalar)}`);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
function buildRuleYaml(rule) {
|
|
193
|
-
const lines = [
|
|
194
|
-
`id: ${quoteYaml(rule.id)}`,
|
|
195
|
-
`version: ${quoteYaml(rule.version)}`,
|
|
196
|
-
...(rule.family ? [`family: ${quoteYaml(rule.family)}`] : []),
|
|
197
|
-
`trigger_family: ${quoteYaml(rule.trigger_family)}`,
|
|
198
|
-
];
|
|
199
|
-
pushList(lines, 'resource_types', rule.resource_types);
|
|
200
|
-
pushList(lines, 'host_base_type', rule.host_base_type);
|
|
201
|
-
pushList(lines, 'required_hops', rule.required_hops);
|
|
202
|
-
pushList(lines, 'guarantees', rule.guarantees);
|
|
203
|
-
pushList(lines, 'non_guarantees', rule.non_guarantees);
|
|
204
|
-
lines.push(`next_action: ${quoteYaml(rule.next_action)}`);
|
|
205
|
-
lines.push('match:');
|
|
206
|
-
pushList(lines, 'trigger_tokens', rule.match.trigger_tokens, ' ');
|
|
207
|
-
if (Array.isArray(rule.match.symbol_kind) && rule.match.symbol_kind.length > 0) {
|
|
208
|
-
pushList(lines, 'symbol_kind', rule.match.symbol_kind, ' ');
|
|
209
|
-
}
|
|
210
|
-
if (Array.isArray(rule.match.module_scope) && rule.match.module_scope.length > 0) {
|
|
211
|
-
pushList(lines, 'module_scope', rule.match.module_scope, ' ');
|
|
212
|
-
}
|
|
213
|
-
if (Array.isArray(rule.match.resource_types) && rule.match.resource_types.length > 0) {
|
|
214
|
-
pushList(lines, 'resource_types', rule.match.resource_types, ' ');
|
|
215
|
-
}
|
|
216
|
-
if (Array.isArray(rule.match.host_base_type) && rule.match.host_base_type.length > 0) {
|
|
217
|
-
pushList(lines, 'host_base_type', rule.match.host_base_type, ' ');
|
|
218
|
-
}
|
|
219
|
-
lines.push('topology:');
|
|
220
|
-
for (const hop of rule.topology) {
|
|
221
|
-
lines.push(` - hop: ${quoteYaml(hop.hop)}`);
|
|
222
|
-
lines.push(' from:');
|
|
223
|
-
renderObjectLines(lines, hop.from || {}, ' ');
|
|
224
|
-
lines.push(' to:');
|
|
225
|
-
renderObjectLines(lines, hop.to || {}, ' ');
|
|
226
|
-
lines.push(' edge:');
|
|
227
|
-
lines.push(` kind: ${quoteYaml(String(hop.edge?.kind || 'calls'))}`);
|
|
228
|
-
if (hop.constraints && Object.keys(hop.constraints).length > 0) {
|
|
229
|
-
lines.push(' constraints:');
|
|
230
|
-
renderObjectLines(lines, hop.constraints, ' ');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
lines.push('closure:');
|
|
234
|
-
pushList(lines, 'required_hops', rule.closure.required_hops, ' ');
|
|
235
|
-
lines.push(' failure_map:');
|
|
236
|
-
for (const [key, value] of Object.entries(rule.closure.failure_map || {})) {
|
|
237
|
-
lines.push(` ${quoteYaml(key)}: ${quoteYaml(String(value || 'rule_matched_but_evidence_missing'))}`);
|
|
238
|
-
}
|
|
239
|
-
lines.push('claims:');
|
|
240
|
-
pushList(lines, 'guarantees', rule.claims.guarantees, ' ');
|
|
241
|
-
pushList(lines, 'non_guarantees', rule.claims.non_guarantees, ' ');
|
|
242
|
-
lines.push(` next_action: ${quoteYaml(rule.claims.next_action)}`);
|
|
243
|
-
if (rule.resource_bindings && rule.resource_bindings.length > 0) {
|
|
244
|
-
lines.push('resource_bindings:');
|
|
245
|
-
for (const binding of rule.resource_bindings) {
|
|
246
|
-
lines.push(` - kind: ${binding.kind}`);
|
|
247
|
-
if (binding.ref_field_pattern)
|
|
248
|
-
lines.push(` ref_field_pattern: ${quoteYaml(binding.ref_field_pattern)}`);
|
|
249
|
-
if (binding.target_entry_points?.length)
|
|
250
|
-
pushList(lines, 'target_entry_points', binding.target_entry_points, ' ');
|
|
251
|
-
if (binding.host_class_pattern)
|
|
252
|
-
lines.push(` host_class_pattern: ${quoteYaml(binding.host_class_pattern)}`);
|
|
253
|
-
if (binding.field_name)
|
|
254
|
-
lines.push(` field_name: ${quoteYaml(binding.field_name)}`);
|
|
255
|
-
if (binding.loader_methods?.length)
|
|
256
|
-
pushList(lines, 'loader_methods', binding.loader_methods, ' ');
|
|
257
|
-
if (binding.scene_name)
|
|
258
|
-
lines.push(` scene_name: ${quoteYaml(binding.scene_name)}`);
|
|
259
|
-
if (binding.source_class_pattern)
|
|
260
|
-
lines.push(` source_class_pattern: ${quoteYaml(binding.source_class_pattern)}`);
|
|
261
|
-
if (binding.source_method)
|
|
262
|
-
lines.push(` source_method: ${quoteYaml(binding.source_method)}`);
|
|
263
|
-
if (binding.target_class_pattern)
|
|
264
|
-
lines.push(` target_class_pattern: ${quoteYaml(binding.target_class_pattern)}`);
|
|
265
|
-
if (binding.target_method)
|
|
266
|
-
lines.push(` target_method: ${quoteYaml(binding.target_method)}`);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (rule.lifecycle_overrides) {
|
|
270
|
-
lines.push('lifecycle_overrides:');
|
|
271
|
-
if (rule.lifecycle_overrides.additional_entry_points?.length) {
|
|
272
|
-
pushList(lines, 'additional_entry_points', rule.lifecycle_overrides.additional_entry_points, ' ');
|
|
273
|
-
}
|
|
274
|
-
if (rule.lifecycle_overrides.scope) {
|
|
275
|
-
lines.push(` scope: ${quoteYaml(rule.lifecycle_overrides.scope)}`);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return `${lines.join('\n')}\n`;
|
|
279
|
-
}
|
|
280
|
-
async function readCatalog(catalogPath) {
|
|
281
|
-
try {
|
|
282
|
-
const raw = await fs.readFile(catalogPath, 'utf-8');
|
|
283
|
-
const parsed = JSON.parse(raw);
|
|
284
|
-
return {
|
|
285
|
-
version: Number(parsed.version || 1),
|
|
286
|
-
rules: Array.isArray(parsed.rules) ? parsed.rules : [],
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
catch (error) {
|
|
290
|
-
if (error?.code === 'ENOENT') {
|
|
291
|
-
return { version: 1, rules: [] };
|
|
292
|
-
}
|
|
293
|
-
throw error;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
async function fileExists(filePath) {
|
|
297
|
-
try {
|
|
298
|
-
await fs.access(filePath);
|
|
299
|
-
return true;
|
|
300
|
-
}
|
|
301
|
-
catch (error) {
|
|
302
|
-
if (error?.code === 'ENOENT')
|
|
303
|
-
return false;
|
|
304
|
-
throw error;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
export async function promoteCuratedRules(input) {
|
|
308
|
-
const normalizedRepoPath = path.resolve(input.repoPath);
|
|
309
|
-
const paths = getRuleLabPaths(normalizedRepoPath, input.runId, input.sliceId);
|
|
310
|
-
const version = String(input.version || '1.0.0');
|
|
311
|
-
const curatedRaw = await fs.readFile(paths.curatedPath, 'utf-8');
|
|
312
|
-
const curatedDoc = JSON.parse(curatedRaw);
|
|
313
|
-
const curatedItems = Array.isArray(curatedDoc.curated) ? curatedDoc.curated : [];
|
|
314
|
-
if (curatedItems.length === 0) {
|
|
315
|
-
throw new Error('No curated candidates available for promotion');
|
|
316
|
-
}
|
|
317
|
-
let dslDraftFromCurate;
|
|
318
|
-
try {
|
|
319
|
-
const dslDraftRaw = await fs.readFile(path.join(path.dirname(paths.curatedPath), 'dsl-draft.json'), 'utf-8');
|
|
320
|
-
dslDraftFromCurate = JSON.parse(dslDraftRaw);
|
|
321
|
-
}
|
|
322
|
-
catch (error) {
|
|
323
|
-
if (error?.code !== 'ENOENT')
|
|
324
|
-
throw error;
|
|
325
|
-
}
|
|
326
|
-
const catalogPath = path.join(paths.rulesRoot, 'catalog.json');
|
|
327
|
-
const catalog = await readCatalog(catalogPath);
|
|
328
|
-
const promotedFiles = [];
|
|
329
|
-
const compiledRules = [];
|
|
330
|
-
await fs.mkdir(paths.promotedRoot, { recursive: true });
|
|
331
|
-
for (const item of curatedItems) {
|
|
332
|
-
const ruleId = String(item.rule_id || item.id || '').trim();
|
|
333
|
-
if (!ruleId) {
|
|
334
|
-
throw new Error('curated item missing rule id');
|
|
335
|
-
}
|
|
336
|
-
requireEvidenceGuard(item, ruleId);
|
|
337
|
-
requireBindingGuard(item, ruleId);
|
|
338
|
-
if (catalog.rules.some((entry) => entry.id === ruleId)) {
|
|
339
|
-
throw new Error(`duplicate_rule_id: ${ruleId} already exists in catalog`);
|
|
340
|
-
}
|
|
341
|
-
const relativeFile = path.join('approved', `${ruleId}.yaml`).split(path.sep).join('/');
|
|
342
|
-
const absoluteFile = path.join(paths.rulesRoot, relativeFile);
|
|
343
|
-
if (await fileExists(absoluteFile)) {
|
|
344
|
-
throw new Error(`duplicate_rule_id: ${ruleId} already exists at ${relativeFile}`);
|
|
345
|
-
}
|
|
346
|
-
const draft = dslDraftFromCurate && curatedItems.length === 1
|
|
347
|
-
? { ...dslDraftFromCurate, id: ruleId, version }
|
|
348
|
-
: { ...toDraftFromCurated(item), id: ruleId, version };
|
|
349
|
-
const compiledRule = compileRule(ruleId, version, draft);
|
|
350
|
-
const yaml = buildRuleYaml(compiledRule);
|
|
351
|
-
await fs.writeFile(absoluteFile, yaml, 'utf-8');
|
|
352
|
-
promotedFiles.push(absoluteFile);
|
|
353
|
-
compiledRules.push(toStageAwareCompiledRule(compiledRule, relativeFile));
|
|
354
|
-
const nextEntry = {
|
|
355
|
-
id: ruleId,
|
|
356
|
-
version,
|
|
357
|
-
enabled: true,
|
|
358
|
-
file: relativeFile,
|
|
359
|
-
...(compiledRule.family ? { family: compiledRule.family } : {}),
|
|
360
|
-
};
|
|
361
|
-
catalog.rules.push(nextEntry);
|
|
362
|
-
}
|
|
363
|
-
await fs.mkdir(path.dirname(catalogPath), { recursive: true });
|
|
364
|
-
await fs.writeFile(catalogPath, `${JSON.stringify(catalog, null, 2)}\n`, 'utf-8');
|
|
365
|
-
const mergeCompiledRules = async (family) => {
|
|
366
|
-
const existing = await loadCompiledRuleBundle(normalizedRepoPath, family, paths.rulesRoot);
|
|
367
|
-
const merged = new Map();
|
|
368
|
-
for (const rule of existing?.rules || []) {
|
|
369
|
-
merged.set(rule.id, rule);
|
|
370
|
-
}
|
|
371
|
-
for (const rule of compiledRules) {
|
|
372
|
-
merged.set(rule.id, rule);
|
|
373
|
-
}
|
|
374
|
-
return writeCompiledRuleBundle(paths.rulesRoot, family, [...merged.values()]);
|
|
375
|
-
};
|
|
376
|
-
const compiledPaths = {
|
|
377
|
-
analyze_rules: await mergeCompiledRules('analyze_rules'),
|
|
378
|
-
retrieval_rules: await mergeCompiledRules('retrieval_rules'),
|
|
379
|
-
verification_rules: await mergeCompiledRules('verification_rules'),
|
|
380
|
-
};
|
|
381
|
-
return {
|
|
382
|
-
catalog,
|
|
383
|
-
promotedFiles,
|
|
384
|
-
compiledPaths,
|
|
385
|
-
paths,
|
|
386
|
-
};
|
|
387
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|