canicode 0.3.3 → 0.4.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/dist/cli/index.js +347 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/mcp/server.js +308 -0
- package/dist/mcp/server.js.map +1 -1
- package/docs/CUSTOMIZATION.md +31 -0
- package/package.json +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -10,6 +10,204 @@ import { join, resolve, basename } from 'path';
|
|
|
10
10
|
import { readFile } from 'fs/promises';
|
|
11
11
|
import { homedir } from 'os';
|
|
12
12
|
|
|
13
|
+
var __defProp = Object.defineProperty;
|
|
14
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
|
+
var __esm = (fn, res) => function __init() {
|
|
16
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
17
|
+
};
|
|
18
|
+
var __export = (target, all) => {
|
|
19
|
+
for (var name in all)
|
|
20
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/monitoring/browser.ts
|
|
24
|
+
var browser_exports = {};
|
|
25
|
+
__export(browser_exports, {
|
|
26
|
+
initBrowserMonitoring: () => initBrowserMonitoring,
|
|
27
|
+
shutdownBrowserMonitoring: () => shutdownBrowserMonitoring,
|
|
28
|
+
trackBrowserError: () => trackBrowserError,
|
|
29
|
+
trackBrowserEvent: () => trackBrowserEvent
|
|
30
|
+
});
|
|
31
|
+
function getGlobal() {
|
|
32
|
+
return globalThis;
|
|
33
|
+
}
|
|
34
|
+
function injectScript(src) {
|
|
35
|
+
return new Promise((resolve4, reject) => {
|
|
36
|
+
const g = getGlobal();
|
|
37
|
+
const doc = g["document"];
|
|
38
|
+
if (!doc) {
|
|
39
|
+
resolve4();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const script = doc["createElement"]("script");
|
|
43
|
+
script["src"] = src;
|
|
44
|
+
script["async"] = true;
|
|
45
|
+
script["onload"] = () => resolve4();
|
|
46
|
+
script["onerror"] = () => reject(new Error(`Failed to load script: ${src}`));
|
|
47
|
+
doc["head"]["appendChild"](script);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function initBrowserMonitoring(config2) {
|
|
51
|
+
if (config2.enabled === false) return;
|
|
52
|
+
const g = getGlobal();
|
|
53
|
+
if (!g["document"]) return;
|
|
54
|
+
monitoringEnabled = true;
|
|
55
|
+
if (config2.posthogApiKey) {
|
|
56
|
+
try {
|
|
57
|
+
await injectScript("https://us-assets.i.posthog.com/static/array.js");
|
|
58
|
+
g["posthog"]?.["init"]?.(config2.posthogApiKey, {
|
|
59
|
+
api_host: "https://us.i.posthog.com",
|
|
60
|
+
autocapture: false,
|
|
61
|
+
capture_pageview: true
|
|
62
|
+
});
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (config2.sentryDsn) {
|
|
67
|
+
try {
|
|
68
|
+
await injectScript("https://browser.sentry-cdn.com/8.0.0/bundle.min.js");
|
|
69
|
+
g["Sentry"]?.["init"]?.({
|
|
70
|
+
dsn: config2.sentryDsn,
|
|
71
|
+
environment: config2.environment ?? "web",
|
|
72
|
+
release: config2.version,
|
|
73
|
+
tracesSampleRate: 0
|
|
74
|
+
});
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function trackBrowserEvent(event, properties) {
|
|
80
|
+
if (!monitoringEnabled) return;
|
|
81
|
+
try {
|
|
82
|
+
getGlobal()["posthog"]?.["capture"]?.(event, properties);
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function trackBrowserError(error, context) {
|
|
87
|
+
if (!monitoringEnabled) return;
|
|
88
|
+
try {
|
|
89
|
+
getGlobal()["Sentry"]?.["captureException"]?.(
|
|
90
|
+
error,
|
|
91
|
+
context ? { extra: context } : void 0
|
|
92
|
+
);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
getGlobal()["posthog"]?.["capture"]?.("error", {
|
|
97
|
+
error: error.message,
|
|
98
|
+
...context
|
|
99
|
+
});
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function shutdownBrowserMonitoring() {
|
|
104
|
+
monitoringEnabled = false;
|
|
105
|
+
}
|
|
106
|
+
var monitoringEnabled;
|
|
107
|
+
var init_browser = __esm({
|
|
108
|
+
"src/monitoring/browser.ts"() {
|
|
109
|
+
monitoringEnabled = false;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// src/monitoring/node.ts
|
|
114
|
+
var node_exports = {};
|
|
115
|
+
__export(node_exports, {
|
|
116
|
+
initNodeMonitoring: () => initNodeMonitoring,
|
|
117
|
+
shutdownNodeMonitoring: () => shutdownNodeMonitoring,
|
|
118
|
+
trackNodeError: () => trackNodeError,
|
|
119
|
+
trackNodeEvent: () => trackNodeEvent
|
|
120
|
+
});
|
|
121
|
+
async function initNodeMonitoring(config2) {
|
|
122
|
+
if (config2.enabled === false) return;
|
|
123
|
+
monitoringEnabled2 = true;
|
|
124
|
+
commonProps = {
|
|
125
|
+
_sdk: "canicode",
|
|
126
|
+
_sdk_version: config2.version ?? "unknown",
|
|
127
|
+
_env: config2.environment ?? "unknown"
|
|
128
|
+
};
|
|
129
|
+
if (config2.posthogApiKey) {
|
|
130
|
+
try {
|
|
131
|
+
const mod = await import('posthog-node');
|
|
132
|
+
const PostHog = mod.PostHog;
|
|
133
|
+
posthogClient = new PostHog(config2.posthogApiKey, {
|
|
134
|
+
host: "https://us.i.posthog.com",
|
|
135
|
+
flushAt: 10,
|
|
136
|
+
flushInterval: 1e4
|
|
137
|
+
});
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (config2.sentryDsn) {
|
|
142
|
+
try {
|
|
143
|
+
const mod = await import('@sentry/node');
|
|
144
|
+
sentryModule = mod;
|
|
145
|
+
sentryModule.init({
|
|
146
|
+
dsn: config2.sentryDsn,
|
|
147
|
+
environment: config2.environment ?? "cli",
|
|
148
|
+
release: config2.version,
|
|
149
|
+
tracesSampleRate: 0
|
|
150
|
+
});
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function trackNodeEvent(event, properties) {
|
|
156
|
+
if (!monitoringEnabled2 || !posthogClient) return;
|
|
157
|
+
try {
|
|
158
|
+
const captureOpts = {
|
|
159
|
+
distinctId: "anonymous",
|
|
160
|
+
event
|
|
161
|
+
};
|
|
162
|
+
captureOpts.properties = { ...commonProps, ...properties };
|
|
163
|
+
posthogClient.capture(captureOpts);
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function trackNodeError(error, context) {
|
|
168
|
+
if (!monitoringEnabled2) return;
|
|
169
|
+
try {
|
|
170
|
+
sentryModule?.captureException(error, context ? { extra: context } : void 0);
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
posthogClient?.capture({
|
|
175
|
+
distinctId: "anonymous",
|
|
176
|
+
event: "cic_error",
|
|
177
|
+
properties: { ...commonProps, error: error.message, ...context }
|
|
178
|
+
});
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function shutdownNodeMonitoring() {
|
|
183
|
+
if (!monitoringEnabled2) return;
|
|
184
|
+
const tasks = [];
|
|
185
|
+
if (posthogClient) {
|
|
186
|
+
tasks.push(
|
|
187
|
+
posthogClient.shutdown().catch(() => {
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
if (sentryModule) {
|
|
192
|
+
tasks.push(
|
|
193
|
+
sentryModule.close(2e3).catch(() => {
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
await Promise.allSettled(tasks);
|
|
198
|
+
posthogClient = null;
|
|
199
|
+
sentryModule = null;
|
|
200
|
+
monitoringEnabled2 = false;
|
|
201
|
+
}
|
|
202
|
+
var posthogClient, sentryModule, monitoringEnabled2, commonProps;
|
|
203
|
+
var init_node = __esm({
|
|
204
|
+
"src/monitoring/node.ts"() {
|
|
205
|
+
posthogClient = null;
|
|
206
|
+
sentryModule = null;
|
|
207
|
+
monitoringEnabled2 = false;
|
|
208
|
+
commonProps = {};
|
|
209
|
+
}
|
|
210
|
+
});
|
|
13
211
|
var CategorySchema = z.enum([
|
|
14
212
|
"layout",
|
|
15
213
|
"token",
|
|
@@ -1024,6 +1222,15 @@ function getReportsDir() {
|
|
|
1024
1222
|
function ensureReportsDir() {
|
|
1025
1223
|
ensureDir(REPORTS_DIR);
|
|
1026
1224
|
}
|
|
1225
|
+
function getTelemetryEnabled() {
|
|
1226
|
+
return readConfig().telemetry !== false;
|
|
1227
|
+
}
|
|
1228
|
+
function getPosthogApiKey() {
|
|
1229
|
+
return process.env["POSTHOG_API_KEY"] ?? readConfig().posthogApiKey;
|
|
1230
|
+
}
|
|
1231
|
+
function getSentryDsn() {
|
|
1232
|
+
return process.env["SENTRY_DSN"] ?? readConfig().sentryDsn;
|
|
1233
|
+
}
|
|
1027
1234
|
|
|
1028
1235
|
// src/core/loader.ts
|
|
1029
1236
|
function isFigmaUrl(input) {
|
|
@@ -1713,6 +1920,77 @@ function createPromptBasedCheck(_cr) {
|
|
|
1713
1920
|
};
|
|
1714
1921
|
}
|
|
1715
1922
|
|
|
1923
|
+
// src/monitoring/events.ts
|
|
1924
|
+
var EVENT_PREFIX = "cic_";
|
|
1925
|
+
var EVENTS = {
|
|
1926
|
+
// Analysis
|
|
1927
|
+
ANALYSIS_STARTED: `${EVENT_PREFIX}analysis_started`,
|
|
1928
|
+
ANALYSIS_COMPLETED: `${EVENT_PREFIX}analysis_completed`,
|
|
1929
|
+
ANALYSIS_FAILED: `${EVENT_PREFIX}analysis_failed`,
|
|
1930
|
+
// Report
|
|
1931
|
+
REPORT_GENERATED: `${EVENT_PREFIX}report_generated`,
|
|
1932
|
+
COMMENT_POSTED: `${EVENT_PREFIX}comment_posted`,
|
|
1933
|
+
COMMENT_FAILED: `${EVENT_PREFIX}comment_failed`,
|
|
1934
|
+
// MCP
|
|
1935
|
+
MCP_TOOL_CALLED: `${EVENT_PREFIX}mcp_tool_called`,
|
|
1936
|
+
// CLI
|
|
1937
|
+
CLI_COMMAND: `${EVENT_PREFIX}cli_command`,
|
|
1938
|
+
CLI_INIT: `${EVENT_PREFIX}cli_init`
|
|
1939
|
+
};
|
|
1940
|
+
|
|
1941
|
+
// src/monitoring/index.ts
|
|
1942
|
+
var _trackEvent = () => {
|
|
1943
|
+
};
|
|
1944
|
+
var _trackError = () => {
|
|
1945
|
+
};
|
|
1946
|
+
var _shutdown = () => Promise.resolve();
|
|
1947
|
+
function isBrowser() {
|
|
1948
|
+
const g = globalThis;
|
|
1949
|
+
return typeof g["window"] !== "undefined" && typeof g["document"] !== "undefined";
|
|
1950
|
+
}
|
|
1951
|
+
async function initMonitoring(config2) {
|
|
1952
|
+
if (config2.enabled === false) return;
|
|
1953
|
+
if (!config2.posthogApiKey && !config2.sentryDsn) return;
|
|
1954
|
+
try {
|
|
1955
|
+
if (isBrowser()) {
|
|
1956
|
+
const { initBrowserMonitoring: initBrowserMonitoring2, trackBrowserEvent: trackBrowserEvent2, trackBrowserError: trackBrowserError2, shutdownBrowserMonitoring: shutdownBrowserMonitoring2 } = await Promise.resolve().then(() => (init_browser(), browser_exports));
|
|
1957
|
+
await initBrowserMonitoring2(config2);
|
|
1958
|
+
_trackEvent = trackBrowserEvent2;
|
|
1959
|
+
_trackError = trackBrowserError2;
|
|
1960
|
+
_shutdown = shutdownBrowserMonitoring2;
|
|
1961
|
+
} else {
|
|
1962
|
+
const { initNodeMonitoring: initNodeMonitoring2, trackNodeEvent: trackNodeEvent2, trackNodeError: trackNodeError2, shutdownNodeMonitoring: shutdownNodeMonitoring2 } = await Promise.resolve().then(() => (init_node(), node_exports));
|
|
1963
|
+
await initNodeMonitoring2(config2);
|
|
1964
|
+
_trackEvent = trackNodeEvent2;
|
|
1965
|
+
_trackError = trackNodeError2;
|
|
1966
|
+
_shutdown = shutdownNodeMonitoring2;
|
|
1967
|
+
}
|
|
1968
|
+
} catch {
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
function trackEvent(event, properties) {
|
|
1972
|
+
try {
|
|
1973
|
+
_trackEvent(event, properties);
|
|
1974
|
+
} catch {
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
function trackError(error, context) {
|
|
1978
|
+
try {
|
|
1979
|
+
_trackError(error, context);
|
|
1980
|
+
} catch {
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
async function shutdownMonitoring() {
|
|
1984
|
+
try {
|
|
1985
|
+
await _shutdown();
|
|
1986
|
+
} catch {
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// src/monitoring/keys.ts
|
|
1991
|
+
var POSTHOG_API_KEY = "phc_rBFeG140KqJLpUnlpYDEFgdMM6JozZeqQsf9twXf5Dq" ;
|
|
1992
|
+
var SENTRY_DSN = "https://80a836a8300b25f17ef5bbf23afb5b3a@o4511080656207872.ingest.us.sentry.io/4511080661319680" ;
|
|
1993
|
+
|
|
1716
1994
|
// src/rules/excluded-names.ts
|
|
1717
1995
|
var EXCLUDED_NAME_PATTERN = /(badge|close|dismiss|overlay|float|fab|dot|indicator|corner|decoration|tag|status|notification|icon|ico|image|asset|filter|dim|dimmed|bg|background|logo|avatar|divider|separator|nav|navigation|gnb|header|footer|sidebar|toolbar|modal|dialog|popup|toast|tooltip|dropdown|menu|sticky|spinner|loader|cursor|cta|chatbot|thumb|thumbnail|tabbar|tab-bar|statusbar|status-bar)/i;
|
|
1718
1996
|
function isExcludedName(name) {
|
|
@@ -2916,6 +3194,7 @@ Typical flow with Figma MCP:
|
|
|
2916
3194
|
customRulesPath: z.string().optional().describe("Path to custom rules JSON file")
|
|
2917
3195
|
},
|
|
2918
3196
|
async ({ designData, input, fileKey, fileName, token, preset, targetNodeId, configPath, customRulesPath }) => {
|
|
3197
|
+
trackEvent(EVENTS.MCP_TOOL_CALLED, { tool: "analyze" });
|
|
2919
3198
|
try {
|
|
2920
3199
|
let file;
|
|
2921
3200
|
let nodeId;
|
|
@@ -2961,6 +3240,13 @@ Typical flow with Figma MCP:
|
|
|
2961
3240
|
});
|
|
2962
3241
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2963
3242
|
exec(`${openCmd} "${reportPath}"`);
|
|
3243
|
+
trackEvent(EVENTS.ANALYSIS_COMPLETED, {
|
|
3244
|
+
nodeCount: result.nodeCount,
|
|
3245
|
+
issueCount: result.issues.length,
|
|
3246
|
+
grade: scores.overall.grade,
|
|
3247
|
+
percentage: scores.overall.percentage,
|
|
3248
|
+
source: designData ? "mcp-data" : "url"
|
|
3249
|
+
});
|
|
2964
3250
|
const issuesByRule = {};
|
|
2965
3251
|
for (const issue of result.issues) {
|
|
2966
3252
|
const id = issue.violation.ruleId;
|
|
@@ -2990,6 +3276,13 @@ Typical flow with Figma MCP:
|
|
|
2990
3276
|
]
|
|
2991
3277
|
};
|
|
2992
3278
|
} catch (error) {
|
|
3279
|
+
trackError(
|
|
3280
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
3281
|
+
{ tool: "analyze" }
|
|
3282
|
+
);
|
|
3283
|
+
trackEvent(EVENTS.ANALYSIS_FAILED, {
|
|
3284
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3285
|
+
});
|
|
2993
3286
|
return {
|
|
2994
3287
|
content: [
|
|
2995
3288
|
{
|
|
@@ -3084,9 +3377,24 @@ Use this when the user asks about customization, configuration, rule settings, o
|
|
|
3084
3377
|
}
|
|
3085
3378
|
);
|
|
3086
3379
|
async function main() {
|
|
3380
|
+
const monitoringConfig = {
|
|
3381
|
+
environment: "mcp",
|
|
3382
|
+
version: pkg.version,
|
|
3383
|
+
enabled: getTelemetryEnabled()
|
|
3384
|
+
};
|
|
3385
|
+
const phKey = getPosthogApiKey() || POSTHOG_API_KEY;
|
|
3386
|
+
monitoringConfig.posthogApiKey = phKey;
|
|
3387
|
+
const sDsn = getSentryDsn() || SENTRY_DSN;
|
|
3388
|
+
monitoringConfig.sentryDsn = sDsn;
|
|
3389
|
+
await initMonitoring(monitoringConfig).catch(() => {
|
|
3390
|
+
});
|
|
3087
3391
|
const transport = new StdioServerTransport();
|
|
3088
3392
|
await server.connect(transport);
|
|
3089
3393
|
}
|
|
3394
|
+
process.on("beforeExit", () => {
|
|
3395
|
+
shutdownMonitoring().catch(() => {
|
|
3396
|
+
});
|
|
3397
|
+
});
|
|
3090
3398
|
main().catch(console.error);
|
|
3091
3399
|
//# sourceMappingURL=server.js.map
|
|
3092
3400
|
//# sourceMappingURL=server.js.map
|