cwtools-shared 0.2.2 → 0.2.3
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/knowledge/rules.js +73 -0
- package/package.json +1 -1
package/dist/knowledge/rules.js
CHANGED
|
@@ -49,6 +49,9 @@ async function queryRulesWithHost(host, args) {
|
|
|
49
49
|
: args.category === 'modifier'
|
|
50
50
|
? cache.modifiers
|
|
51
51
|
: cache.scopeChanges;
|
|
52
|
+
const dottedScopeChain = args.name && args.category === 'scope_change'
|
|
53
|
+
? buildDottedScopeChainRule(args.name, args.scope, cache.scopeChanges)
|
|
54
|
+
: undefined;
|
|
52
55
|
if (args.name) {
|
|
53
56
|
const needle = normalizeRuleNameQuery(args.name, args.category);
|
|
54
57
|
const filtered = rules
|
|
@@ -69,6 +72,12 @@ async function queryRulesWithHost(host, args) {
|
|
|
69
72
|
rules = filtered;
|
|
70
73
|
}
|
|
71
74
|
}
|
|
75
|
+
if (dottedScopeChain) {
|
|
76
|
+
rules = [
|
|
77
|
+
dottedScopeChain,
|
|
78
|
+
...rules.filter(rule => rule.name !== dottedScopeChain.name),
|
|
79
|
+
];
|
|
80
|
+
}
|
|
72
81
|
if (args.scope) {
|
|
73
82
|
const scope = args.scope.toLowerCase();
|
|
74
83
|
rules = rules.filter(rule => rule.scopes.length === 0
|
|
@@ -1519,6 +1528,70 @@ function normalizeRuleNameQuery(name, category) {
|
|
|
1519
1528
|
const parts = lowered.split('.').map(part => part.trim()).filter(Boolean);
|
|
1520
1529
|
return parts[parts.length - 1] ?? lowered;
|
|
1521
1530
|
}
|
|
1531
|
+
function buildDottedScopeChainRule(query, contextScope, scopeChanges) {
|
|
1532
|
+
const rawParts = query.trim().split('.').map(normalizeScopeName).filter(Boolean);
|
|
1533
|
+
if (rawParts.length < 2)
|
|
1534
|
+
return undefined;
|
|
1535
|
+
const contextPointers = new Set(['from', 'prev', 'root', 'this']);
|
|
1536
|
+
const first = rawParts[0];
|
|
1537
|
+
const startsWithPointer = contextPointers.has(first);
|
|
1538
|
+
const initialScope = startsWithPointer ? contextScope?.trim().toLowerCase() : first;
|
|
1539
|
+
const linkNames = rawParts.slice(1);
|
|
1540
|
+
if (!initialScope || linkNames.length === 0)
|
|
1541
|
+
return undefined;
|
|
1542
|
+
const hops = [];
|
|
1543
|
+
let currentScope = initialScope;
|
|
1544
|
+
for (const linkName of linkNames) {
|
|
1545
|
+
const link = scopeChanges.find(rule => rule.name.toLowerCase() === linkName
|
|
1546
|
+
&& !!rule.hardFacts?.pushScope
|
|
1547
|
+
&& scopeListContains(rule.hardFacts.supportedScopes ?? rule.scopes, currentScope));
|
|
1548
|
+
const outputScope = link?.hardFacts?.pushScope?.trim().toLowerCase();
|
|
1549
|
+
if (!link || !outputScope)
|
|
1550
|
+
return undefined;
|
|
1551
|
+
hops.push({ link, inputScope: currentScope, outputScope });
|
|
1552
|
+
currentScope = outputScope;
|
|
1553
|
+
}
|
|
1554
|
+
if (hops.length === 0)
|
|
1555
|
+
return undefined;
|
|
1556
|
+
const normalizedQuery = rawParts.join('.');
|
|
1557
|
+
const syntax = `${normalizedQuery} = { ... }`;
|
|
1558
|
+
const source = hops[0].link;
|
|
1559
|
+
const hopText = hops.map(hop => `${hop.inputScope}.${hop.link.name} -> ${hop.outputScope}`).join('; ');
|
|
1560
|
+
const linkHints = hops.map((hop) => ({
|
|
1561
|
+
text: `Dotted scope chain hop '${hop.inputScope}.${hop.link.name}' is legal because links.cwt declares '${hop.link.name}' with input scope '${hop.inputScope}' and output scope '${hop.outputScope}'.`,
|
|
1562
|
+
source: 'links.cwt',
|
|
1563
|
+
file: hop.link.sourceFile,
|
|
1564
|
+
line: hop.link.sourceLine,
|
|
1565
|
+
confidence: 'hint',
|
|
1566
|
+
}));
|
|
1567
|
+
return {
|
|
1568
|
+
name: normalizedQuery,
|
|
1569
|
+
description: `Legal dotted scope chain from ${initialScope} to ${currentScope}. Hops: ${hopText}.`,
|
|
1570
|
+
scopes: [initialScope],
|
|
1571
|
+
syntax,
|
|
1572
|
+
category: 'scope_change',
|
|
1573
|
+
sourceFile: source.sourceFile,
|
|
1574
|
+
sourceLine: source.sourceLine,
|
|
1575
|
+
hardFacts: {
|
|
1576
|
+
category: 'scope_change',
|
|
1577
|
+
supportedScopes: [initialScope],
|
|
1578
|
+
pushScope: currentScope,
|
|
1579
|
+
valueReferences: [],
|
|
1580
|
+
syntax,
|
|
1581
|
+
cwtSource: source.sourceFile && source.sourceLine
|
|
1582
|
+
? { file: source.sourceFile, line: source.sourceLine }
|
|
1583
|
+
: undefined,
|
|
1584
|
+
},
|
|
1585
|
+
semanticHints: linkHints.slice(0, 8),
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
function scopeListContains(scopes, scope) {
|
|
1589
|
+
const lowerScope = scope.toLowerCase();
|
|
1590
|
+
return scopes.some(candidate => {
|
|
1591
|
+
const lower = candidate.toLowerCase();
|
|
1592
|
+
return lower === lowerScope || lower === 'all' || lower === 'any';
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1522
1595
|
function scoreRuleNameMatch(name, needle) {
|
|
1523
1596
|
const lower = name.toLowerCase();
|
|
1524
1597
|
if (lower === needle)
|