canicode 0.12.2 → 0.12.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/cli/index.js +109 -36
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +86 -34
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +90 -35
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
- package/skills/canicode-roundtrip/SKILL.md +86 -0
- package/skills/canicode-roundtrip/helpers-bootstrap.js +1 -1
- package/skills/canicode-roundtrip/helpers-installer.js +2 -2
- package/skills/canicode-roundtrip/helpers.js +429 -0
- package/skills/cursor/canicode-roundtrip/SKILL.md +86 -0
- package/skills/cursor/canicode-roundtrip/helpers-bootstrap.js +1 -1
- package/skills/cursor/canicode-roundtrip/helpers-installer.js +2 -2
- package/skills/cursor/canicode-roundtrip/helpers.js +429 -0
package/dist/mcp/server.js
CHANGED
|
@@ -852,6 +852,7 @@ var RuleEngine = class {
|
|
|
852
852
|
analysisState,
|
|
853
853
|
scope,
|
|
854
854
|
rootNodeType,
|
|
855
|
+
rootNode,
|
|
855
856
|
void 0,
|
|
856
857
|
void 0
|
|
857
858
|
);
|
|
@@ -888,7 +889,7 @@ var RuleEngine = class {
|
|
|
888
889
|
/**
|
|
889
890
|
* Recursively traverse the tree and run rules
|
|
890
891
|
*/
|
|
891
|
-
traverseAndCheck(node, file, rules, maxDepth, issues, failedRules, depth, path, ancestorTypes, componentDepth, analysisState, scope, rootNodeType, parent, siblings) {
|
|
892
|
+
traverseAndCheck(node, file, rules, maxDepth, issues, failedRules, depth, path, ancestorTypes, componentDepth, analysisState, scope, rootNodeType, analysisRoot, parent, siblings) {
|
|
892
893
|
const nodePath = [...path, node.name];
|
|
893
894
|
const isComponentBoundary = node.type === "COMPONENT" || node.type === "COMPONENT_SET" || node.type === "INSTANCE";
|
|
894
895
|
const currentComponentDepth = isComponentBoundary ? 0 : componentDepth;
|
|
@@ -911,6 +912,7 @@ var RuleEngine = class {
|
|
|
911
912
|
analysisState,
|
|
912
913
|
scope,
|
|
913
914
|
rootNodeType,
|
|
915
|
+
analysisRoot,
|
|
914
916
|
findAcknowledgment: (nodeId, ruleId) => acknowledgmentsByKey.get(`${normalizeNodeId(nodeId)}::${ruleId}`)
|
|
915
917
|
};
|
|
916
918
|
for (const rule of rules) {
|
|
@@ -960,6 +962,7 @@ var RuleEngine = class {
|
|
|
960
962
|
analysisState,
|
|
961
963
|
scope,
|
|
962
964
|
rootNodeType,
|
|
965
|
+
analysisRoot,
|
|
963
966
|
node,
|
|
964
967
|
node.children
|
|
965
968
|
);
|
|
@@ -1943,7 +1946,7 @@ function computeApplyContext(violation, instanceContext) {
|
|
|
1943
1946
|
}
|
|
1944
1947
|
|
|
1945
1948
|
// package.json
|
|
1946
|
-
var version = "0.12.
|
|
1949
|
+
var version = "0.12.3";
|
|
1947
1950
|
|
|
1948
1951
|
// src/core/engine/scoring.ts
|
|
1949
1952
|
var GRADE_ORDER = ["S", "A+", "A", "B+", "B", "C+", "C", "D", "F"];
|
|
@@ -2655,7 +2658,11 @@ function mapToQuestion(issue, file) {
|
|
|
2655
2658
|
...applyContext.annotationProperties !== void 0 ? { annotationProperties: applyContext.annotationProperties } : {},
|
|
2656
2659
|
...suggestedName !== void 0 ? { suggestedName } : {},
|
|
2657
2660
|
isInstanceChild: applyContext.isInstanceChild,
|
|
2658
|
-
...applyContext.sourceChildId !== void 0 ? { sourceChildId: applyContext.sourceChildId } : {}
|
|
2661
|
+
...applyContext.sourceChildId !== void 0 ? { sourceChildId: applyContext.sourceChildId } : {},
|
|
2662
|
+
// #560 / Phase 3 delta 4a: thread groupMembers through from the
|
|
2663
|
+
// violation. Currently only populated by `missing-component`
|
|
2664
|
+
// Stage 3; non-group rules pass undefined and the field is omitted.
|
|
2665
|
+
...issue.violation.groupMembers !== void 0 ? { groupMembers: issue.violation.groupMembers } : {}
|
|
2659
2666
|
};
|
|
2660
2667
|
}
|
|
2661
2668
|
function deduplicateBySourceComponent(questions) {
|
|
@@ -3909,7 +3916,24 @@ var EVENTS = {
|
|
|
3909
3916
|
// has a single place to wire it up.
|
|
3910
3917
|
ROUNDTRIP_DEFINITION_WRITE_SKIPPED: `${EVENT_PREFIX}roundtrip_definition_write_skipped`,
|
|
3911
3918
|
/** CLI `canicode roundtrip-tally` completed successfully. */
|
|
3912
|
-
ROUNDTRIP_TALLY: `${EVENT_PREFIX}roundtrip_tally
|
|
3919
|
+
ROUNDTRIP_TALLY: `${EVENT_PREFIX}roundtrip_tally`,
|
|
3920
|
+
/**
|
|
3921
|
+
* Phase 3 (#508 / ADR-023): `applyComponentize` outcome — either a
|
|
3922
|
+
* successful `createComponentFromNode` call or one of the guard rejections
|
|
3923
|
+
* (instance-child, free-form parent, error) that route to the Strategy C
|
|
3924
|
+
* annotate-fallback. Surfaces under `props.outcome` so a Node-side reader
|
|
3925
|
+
* can split the funnel.
|
|
3926
|
+
*/
|
|
3927
|
+
ROUNDTRIP_COMPONENTIZE: `${EVENT_PREFIX}roundtrip_componentize`,
|
|
3928
|
+
/**
|
|
3929
|
+
* Phase 3 (#508 / ADR-023, #554): `applyReplaceWithInstance` outcome —
|
|
3930
|
+
* either a successful instance swap (`outcome: "replaced"`) or one of the
|
|
3931
|
+
* guard rejections (`"skipped-free-form-parent"`, `"skipped-prereq-missing"`,
|
|
3932
|
+
* `"error"`). The primitive pairs with `ROUNDTRIP_COMPONENTIZE` so a
|
|
3933
|
+
* Node-side reader can correlate componentize+swap pairs across a single
|
|
3934
|
+
* Phase 3 batch.
|
|
3935
|
+
*/
|
|
3936
|
+
ROUNDTRIP_REPLACE_WITH_INSTANCE: `${EVENT_PREFIX}roundtrip_replace_with_instance`
|
|
3913
3937
|
};
|
|
3914
3938
|
|
|
3915
3939
|
// src/core/monitoring/capture.ts
|
|
@@ -4309,8 +4333,8 @@ var missingComponentMsg = {
|
|
|
4309
4333
|
message: `"${name}" appears ${count} times`,
|
|
4310
4334
|
suggestion: `Extract as a reusable component`
|
|
4311
4335
|
}),
|
|
4312
|
-
structureRepetition: (name,
|
|
4313
|
-
message: `"${name}" and ${
|
|
4336
|
+
structureRepetition: (name, matchCount) => ({
|
|
4337
|
+
message: `"${name}" and ${matchCount} other frame(s) share the same internal structure`,
|
|
4314
4338
|
suggestion: `Extract a shared component from the repeated structure`
|
|
4315
4339
|
}),
|
|
4316
4340
|
styleOverride: (componentName, overrides) => ({
|
|
@@ -4921,6 +4945,41 @@ function getSeenStage1(context) {
|
|
|
4921
4945
|
function getSeenStage4(context) {
|
|
4922
4946
|
return getAnalysisState(context, SEEN_STAGE4_KEY, () => /* @__PURE__ */ new Set());
|
|
4923
4947
|
}
|
|
4948
|
+
function stage3GroupsKey(maxDepth) {
|
|
4949
|
+
return `missing-component:stage3Groups:depth=${maxDepth}`;
|
|
4950
|
+
}
|
|
4951
|
+
function nodeQualifiesForStage3(node, parent, insideInstance) {
|
|
4952
|
+
if (insideInstance) return false;
|
|
4953
|
+
if (node.type !== "FRAME") return false;
|
|
4954
|
+
if (parent?.type === "COMPONENT_SET") return false;
|
|
4955
|
+
if (!node.children || node.children.length === 0) return false;
|
|
4956
|
+
return true;
|
|
4957
|
+
}
|
|
4958
|
+
function buildStage3Groups(root, maxFingerprintDepth) {
|
|
4959
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4960
|
+
const walk = (node, parent, ancestorIsInstance) => {
|
|
4961
|
+
const insideInstance = ancestorIsInstance || node.type === "INSTANCE";
|
|
4962
|
+
if (nodeQualifiesForStage3(node, parent, ancestorIsInstance)) {
|
|
4963
|
+
const fp = buildFingerprint(node, maxFingerprintDepth);
|
|
4964
|
+
const existing = groups.get(fp);
|
|
4965
|
+
if (existing) existing.memberIds.push(node.id);
|
|
4966
|
+
else groups.set(fp, { memberIds: [node.id] });
|
|
4967
|
+
}
|
|
4968
|
+
if (node.children) {
|
|
4969
|
+
for (const child of node.children) walk(child, node, insideInstance);
|
|
4970
|
+
}
|
|
4971
|
+
};
|
|
4972
|
+
walk(root, null, false);
|
|
4973
|
+
return groups;
|
|
4974
|
+
}
|
|
4975
|
+
function getStage3Groups(context, maxFingerprintDepth) {
|
|
4976
|
+
const root = context.analysisRoot ?? context.file.document;
|
|
4977
|
+
return getAnalysisState(
|
|
4978
|
+
context,
|
|
4979
|
+
stage3GroupsKey(maxFingerprintDepth),
|
|
4980
|
+
() => buildStage3Groups(root, maxFingerprintDepth)
|
|
4981
|
+
);
|
|
4982
|
+
}
|
|
4924
4983
|
var missingComponentDef = {
|
|
4925
4984
|
id: "missing-component",
|
|
4926
4985
|
name: "Missing Component",
|
|
@@ -4935,7 +4994,8 @@ var missingComponentCheck = (node, context, options) => {
|
|
|
4935
4994
|
const matchingComponent = Object.values(components).find(
|
|
4936
4995
|
(c) => c.name.toLowerCase() === node.name.toLowerCase()
|
|
4937
4996
|
);
|
|
4938
|
-
const
|
|
4997
|
+
const scopeRoot = context.analysisRoot ?? context.file.document;
|
|
4998
|
+
const frameNames = collectFrameNames(scopeRoot);
|
|
4939
4999
|
const sameNameFrames = frameNames.get(node.name);
|
|
4940
5000
|
const firstFrame = sameNameFrames?.[0];
|
|
4941
5001
|
if (matchingComponent) {
|
|
@@ -4963,37 +5023,32 @@ var missingComponentCheck = (node, context, options) => {
|
|
|
4963
5023
|
};
|
|
4964
5024
|
}
|
|
4965
5025
|
}
|
|
4966
|
-
if (isInsideInstance(context))
|
|
4967
|
-
|
|
4968
|
-
|
|
5026
|
+
if (!nodeQualifiesForStage3(node, context.parent ?? null, isInsideInstance(context))) {
|
|
5027
|
+
return null;
|
|
5028
|
+
}
|
|
4969
5029
|
const structureMinRepetitions = options?.["structureMinRepetitions"] ?? getRuleOption("missing-component", "structureMinRepetitions", 2);
|
|
4970
5030
|
const maxFingerprintDepth = options?.["maxFingerprintDepth"] ?? getRuleOption("missing-component", "maxFingerprintDepth", 3);
|
|
5031
|
+
const groups = getStage3Groups(context, maxFingerprintDepth);
|
|
4971
5032
|
const fingerprint = buildFingerprint(node, maxFingerprintDepth);
|
|
4972
|
-
const
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
);
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
)
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
nodePath: context.path.join(" > "),
|
|
4992
|
-
...missingComponentMsg.structureRepetition(node.name, count - 1)
|
|
4993
|
-
};
|
|
4994
|
-
}
|
|
4995
|
-
}
|
|
4996
|
-
return null;
|
|
5033
|
+
const group = groups.get(fingerprint);
|
|
5034
|
+
if (!group) return null;
|
|
5035
|
+
if (group.memberIds.length < structureMinRepetitions) return null;
|
|
5036
|
+
if (group.memberIds[0] !== node.id) return null;
|
|
5037
|
+
return {
|
|
5038
|
+
ruleId: missingComponentDef.id,
|
|
5039
|
+
subType: "structure-repetition",
|
|
5040
|
+
nodeId: node.id,
|
|
5041
|
+
nodePath: context.path.join(" > "),
|
|
5042
|
+
...missingComponentMsg.structureRepetition(
|
|
5043
|
+
node.name,
|
|
5044
|
+
group.memberIds.length - 1
|
|
5045
|
+
),
|
|
5046
|
+
// #560 / delta 4a: surface the full group so the apply step can drive
|
|
5047
|
+
// the componentize+swap loop from a single user answer. `nodeId` is
|
|
5048
|
+
// the document-order first member; the rest are siblings or cross-
|
|
5049
|
+
// parent matches found by the scope-wide pass (#557).
|
|
5050
|
+
groupMembers: [...group.memberIds]
|
|
5051
|
+
};
|
|
4997
5052
|
}
|
|
4998
5053
|
if (node.type === "INSTANCE" && node.componentId) {
|
|
4999
5054
|
const seenStage4 = getSeenStage4(context);
|