scene-capability-engine 3.6.45 → 3.6.46
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/CHANGELOG.md +11 -0
- package/docs/releases/README.md +1 -0
- package/docs/releases/v3.6.46.md +23 -0
- package/docs/zh/releases/README.md +1 -0
- package/docs/zh/releases/v3.6.46.md +23 -0
- package/package.json +4 -2
- package/scripts/auto-strategy-router.js +231 -0
- package/scripts/capability-mapping-report.js +339 -0
- package/scripts/check-branding-consistency.js +140 -0
- package/scripts/check-sce-tracking.js +54 -0
- package/scripts/check-skip-allowlist.js +94 -0
- package/scripts/errorbook-registry-health-gate.js +172 -0
- package/scripts/errorbook-release-gate.js +132 -0
- package/scripts/failure-attribution-repair.js +317 -0
- package/scripts/git-managed-gate.js +464 -0
- package/scripts/interactive-approval-event-projection.js +400 -0
- package/scripts/interactive-approval-workflow.js +829 -0
- package/scripts/interactive-authorization-tier-evaluate.js +413 -0
- package/scripts/interactive-change-plan-gate.js +225 -0
- package/scripts/interactive-context-bridge.js +617 -0
- package/scripts/interactive-customization-loop.js +1690 -0
- package/scripts/interactive-dialogue-governance.js +842 -0
- package/scripts/interactive-feedback-log.js +253 -0
- package/scripts/interactive-flow-smoke.js +238 -0
- package/scripts/interactive-flow.js +1059 -0
- package/scripts/interactive-governance-report.js +1112 -0
- package/scripts/interactive-intent-build.js +707 -0
- package/scripts/interactive-loop-smoke.js +215 -0
- package/scripts/interactive-moqui-adapter.js +304 -0
- package/scripts/interactive-plan-build.js +426 -0
- package/scripts/interactive-runtime-policy-evaluate.js +495 -0
- package/scripts/interactive-work-order-build.js +552 -0
- package/scripts/matrix-regression-gate.js +167 -0
- package/scripts/moqui-core-regression-suite.js +397 -0
- package/scripts/moqui-lexicon-audit.js +651 -0
- package/scripts/moqui-matrix-remediation-phased-runner.js +865 -0
- package/scripts/moqui-matrix-remediation-queue.js +852 -0
- package/scripts/moqui-metadata-extract.js +1340 -0
- package/scripts/moqui-rebuild-gate.js +167 -0
- package/scripts/moqui-release-summary.js +729 -0
- package/scripts/moqui-standard-rebuild.js +1370 -0
- package/scripts/moqui-template-baseline-report.js +682 -0
- package/scripts/npm-package-runtime-asset-check.js +221 -0
- package/scripts/problem-closure-gate.js +441 -0
- package/scripts/release-asset-integrity-check.js +216 -0
- package/scripts/release-asset-nonempty-normalize.js +166 -0
- package/scripts/release-drift-evaluate.js +223 -0
- package/scripts/release-drift-signals.js +255 -0
- package/scripts/release-governance-snapshot-export.js +132 -0
- package/scripts/release-ops-weekly-summary.js +934 -0
- package/scripts/release-risk-remediation-bundle.js +315 -0
- package/scripts/release-weekly-ops-gate.js +423 -0
- package/scripts/state-migration-reconciliation-gate.js +110 -0
- package/scripts/state-storage-tiering-audit.js +337 -0
- package/scripts/steering-content-audit.js +393 -0
- package/scripts/symbol-evidence-locate.js +366 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_MAX_HITS = 10;
|
|
8
|
+
const DEFAULT_MIN_SCORE = 0.35;
|
|
9
|
+
const DEFAULT_MIN_RELIABLE_SCORE = 0.6;
|
|
10
|
+
const DEFAULT_EXTENSIONS = [
|
|
11
|
+
'.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx',
|
|
12
|
+
'.py', '.java', '.go', '.rb', '.php', '.cs',
|
|
13
|
+
'.json', '.yaml', '.yml', '.md'
|
|
14
|
+
];
|
|
15
|
+
const DEFAULT_IGNORED_DIRS = new Set([
|
|
16
|
+
'.git',
|
|
17
|
+
'.svn',
|
|
18
|
+
'.hg',
|
|
19
|
+
'node_modules',
|
|
20
|
+
'dist',
|
|
21
|
+
'build',
|
|
22
|
+
'.next',
|
|
23
|
+
'.cache'
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
function parseArgs(argv = []) {
|
|
27
|
+
const options = {
|
|
28
|
+
workspace: process.cwd(),
|
|
29
|
+
query: '',
|
|
30
|
+
queryFile: '',
|
|
31
|
+
maxHits: DEFAULT_MAX_HITS,
|
|
32
|
+
minScore: DEFAULT_MIN_SCORE,
|
|
33
|
+
minReliableScore: DEFAULT_MIN_RELIABLE_SCORE,
|
|
34
|
+
extensions: [...DEFAULT_EXTENSIONS],
|
|
35
|
+
strict: false,
|
|
36
|
+
json: false
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
40
|
+
const token = argv[index];
|
|
41
|
+
const next = argv[index + 1];
|
|
42
|
+
|
|
43
|
+
if (token === '--workspace' && next) {
|
|
44
|
+
options.workspace = next;
|
|
45
|
+
index += 1;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (token === '--query' && next) {
|
|
49
|
+
options.query = next;
|
|
50
|
+
index += 1;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (token === '--query-file' && next) {
|
|
54
|
+
options.queryFile = next;
|
|
55
|
+
index += 1;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (token === '--max-hits' && next) {
|
|
59
|
+
options.maxHits = Number(next);
|
|
60
|
+
index += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (token === '--min-score' && next) {
|
|
64
|
+
options.minScore = Number(next);
|
|
65
|
+
index += 1;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (token === '--min-reliable-score' && next) {
|
|
69
|
+
options.minReliableScore = Number(next);
|
|
70
|
+
index += 1;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (token === '--extensions' && next) {
|
|
74
|
+
options.extensions = next
|
|
75
|
+
.split(',')
|
|
76
|
+
.map((item) => item.trim().toLowerCase())
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.map((item) => (item.startsWith('.') ? item : `.${item}`));
|
|
79
|
+
index += 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (token === '--strict') {
|
|
83
|
+
options.strict = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (token === '--json') {
|
|
87
|
+
options.json = true;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (token === '--help' || token === '-h') {
|
|
91
|
+
printHelpAndExit(0);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!Number.isFinite(options.maxHits) || options.maxHits <= 0) {
|
|
96
|
+
throw new Error('--max-hits must be a positive number.');
|
|
97
|
+
}
|
|
98
|
+
if (!Number.isFinite(options.minScore) || options.minScore < 0 || options.minScore > 1) {
|
|
99
|
+
throw new Error('--min-score must be between 0 and 1.');
|
|
100
|
+
}
|
|
101
|
+
if (
|
|
102
|
+
!Number.isFinite(options.minReliableScore)
|
|
103
|
+
|| options.minReliableScore < 0
|
|
104
|
+
|| options.minReliableScore > 1
|
|
105
|
+
) {
|
|
106
|
+
throw new Error('--min-reliable-score must be between 0 and 1.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return options;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function printHelpAndExit(code) {
|
|
113
|
+
const lines = [
|
|
114
|
+
'Usage: node scripts/symbol-evidence-locate.js --query "<text>" [options]',
|
|
115
|
+
'',
|
|
116
|
+
'Options:',
|
|
117
|
+
' --workspace <path> Workspace root to search (default: current directory)',
|
|
118
|
+
' --query <text> Query text for symbol localization',
|
|
119
|
+
' --query-file <path> Query text file (fallback when --query absent)',
|
|
120
|
+
` --max-hits <n> Maximum hits to return (default: ${DEFAULT_MAX_HITS})`,
|
|
121
|
+
` --min-score <0-1> Minimum hit score (default: ${DEFAULT_MIN_SCORE})`,
|
|
122
|
+
` --min-reliable-score <0-1> Reliability threshold (default: ${DEFAULT_MIN_RELIABLE_SCORE})`,
|
|
123
|
+
` --extensions <csv> File extensions (default: ${DEFAULT_EXTENSIONS.join(',')})`,
|
|
124
|
+
' --strict Exit code 2 when no reliable evidence is found',
|
|
125
|
+
' --json Print JSON payload',
|
|
126
|
+
' -h, --help Show this help'
|
|
127
|
+
];
|
|
128
|
+
console.log(lines.join('\n'));
|
|
129
|
+
process.exit(code);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function resolveQuery(options) {
|
|
133
|
+
if (typeof options.query === 'string' && options.query.trim()) {
|
|
134
|
+
return options.query.trim();
|
|
135
|
+
}
|
|
136
|
+
if (typeof options.queryFile === 'string' && options.queryFile.trim()) {
|
|
137
|
+
const resolvedFile = path.resolve(options.workspace, options.queryFile);
|
|
138
|
+
if (!fs.existsSync(resolvedFile)) {
|
|
139
|
+
throw new Error(`query file not found: ${options.queryFile}`);
|
|
140
|
+
}
|
|
141
|
+
const content = fs.readFileSync(resolvedFile, 'utf8').trim();
|
|
142
|
+
if (!content) {
|
|
143
|
+
throw new Error(`query file is empty: ${options.queryFile}`);
|
|
144
|
+
}
|
|
145
|
+
return content;
|
|
146
|
+
}
|
|
147
|
+
throw new Error('query is required. Use --query or --query-file.');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function normalizeQueryTokens(query) {
|
|
151
|
+
return `${query || ''}`
|
|
152
|
+
.toLowerCase()
|
|
153
|
+
.replace(/[^a-z0-9_:\-\s]/g, ' ')
|
|
154
|
+
.split(/\s+/)
|
|
155
|
+
.map((token) => token.trim())
|
|
156
|
+
.filter((token) => token.length >= 2);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function listCandidateFiles(workspaceRoot, extensions) {
|
|
160
|
+
const resolvedRoot = path.resolve(workspaceRoot);
|
|
161
|
+
const extSet = new Set((extensions || []).map((item) => `${item}`.toLowerCase()));
|
|
162
|
+
const files = [];
|
|
163
|
+
const stack = [resolvedRoot];
|
|
164
|
+
|
|
165
|
+
while (stack.length > 0) {
|
|
166
|
+
const current = stack.pop();
|
|
167
|
+
let entries = [];
|
|
168
|
+
try {
|
|
169
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
170
|
+
} catch (_error) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const entry of entries) {
|
|
175
|
+
const fullPath = path.join(current, entry.name);
|
|
176
|
+
if (entry.isDirectory()) {
|
|
177
|
+
if (DEFAULT_IGNORED_DIRS.has(entry.name)) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
stack.push(fullPath);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!entry.isFile()) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
189
|
+
if (extSet.size > 0 && !extSet.has(ext)) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
files.push(fullPath);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return files;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function extractSymbolFromLine(line = '') {
|
|
200
|
+
const patterns = [
|
|
201
|
+
/\bfunction\s+([A-Za-z_$][\w$]*)\s*\(/,
|
|
202
|
+
/\bclass\s+([A-Za-z_$][\w$]*)\b/,
|
|
203
|
+
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/,
|
|
204
|
+
/\b([A-Za-z_$][\w$]*)\s*:\s*(?:async\s*)?\(/,
|
|
205
|
+
/\b([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(/
|
|
206
|
+
];
|
|
207
|
+
for (const pattern of patterns) {
|
|
208
|
+
const match = pattern.exec(line);
|
|
209
|
+
if (match && match[1]) {
|
|
210
|
+
return match[1];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return '';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function computeHitScore({ query, queryTokens, line, symbol }) {
|
|
217
|
+
const loweredLine = `${line || ''}`.toLowerCase();
|
|
218
|
+
const normalizedQuery = `${query || ''}`.toLowerCase().trim();
|
|
219
|
+
const normalizedSymbol = `${symbol || ''}`.toLowerCase();
|
|
220
|
+
if (queryTokens.length === 0) {
|
|
221
|
+
return 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const tokenMatches = queryTokens.filter((token) => loweredLine.includes(token)).length;
|
|
225
|
+
if (tokenMatches === 0) {
|
|
226
|
+
return 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const tokenCoverage = tokenMatches / queryTokens.length;
|
|
230
|
+
const hasPhraseMatch = normalizedQuery && loweredLine.includes(normalizedQuery);
|
|
231
|
+
const hasSymbolMatch = normalizedSymbol
|
|
232
|
+
&& queryTokens.some((token) => normalizedSymbol.includes(token) || token.includes(normalizedSymbol));
|
|
233
|
+
|
|
234
|
+
const rawScore = (tokenCoverage * 0.65) + (hasPhraseMatch ? 0.2 : 0) + (hasSymbolMatch ? 0.15 : 0);
|
|
235
|
+
return Math.max(0, Math.min(1, Number(rawScore.toFixed(4))));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function confidenceFromScore(score) {
|
|
239
|
+
if (!Number.isFinite(score) || score <= 0) {
|
|
240
|
+
return 'none';
|
|
241
|
+
}
|
|
242
|
+
if (score >= 0.8) {
|
|
243
|
+
return 'high';
|
|
244
|
+
}
|
|
245
|
+
if (score >= 0.6) {
|
|
246
|
+
return 'medium';
|
|
247
|
+
}
|
|
248
|
+
if (score >= 0.4) {
|
|
249
|
+
return 'low';
|
|
250
|
+
}
|
|
251
|
+
return 'none';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function locateSymbolEvidence({
|
|
255
|
+
workspace,
|
|
256
|
+
query,
|
|
257
|
+
maxHits = DEFAULT_MAX_HITS,
|
|
258
|
+
minScore = DEFAULT_MIN_SCORE,
|
|
259
|
+
minReliableScore = DEFAULT_MIN_RELIABLE_SCORE,
|
|
260
|
+
extensions = DEFAULT_EXTENSIONS
|
|
261
|
+
}) {
|
|
262
|
+
const workspaceRoot = path.resolve(workspace);
|
|
263
|
+
const queryTokens = normalizeQueryTokens(query);
|
|
264
|
+
const candidateFiles = listCandidateFiles(workspaceRoot, extensions);
|
|
265
|
+
const hits = [];
|
|
266
|
+
|
|
267
|
+
for (const filePath of candidateFiles) {
|
|
268
|
+
let content = '';
|
|
269
|
+
try {
|
|
270
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
271
|
+
} catch (_error) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const lines = content.split(/\r?\n/);
|
|
276
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
277
|
+
const line = lines[index];
|
|
278
|
+
const symbol = extractSymbolFromLine(line);
|
|
279
|
+
const score = computeHitScore({ query, queryTokens, line, symbol });
|
|
280
|
+
if (score < minScore) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const relativeFile = path.relative(workspaceRoot, filePath).replace(/\\/g, '/');
|
|
284
|
+
hits.push({
|
|
285
|
+
file: relativeFile || path.basename(filePath),
|
|
286
|
+
line: index + 1,
|
|
287
|
+
snippet: line.trim().slice(0, 240),
|
|
288
|
+
symbol: symbol || 'unknown',
|
|
289
|
+
score,
|
|
290
|
+
source: symbol ? 'symbol-heuristic' : 'text-grep'
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
hits.sort((a, b) => b.score - a.score || a.file.localeCompare(b.file) || (a.line - b.line));
|
|
296
|
+
const slicedHits = hits.slice(0, Math.max(1, Math.floor(maxHits)));
|
|
297
|
+
const reliableHits = slicedHits.filter((item) => item.score >= minReliableScore);
|
|
298
|
+
const topScore = slicedHits.length > 0 ? slicedHits[0].score : 0;
|
|
299
|
+
const confidence = confidenceFromScore(topScore);
|
|
300
|
+
const reliable = reliableHits.length > 0;
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
mode: 'symbol-evidence-locate',
|
|
304
|
+
query,
|
|
305
|
+
hits: slicedHits,
|
|
306
|
+
evidence: {
|
|
307
|
+
confidence,
|
|
308
|
+
reliable,
|
|
309
|
+
fallback_action: reliable ? 'allow_write' : 'block_high_risk_write',
|
|
310
|
+
advisory: reliable
|
|
311
|
+
? 'Symbol evidence is reliable; scoped code change can proceed.'
|
|
312
|
+
: 'No reliable symbol evidence found. Fallback to answer-only mode and block high-risk writes.'
|
|
313
|
+
},
|
|
314
|
+
summary: {
|
|
315
|
+
searched_files: candidateFiles.length,
|
|
316
|
+
candidate_files: candidateFiles.length,
|
|
317
|
+
total_hits: slicedHits.length,
|
|
318
|
+
reliable_hits: reliableHits.length
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function main() {
|
|
324
|
+
const options = parseArgs(process.argv.slice(2));
|
|
325
|
+
const query = resolveQuery(options);
|
|
326
|
+
const payload = locateSymbolEvidence({
|
|
327
|
+
workspace: options.workspace,
|
|
328
|
+
query,
|
|
329
|
+
maxHits: options.maxHits,
|
|
330
|
+
minScore: options.minScore,
|
|
331
|
+
minReliableScore: options.minReliableScore,
|
|
332
|
+
extensions: options.extensions
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
if (options.json) {
|
|
336
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
337
|
+
} else {
|
|
338
|
+
process.stdout.write(`symbol evidence confidence=${payload.evidence.confidence}\n`);
|
|
339
|
+
process.stdout.write(`hits=${payload.hits.length} reliable=${payload.summary.reliable_hits}\n`);
|
|
340
|
+
process.stdout.write(`fallback_action=${payload.evidence.fallback_action}\n`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (options.strict && payload.evidence.reliable !== true) {
|
|
344
|
+
process.exitCode = 2;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (require.main === module) {
|
|
349
|
+
try {
|
|
350
|
+
main();
|
|
351
|
+
} catch (error) {
|
|
352
|
+
console.error(`symbol-evidence-locate failed: ${error.message}`);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
module.exports = {
|
|
358
|
+
parseArgs,
|
|
359
|
+
resolveQuery,
|
|
360
|
+
normalizeQueryTokens,
|
|
361
|
+
listCandidateFiles,
|
|
362
|
+
extractSymbolFromLine,
|
|
363
|
+
computeHitScore,
|
|
364
|
+
confidenceFromScore,
|
|
365
|
+
locateSymbolEvidence
|
|
366
|
+
};
|