canicode 0.10.3 → 0.10.4
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/README.md +35 -30
- package/dist/cli/index.js +345 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +257 -3
- package/dist/index.js +83 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +144 -23
- package/dist/mcp/server.js.map +1 -1
- package/package.json +2 -1
- package/skills/canicode-gotchas/SKILL.md +66 -28
- package/skills/canicode-roundtrip/SKILL.md +180 -79
- package/skills/canicode-roundtrip/helpers.js +218 -2
package/dist/mcp/server.js
CHANGED
|
@@ -615,6 +615,14 @@ function defineRule(rule) {
|
|
|
615
615
|
ruleRegistry.register(rule);
|
|
616
616
|
return rule;
|
|
617
617
|
}
|
|
618
|
+
var AcknowledgmentSchema = z.object({
|
|
619
|
+
nodeId: z.string(),
|
|
620
|
+
ruleId: z.string()
|
|
621
|
+
});
|
|
622
|
+
z.array(AcknowledgmentSchema);
|
|
623
|
+
function normalizeNodeId(id) {
|
|
624
|
+
return id.replace(/-/g, ":");
|
|
625
|
+
}
|
|
618
626
|
|
|
619
627
|
// src/core/engine/rule-engine.ts
|
|
620
628
|
function calculateMaxDepth(node, currentDepth = 0) {
|
|
@@ -665,6 +673,7 @@ var RuleEngine = class {
|
|
|
665
673
|
targetNodeId;
|
|
666
674
|
excludeNamePattern;
|
|
667
675
|
excludeNodeTypes;
|
|
676
|
+
acknowledgments;
|
|
668
677
|
constructor(options = {}) {
|
|
669
678
|
this.configs = options.configs ?? RULE_CONFIGS;
|
|
670
679
|
this.enabledRuleIds = options.enabledRules ? new Set(options.enabledRules) : null;
|
|
@@ -672,6 +681,11 @@ var RuleEngine = class {
|
|
|
672
681
|
this.targetNodeId = options.targetNodeId;
|
|
673
682
|
this.excludeNamePattern = options.excludeNodeNames && options.excludeNodeNames.length > 0 ? new RegExp(`\\b(${options.excludeNodeNames.join("|")})\\b`, "i") : null;
|
|
674
683
|
this.excludeNodeTypes = options.excludeNodeTypes && options.excludeNodeTypes.length > 0 ? new Set(options.excludeNodeTypes) : null;
|
|
684
|
+
this.acknowledgments = new Set(
|
|
685
|
+
(options.acknowledgments ?? []).map(
|
|
686
|
+
(a) => `${normalizeNodeId(a.nodeId)}::${a.ruleId}`
|
|
687
|
+
)
|
|
688
|
+
);
|
|
675
689
|
}
|
|
676
690
|
/**
|
|
677
691
|
* Analyze a Figma file and return issues
|
|
@@ -706,6 +720,14 @@ var RuleEngine = class {
|
|
|
706
720
|
void 0,
|
|
707
721
|
void 0
|
|
708
722
|
);
|
|
723
|
+
if (this.acknowledgments.size > 0) {
|
|
724
|
+
for (const issue of issues) {
|
|
725
|
+
const key = `${normalizeNodeId(issue.violation.nodeId)}::${issue.violation.ruleId}`;
|
|
726
|
+
if (this.acknowledgments.has(key)) {
|
|
727
|
+
issue.acknowledged = true;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
709
731
|
return {
|
|
710
732
|
file,
|
|
711
733
|
issues,
|
|
@@ -1708,8 +1730,6 @@ function resolveTargetProperty(ruleId, subType) {
|
|
|
1708
1730
|
if (subType === "horizontal") return "layoutSizingHorizontal";
|
|
1709
1731
|
return ["layoutSizingHorizontal", "layoutSizingVertical"];
|
|
1710
1732
|
case "missing-size-constraint":
|
|
1711
|
-
if (subType === "wrap") return "minWidth";
|
|
1712
|
-
if (subType === "max-width") return "maxWidth";
|
|
1713
1733
|
return ["minWidth", "maxWidth"];
|
|
1714
1734
|
case "irregular-spacing":
|
|
1715
1735
|
if (subType === "gap") return "itemSpacing";
|
|
@@ -1753,7 +1773,7 @@ function computeApplyContext(violation, instanceContext) {
|
|
|
1753
1773
|
}
|
|
1754
1774
|
|
|
1755
1775
|
// package.json
|
|
1756
|
-
var version = "0.10.
|
|
1776
|
+
var version = "0.10.4";
|
|
1757
1777
|
|
|
1758
1778
|
// src/core/engine/scoring.ts
|
|
1759
1779
|
function computeTotalScorePerCategory(configs) {
|
|
@@ -1809,7 +1829,8 @@ function calculateScores(result, configs) {
|
|
|
1809
1829
|
uniqueRulesPerCategory.get(category).add(ruleId);
|
|
1810
1830
|
ruleScorePerCategory.get(category).set(ruleId, Math.abs(issue.config.score));
|
|
1811
1831
|
const ruleCountMap = ruleIssueCountPerCategory.get(category);
|
|
1812
|
-
|
|
1832
|
+
const weight = issue.acknowledged === true ? 0.5 : 1;
|
|
1833
|
+
ruleCountMap.set(ruleId, (ruleCountMap.get(ruleId) ?? 0) + weight);
|
|
1813
1834
|
}
|
|
1814
1835
|
for (const category of CATEGORIES) {
|
|
1815
1836
|
const ruleCountMap = ruleIssueCountPerCategory.get(category);
|
|
@@ -1856,7 +1877,8 @@ function calculateScores(result, configs) {
|
|
|
1856
1877
|
risk: 0,
|
|
1857
1878
|
missingInfo: 0,
|
|
1858
1879
|
suggestion: 0,
|
|
1859
|
-
nodeCount
|
|
1880
|
+
nodeCount,
|
|
1881
|
+
acknowledgedCount: 0
|
|
1860
1882
|
};
|
|
1861
1883
|
for (const issue of result.issues) {
|
|
1862
1884
|
switch (issue.config.severity) {
|
|
@@ -1873,6 +1895,7 @@ function calculateScores(result, configs) {
|
|
|
1873
1895
|
summary.suggestion++;
|
|
1874
1896
|
break;
|
|
1875
1897
|
}
|
|
1898
|
+
if (issue.acknowledged === true) summary.acknowledgedCount++;
|
|
1876
1899
|
}
|
|
1877
1900
|
return {
|
|
1878
1901
|
overall: {
|
|
@@ -1923,7 +1946,14 @@ function formatScoreSummary(report) {
|
|
|
1923
1946
|
lines.push(` Risk: ${report.summary.risk}`);
|
|
1924
1947
|
lines.push(` Missing Info: ${report.summary.missingInfo}`);
|
|
1925
1948
|
lines.push(` Suggestion: ${report.summary.suggestion}`);
|
|
1926
|
-
|
|
1949
|
+
if (report.summary.acknowledgedCount > 0) {
|
|
1950
|
+
const unaddressed = report.summary.totalIssues - report.summary.acknowledgedCount;
|
|
1951
|
+
lines.push(
|
|
1952
|
+
` Total: ${report.summary.totalIssues} (${report.summary.acknowledgedCount} acknowledged via canicode annotations / ${unaddressed} unaddressed)`
|
|
1953
|
+
);
|
|
1954
|
+
} else {
|
|
1955
|
+
lines.push(` Total: ${report.summary.totalIssues}`);
|
|
1956
|
+
}
|
|
1927
1957
|
return lines.join("\n");
|
|
1928
1958
|
}
|
|
1929
1959
|
function buildResultJson(fileName, result, scores, options) {
|
|
@@ -1947,17 +1977,20 @@ function buildResultJson(fileName, result, scores, options) {
|
|
|
1947
1977
|
...applyContext.annotationProperties !== void 0 ? { annotationProperties: applyContext.annotationProperties } : {},
|
|
1948
1978
|
...suggestedName !== void 0 ? { suggestedName } : {},
|
|
1949
1979
|
isInstanceChild: applyContext.isInstanceChild,
|
|
1950
|
-
...applyContext.sourceChildId !== void 0 ? { sourceChildId: applyContext.sourceChildId } : {}
|
|
1980
|
+
...applyContext.sourceChildId !== void 0 ? { sourceChildId: applyContext.sourceChildId } : {},
|
|
1981
|
+
...issue.acknowledged === true ? { acknowledged: true } : {}
|
|
1951
1982
|
};
|
|
1952
1983
|
});
|
|
1953
1984
|
const json = {
|
|
1954
1985
|
version,
|
|
1955
1986
|
analyzedAt: result.analyzedAt,
|
|
1956
1987
|
...options?.fileKey && { fileKey: options.fileKey },
|
|
1988
|
+
...options?.designKey && { designKey: options.designKey },
|
|
1957
1989
|
fileName,
|
|
1958
1990
|
nodeCount: result.nodeCount,
|
|
1959
1991
|
maxDepth: result.maxDepth,
|
|
1960
1992
|
issueCount: result.issues.length,
|
|
1993
|
+
acknowledgedCount: scores.summary.acknowledgedCount,
|
|
1961
1994
|
isReadyForCodeGen: isReadyForCodeGen(scores.overall.grade),
|
|
1962
1995
|
blockingIssueCount: scores.summary.blocking,
|
|
1963
1996
|
scores: {
|
|
@@ -2084,9 +2117,72 @@ var GOTCHA_QUESTIONS = {
|
|
|
2084
2117
|
}
|
|
2085
2118
|
};
|
|
2086
2119
|
|
|
2120
|
+
// src/core/gotcha/group-and-batch-questions.ts
|
|
2121
|
+
var BATCHABLE_RULE_IDS = [
|
|
2122
|
+
"missing-size-constraint",
|
|
2123
|
+
"irregular-spacing",
|
|
2124
|
+
"no-auto-layout",
|
|
2125
|
+
"fixed-size-in-auto-layout"
|
|
2126
|
+
];
|
|
2127
|
+
var BATCHABLE_SET = new Set(BATCHABLE_RULE_IDS);
|
|
2128
|
+
var NO_SOURCE_SENTINEL = "_no-source";
|
|
2129
|
+
function groupAndBatchSurveyQuestions(questions) {
|
|
2130
|
+
if (questions.length === 0) {
|
|
2131
|
+
return { groups: [] };
|
|
2132
|
+
}
|
|
2133
|
+
const sorted = [...questions].sort(compareQuestions);
|
|
2134
|
+
const groups = [];
|
|
2135
|
+
let currentGroup = null;
|
|
2136
|
+
let lastGroupKey = null;
|
|
2137
|
+
for (const question of sorted) {
|
|
2138
|
+
const groupKey = sourceComponentKey(question);
|
|
2139
|
+
if (currentGroup === null || groupKey !== lastGroupKey) {
|
|
2140
|
+
currentGroup = {
|
|
2141
|
+
instanceContext: question.instanceContext ?? null,
|
|
2142
|
+
batches: []
|
|
2143
|
+
};
|
|
2144
|
+
groups.push(currentGroup);
|
|
2145
|
+
lastGroupKey = groupKey;
|
|
2146
|
+
}
|
|
2147
|
+
pushIntoBatch(currentGroup, question);
|
|
2148
|
+
}
|
|
2149
|
+
return { groups };
|
|
2150
|
+
}
|
|
2151
|
+
function compareQuestions(a, b) {
|
|
2152
|
+
const aKey = sourceComponentKey(a);
|
|
2153
|
+
const bKey = sourceComponentKey(b);
|
|
2154
|
+
if (aKey !== bKey) {
|
|
2155
|
+
if (aKey === NO_SOURCE_SENTINEL) return 1;
|
|
2156
|
+
if (bKey === NO_SOURCE_SENTINEL) return -1;
|
|
2157
|
+
return aKey.localeCompare(bKey);
|
|
2158
|
+
}
|
|
2159
|
+
if (a.ruleId !== b.ruleId) return a.ruleId.localeCompare(b.ruleId);
|
|
2160
|
+
if (a.nodeName !== b.nodeName) return a.nodeName.localeCompare(b.nodeName);
|
|
2161
|
+
return a.nodeId.localeCompare(b.nodeId);
|
|
2162
|
+
}
|
|
2163
|
+
function sourceComponentKey(question) {
|
|
2164
|
+
return question.instanceContext?.sourceComponentId ?? NO_SOURCE_SENTINEL;
|
|
2165
|
+
}
|
|
2166
|
+
function pushIntoBatch(group, question) {
|
|
2167
|
+
const sceneWeight = Math.max(question.replicas ?? 1, 1);
|
|
2168
|
+
const isBatchable = BATCHABLE_SET.has(question.ruleId);
|
|
2169
|
+
const last = group.batches.at(-1);
|
|
2170
|
+
if (last !== void 0 && last.ruleId === question.ruleId && isBatchable && last.batchable) {
|
|
2171
|
+
last.questions.push(question);
|
|
2172
|
+
last.totalScenes += sceneWeight;
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
group.batches.push({
|
|
2176
|
+
ruleId: question.ruleId,
|
|
2177
|
+
batchable: isBatchable,
|
|
2178
|
+
questions: [question],
|
|
2179
|
+
totalScenes: sceneWeight
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2087
2183
|
// src/core/gotcha/survey-generator.ts
|
|
2088
2184
|
var NODE_PATH_SEPARATOR = " > ";
|
|
2089
|
-
function generateGotchaSurvey(result, scores) {
|
|
2185
|
+
function generateGotchaSurvey(result, scores, options = {}) {
|
|
2090
2186
|
const grade = scores.overall.grade;
|
|
2091
2187
|
const relevantIssues = result.issues.filter(
|
|
2092
2188
|
(issue) => issue.config.severity === "blocking" || issue.config.severity === "risk"
|
|
@@ -2095,16 +2191,23 @@ function generateGotchaSurvey(result, scores) {
|
|
|
2095
2191
|
const sorted = stableSortBySeverity(deduped);
|
|
2096
2192
|
const mapped = sorted.map((issue) => mapToQuestion(issue, result.file)).filter((q) => q !== null);
|
|
2097
2193
|
const questions = deduplicateBySourceComponent(mapped);
|
|
2194
|
+
const groupedQuestions = groupAndBatchSurveyQuestions(questions);
|
|
2098
2195
|
return {
|
|
2099
2196
|
designGrade: grade,
|
|
2100
2197
|
isReadyForCodeGen: isReadyForCodeGen(grade),
|
|
2101
|
-
questions
|
|
2198
|
+
questions,
|
|
2199
|
+
groupedQuestions,
|
|
2200
|
+
designKey: options.designKey ?? ""
|
|
2102
2201
|
};
|
|
2103
2202
|
}
|
|
2104
2203
|
function deduplicateSiblingIssues(issues) {
|
|
2105
2204
|
const seen = /* @__PURE__ */ new Set();
|
|
2106
2205
|
const result = [];
|
|
2107
2206
|
for (const issue of issues) {
|
|
2207
|
+
if (isInstanceChildNodeId(issue.violation.nodeId)) {
|
|
2208
|
+
result.push(issue);
|
|
2209
|
+
continue;
|
|
2210
|
+
}
|
|
2108
2211
|
const parentPath = getParentPath(issue.violation.nodePath);
|
|
2109
2212
|
const key = `${parentPath}||${issue.violation.ruleId}`;
|
|
2110
2213
|
if (!seen.has(key)) {
|
|
@@ -3539,6 +3642,18 @@ function shutdownMonitoring() {
|
|
|
3539
3642
|
// src/core/monitoring/keys.ts
|
|
3540
3643
|
var POSTHOG_API_KEY = "phc_rBFeG140KqJLpUnlpYDEFgdMM6JozZeqQsf9twXf5Dq" ;
|
|
3541
3644
|
var SENTRY_DSN = "https://80a836a8300b25f17ef5bbf23afb5b3a@o4511080656207872.ingest.us.sentry.io/4511080661319680" ;
|
|
3645
|
+
function isFigmaUrl2(input) {
|
|
3646
|
+
return input.includes("figma.com/");
|
|
3647
|
+
}
|
|
3648
|
+
z.string();
|
|
3649
|
+
function computeDesignKey(input) {
|
|
3650
|
+
if (isFigmaUrl2(input)) {
|
|
3651
|
+
const { fileKey, nodeId } = parseFigmaUrl(input);
|
|
3652
|
+
if (!nodeId) return fileKey;
|
|
3653
|
+
return `${fileKey}#${nodeId.replace(/-/g, ":")}`;
|
|
3654
|
+
}
|
|
3655
|
+
return resolve(input);
|
|
3656
|
+
}
|
|
3542
3657
|
|
|
3543
3658
|
// src/core/rules/node-semantics.ts
|
|
3544
3659
|
function isContainerNode(node) {
|
|
@@ -4645,6 +4760,7 @@ var inconsistentNamingConventionCheck = (node, context) => {
|
|
|
4645
4760
|
if (nodeConvention && nodeConvention !== dominantConvention && maxCount >= 2) {
|
|
4646
4761
|
if (isCompatible(nodeConvention, dominantConvention, node.name)) return null;
|
|
4647
4762
|
const suggested = convertName(node.name, dominantConvention);
|
|
4763
|
+
if (suggested === node.name) return null;
|
|
4648
4764
|
return {
|
|
4649
4765
|
ruleId: inconsistentNamingConventionDef.id,
|
|
4650
4766
|
nodeId: node.id,
|
|
@@ -4882,7 +4998,9 @@ Provide a Figma URL or fixture path via the input parameter. Requires FIGMA_TOKE
|
|
|
4882
4998
|
token: z.string().optional().describe("Figma API token (falls back to FIGMA_TOKEN env var)"),
|
|
4883
4999
|
preset: z.enum(["relaxed", "dev-friendly", "ai-ready", "strict"]).optional().describe("Analysis preset"),
|
|
4884
5000
|
targetNodeId: z.string().optional().describe("Scope analysis to a specific node ID"),
|
|
4885
|
-
configPath: z.string().optional().describe("Path to config JSON file for rule overrides")
|
|
5001
|
+
configPath: z.string().optional().describe("Path to config JSON file for rule overrides"),
|
|
5002
|
+
openReport: z.boolean().optional().describe("Open the generated HTML report in the user's browser. Defaults to false \u2014 opt in only when a visible report is the explicit user request (#365). The HTML file is always written to disk regardless."),
|
|
5003
|
+
acknowledgments: z.array(AcknowledgmentSchema).optional().describe("(#371) Pre-resolved [{ nodeId, ruleId }] pairs harvested from canicode-authored Figma annotations (e.g. via the `readCanicodeAcknowledgments` Plugin helper inside a use_figma batch). Matching issues are flagged `acknowledged: true` and contribute half weight to the density score so re-analyze surfaces movement after a roundtrip even under ADR-012's annotate-by-default policy.")
|
|
4886
5004
|
},
|
|
4887
5005
|
{
|
|
4888
5006
|
readOnlyHint: false,
|
|
@@ -4890,7 +5008,7 @@ Provide a Figma URL or fixture path via the input parameter. Requires FIGMA_TOKE
|
|
|
4890
5008
|
openWorldHint: true,
|
|
4891
5009
|
title: "Analyze Figma Design"
|
|
4892
5010
|
},
|
|
4893
|
-
async ({ input, token, preset, targetNodeId, configPath }) => {
|
|
5011
|
+
async ({ input, token, preset, targetNodeId, configPath, openReport, acknowledgments }) => {
|
|
4894
5012
|
trackEvent(EVENTS.MCP_TOOL_CALLED, { tool: "analyze" });
|
|
4895
5013
|
try {
|
|
4896
5014
|
const { file, nodeId } = await loadFile(input, token);
|
|
@@ -4902,7 +5020,8 @@ Provide a Figma URL or fixture path via the input parameter. Requires FIGMA_TOKE
|
|
|
4902
5020
|
}
|
|
4903
5021
|
const result = analyzeFile(file, {
|
|
4904
5022
|
configs,
|
|
4905
|
-
...effectiveNodeId ? { targetNodeId: effectiveNodeId } : {}
|
|
5023
|
+
...effectiveNodeId ? { targetNodeId: effectiveNodeId } : {},
|
|
5024
|
+
...acknowledgments && acknowledgments.length > 0 ? { acknowledgments } : {}
|
|
4906
5025
|
});
|
|
4907
5026
|
const scores = calculateScores(result, configs);
|
|
4908
5027
|
const figmaToken = token ?? process.env["FIGMA_TOKEN"];
|
|
@@ -4911,14 +5030,16 @@ Provide a Figma URL or fixture path via the input parameter. Requires FIGMA_TOKE
|
|
|
4911
5030
|
const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}-${String(now.getMinutes()).padStart(2, "0")}`;
|
|
4912
5031
|
ensureReportsDir();
|
|
4913
5032
|
const reportPath = `${getReportsDir()}/report-${ts}-${file.fileKey}.html`;
|
|
4914
|
-
await new Promise((
|
|
5033
|
+
await new Promise((resolve6, reject) => {
|
|
4915
5034
|
writeFile(reportPath, html, "utf-8", (err) => {
|
|
4916
5035
|
if (err) reject(err);
|
|
4917
|
-
else
|
|
5036
|
+
else resolve6();
|
|
4918
5037
|
});
|
|
4919
5038
|
});
|
|
4920
|
-
|
|
4921
|
-
|
|
5039
|
+
if (openReport === true) {
|
|
5040
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
5041
|
+
exec(`${openCmd} "${reportPath}"`);
|
|
5042
|
+
}
|
|
4922
5043
|
trackEvent(EVENTS.ANALYSIS_COMPLETED, {
|
|
4923
5044
|
nodeCount: result.nodeCount,
|
|
4924
5045
|
issueCount: result.issues.length,
|
|
@@ -4930,7 +5051,7 @@ Provide a Figma URL or fixture path via the input parameter. Requires FIGMA_TOKE
|
|
|
4930
5051
|
content: [
|
|
4931
5052
|
{
|
|
4932
5053
|
type: "text",
|
|
4933
|
-
text: JSON.stringify(buildResultJson(file.name, result, scores, { fileKey: file.fileKey }), null, 2)
|
|
5054
|
+
text: JSON.stringify(buildResultJson(file.name, result, scores, { fileKey: file.fileKey, designKey: computeDesignKey(input) }), null, 2)
|
|
4934
5055
|
}
|
|
4935
5056
|
]
|
|
4936
5057
|
};
|
|
@@ -4990,7 +5111,7 @@ Provide a Figma URL or fixture path via the input parameter. Requires FIGMA_TOKE
|
|
|
4990
5111
|
...effectiveNodeId ? { targetNodeId: effectiveNodeId } : {}
|
|
4991
5112
|
});
|
|
4992
5113
|
const scores = calculateScores(result, configs);
|
|
4993
|
-
const survey = generateGotchaSurvey(result, scores);
|
|
5114
|
+
const survey = generateGotchaSurvey(result, scores, { designKey: computeDesignKey(input) });
|
|
4994
5115
|
trackEvent(EVENTS.ANALYSIS_COMPLETED, {
|
|
4995
5116
|
nodeCount: result.nodeCount,
|
|
4996
5117
|
issueCount: result.issues.length,
|
|
@@ -5111,10 +5232,10 @@ Get your token: Figma \u2192 Settings \u2192 Security \u2192 Personal access tok
|
|
|
5111
5232
|
|
|
5112
5233
|
## MCP Server (Claude Code / Cursor / Claude Desktop)
|
|
5113
5234
|
\`\`\`bash
|
|
5114
|
-
claude mcp add canicode
|
|
5235
|
+
claude mcp add canicode -- npx --yes --package=canicode canicode-mcp
|
|
5115
5236
|
\`\`\`
|
|
5116
5237
|
|
|
5117
|
-
Requires FIGMA_TOKEN for live Figma URL analysis.
|
|
5238
|
+
Requires FIGMA_TOKEN for live Figma URL analysis. The MCP server reads it from \`~/.canicode/config.json\` (set via \`canicode init --token \u2026\`) or from the host's environment, so do **not** pass \`-e FIGMA_TOKEN=\u2026\` to \`claude mcp add\` \u2014 \`@anthropic-ai/claude-code\`'s current parser rejects short-form flags placed before \`--\`. (#364, #366)
|
|
5118
5239
|
|
|
5119
5240
|
## CLI only (no MCP server)
|
|
5120
5241
|
|
|
@@ -5234,16 +5355,16 @@ ${inlineTopics[selectedTopic]}` }]
|
|
|
5234
5355
|
};
|
|
5235
5356
|
}
|
|
5236
5357
|
const { readFile: readFile3 } = await import('fs/promises');
|
|
5237
|
-
const { resolve:
|
|
5358
|
+
const { resolve: resolve6, dirname: dirname4 } = await import('path');
|
|
5238
5359
|
const { fileURLToPath } = await import('url');
|
|
5239
5360
|
try {
|
|
5240
5361
|
const __dirname = dirname4(fileURLToPath(import.meta.url));
|
|
5241
|
-
const docPath =
|
|
5362
|
+
const docPath = resolve6(__dirname, "../../docs/CUSTOMIZATION.md");
|
|
5242
5363
|
let content;
|
|
5243
5364
|
try {
|
|
5244
5365
|
content = await readFile3(docPath, "utf-8");
|
|
5245
5366
|
} catch {
|
|
5246
|
-
const altPath =
|
|
5367
|
+
const altPath = resolve6(__dirname, "../docs/CUSTOMIZATION.md");
|
|
5247
5368
|
content = await readFile3(altPath, "utf-8");
|
|
5248
5369
|
}
|
|
5249
5370
|
if (selectedTopic !== "all") {
|