ucn 5.1.1 → 5.2.0
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/.claude/skills/ucn/SKILL.md +11 -4
- package/.claude/skills/ucn/references/commands.md +2 -2
- package/README.md +28 -13
- package/core/cache.js +28 -1
- package/core/callers.js +1242 -57
- package/core/graph-build.js +6 -0
- package/core/index-ir.js +3 -2
- package/core/ir.js +3 -2
- package/core/output/refactoring.js +1 -1
- package/core/project.js +32 -0
- package/core/search.js +9 -0
- package/core/verify.js +1084 -36
- package/languages/c-family.js +231 -9
- package/languages/csharp.js +14 -3
- package/languages/go.js +473 -71
- package/languages/javascript.js +85 -3
- package/languages/python.js +364 -95
- package/languages/rust.js +87 -4
- package/languages/utils.js +11 -0
- package/mcp/server.js +99 -103
- package/mcp/stdio-server.js +296 -0
- package/package.json +10 -8
package/core/graph-build.js
CHANGED
|
@@ -451,6 +451,12 @@ function buildInheritanceGraph(index) {
|
|
|
451
451
|
}
|
|
452
452
|
index.extendsGraph.get(symbol.name).push({
|
|
453
453
|
file: filePath,
|
|
454
|
+
// Per-def anchor (fix #300, attrs-measured): five
|
|
455
|
+
// function-local `class C2Slots(...)` defs in one file
|
|
456
|
+
// carry DIFFERENT parents — file-granular lookup returned
|
|
457
|
+
// the first entry for all of them. startLine lets scope-
|
|
458
|
+
// aware consumers pick the def the call site actually sees.
|
|
459
|
+
startLine: symbol.startLine,
|
|
454
460
|
parents: resolvedParents
|
|
455
461
|
});
|
|
456
462
|
|
package/core/index-ir.js
CHANGED
|
@@ -66,7 +66,7 @@ function createFileEntryFromIR({
|
|
|
66
66
|
|
|
67
67
|
const OPTIONAL_SYMBOL_FIELDS = Object.freeze([
|
|
68
68
|
'returnedFunctionResult', 'isFunctionVariable', 'paramTypes', 'isAsync',
|
|
69
|
-
'isGenerator', 'generics', 'extends', 'implements', 'indent', 'isNested',
|
|
69
|
+
'isGenerator', 'generics', 'genericBounds', 'extends', 'implements', 'indent', 'isNested',
|
|
70
70
|
'enclosingType', 'isMethod', 'receiver', 'memberType', 'fieldType',
|
|
71
71
|
'aliasOf', 'derefTarget', 'decorators', 'decoratorsWithArgs',
|
|
72
72
|
'annotationsWithArgs', 'attributesWithArgs', 'nameLine', 'traitImpl',
|
|
@@ -76,8 +76,9 @@ const OPTIONAL_SYMBOL_FIELDS = Object.freeze([
|
|
|
76
76
|
'lexicalScopeStartLine', 'lexicalScopeEndLine',
|
|
77
77
|
'returnTypeQualifier', 'macroNeverReturns', 'callbackParamTypes', 'iteratorItemType',
|
|
78
78
|
'returnedConcreteType', 'returnedConstructors', 'templateDependent',
|
|
79
|
+
'isSpecialization',
|
|
79
80
|
'linkage', 'functionLike', 'callableAlias', 'exportedAlias',
|
|
80
|
-
'aliasOwner', 'aliasMember',
|
|
81
|
+
'aliasOwner', 'aliasMember', 'macroParamEffects',
|
|
81
82
|
]);
|
|
82
83
|
|
|
83
84
|
function materializeSymbol(fileEntry, item) {
|
package/core/ir.js
CHANGED
|
@@ -43,7 +43,7 @@ function normalizeSymbol(symbol, family, language, kind, owner = null) {
|
|
|
43
43
|
};
|
|
44
44
|
const passthrough = [
|
|
45
45
|
'docstring', 'returnedFunctionResult', 'isFunctionVariable', 'paramTypes',
|
|
46
|
-
'isAsync', 'isGenerator', 'generics', 'extends', 'implements', 'indent',
|
|
46
|
+
'isAsync', 'isGenerator', 'generics', 'genericBounds', 'extends', 'implements', 'indent',
|
|
47
47
|
'isNested', 'enclosingType', 'isMethod', 'memberType', 'fieldType',
|
|
48
48
|
'aliasOf', 'derefTarget', 'decorators', 'decoratorsWithArgs',
|
|
49
49
|
'annotationsWithArgs', 'attributesWithArgs', 'nameLine', 'traitImpl',
|
|
@@ -53,8 +53,9 @@ function normalizeSymbol(symbol, family, language, kind, owner = null) {
|
|
|
53
53
|
'namespace', 'lexicalScopeStartLine', 'lexicalScopeEndLine',
|
|
54
54
|
'returnTypeQualifier', 'macroNeverReturns', 'callbackParamTypes', 'iteratorItemType',
|
|
55
55
|
'returnedConcreteType', 'returnedConstructors', 'templateDependent',
|
|
56
|
+
'isSpecialization',
|
|
56
57
|
'linkage', 'functionLike', 'callableAlias', 'exportedAlias',
|
|
57
|
-
'aliasOwner', 'aliasMember',
|
|
58
|
+
'aliasOwner', 'aliasMember', 'macroParamEffects',
|
|
58
59
|
];
|
|
59
60
|
for (const field of passthrough) {
|
|
60
61
|
if (symbol[field] !== undefined && symbol[field] !== null) {
|
|
@@ -70,7 +70,7 @@ function formatPlan(plan, options = {}) {
|
|
|
70
70
|
lines.push(` Files affected: ${plan.filesAffected}`);
|
|
71
71
|
if (plan.changeSummary) {
|
|
72
72
|
const summary = plan.changeSummary;
|
|
73
|
-
lines.push(` Definition ${summary.definitions}, calls
|
|
73
|
+
lines.push(` Definition ${summary.definitions}, calls ${summary.calls}, references ${summary.references || 0}, imports ${summary.imports}, exports ${summary.exports}; manual review required for ${summary.reviewRequired} of these changes`);
|
|
74
74
|
}
|
|
75
75
|
if (plan.unchangedSites > 0) {
|
|
76
76
|
lines.push(` ${plan.unchangedSites} existing call site${plan.unchangedSites === 1 ? '' : 's'} require no edit because the new parameter has a default.`);
|
package/core/project.js
CHANGED
|
@@ -444,6 +444,9 @@ class ProjectIndex {
|
|
|
444
444
|
this._completenessCache = null;
|
|
445
445
|
this._attrTypeCache = null;
|
|
446
446
|
this._computedDispatchBlindspots = null;
|
|
447
|
+
this._cppVisibleFilesCache?.clear();
|
|
448
|
+
this._cppTargetVisibilityCache?.clear();
|
|
449
|
+
this._cppMacroParamOutcomesCache?.clear();
|
|
447
450
|
// Endpoints cache (server routes / client requests / bridges) becomes
|
|
448
451
|
// stale when files change; clear on every rebuild.
|
|
449
452
|
this._endpointsCache = null;
|
|
@@ -589,6 +592,13 @@ class ProjectIndex {
|
|
|
589
592
|
return false;
|
|
590
593
|
}
|
|
591
594
|
|
|
595
|
+
// These query caches depend on project symbols and include closure.
|
|
596
|
+
// A changed file can alter either even before a full rebuild reaches
|
|
597
|
+
// its graph phase, so no prior call-identity verdict may survive.
|
|
598
|
+
this._cppVisibleFilesCache?.clear();
|
|
599
|
+
this._cppTargetVisibilityCache?.clear();
|
|
600
|
+
this._cppMacroParamOutcomesCache?.clear();
|
|
601
|
+
|
|
592
602
|
if (existing) {
|
|
593
603
|
this.removeFileSymbols(filePath);
|
|
594
604
|
}
|
|
@@ -942,6 +952,28 @@ class ProjectIndex {
|
|
|
942
952
|
return entries;
|
|
943
953
|
}
|
|
944
954
|
|
|
955
|
+
/**
|
|
956
|
+
* Def-exact inheritance parents (fix #300): resolve parents for ONE
|
|
957
|
+
* specific class definition by (file, startLine). Same-name classes in
|
|
958
|
+
* one file (function-local test classes) carry different parents; the
|
|
959
|
+
* file-granular lookup above conflates them. Returns null when no entry
|
|
960
|
+
* matches — for a known def that means it extends nothing (entries are
|
|
961
|
+
* only recorded for extends-bearing defs), which callers must treat as
|
|
962
|
+
* "no parents", never fall back to a sibling def's edges.
|
|
963
|
+
* @param {string} className
|
|
964
|
+
* @param {string} contextFile
|
|
965
|
+
* @param {number} defStartLine
|
|
966
|
+
* @returns {string[]|null}
|
|
967
|
+
*/
|
|
968
|
+
_getInheritanceParentsAt(className, contextFile, defStartLine) {
|
|
969
|
+
const entries = this.extendsGraph.get(className);
|
|
970
|
+
if (!entries || entries.length === 0) return null;
|
|
971
|
+
if (typeof entries[0] !== 'object' || entries[0].file === undefined) return null;
|
|
972
|
+
const match = entries.find(e => e.file === contextFile &&
|
|
973
|
+
e.startLine === defStartLine);
|
|
974
|
+
return match ? match.parents : null;
|
|
975
|
+
}
|
|
976
|
+
|
|
945
977
|
/**
|
|
946
978
|
* Resolve which file a class is defined in, preferring contextFile.
|
|
947
979
|
* Used during inheritance BFS to find grandparent chains.
|
package/core/search.js
CHANGED
|
@@ -352,6 +352,15 @@ function usages(index, name, options = {}) {
|
|
|
352
352
|
usageType: u.usageType,
|
|
353
353
|
isDefinition: false,
|
|
354
354
|
...(u.receiver && { receiver: u.receiver }),
|
|
355
|
+
// Refactoring internals need the exact AST token and
|
|
356
|
+
// receiver provenance, but the public usages JSON is a
|
|
357
|
+
// stable line-oriented inventory. Keep this opt-in so
|
|
358
|
+
// plan gains precision without changing that surface.
|
|
359
|
+
...(options.internalEvidence && {
|
|
360
|
+
...(Number.isInteger(u.column) && { column: u.column }),
|
|
361
|
+
...(u.receiverIsModule && { receiverIsModule: true }),
|
|
362
|
+
...(u.receiverLocalBinding && { receiverLocalBinding: true }),
|
|
363
|
+
}),
|
|
355
364
|
...(callerSym && {
|
|
356
365
|
callerName: callerSym.name,
|
|
357
366
|
callerStartLine: callerSym.startLine
|