canicode 0.6.0 → 0.6.2
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 +13 -4
- package/dist/cli/index.js +111 -230
- package/dist/cli/index.js.map +1 -1
- package/dist/mcp/server.js +115 -230
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -103,9 +103,18 @@ canicode init --token figd_xxxxxxxxxxxxx
|
|
|
103
103
|
|
|
104
104
|
### MCP Server (Claude Code / Cursor / Claude Desktop)
|
|
105
105
|
|
|
106
|
-
**Claude Code:**
|
|
106
|
+
**Claude Code (recommended — with official Figma MCP, no token needed):**
|
|
107
107
|
```bash
|
|
108
|
-
|
|
108
|
+
# 1. Install canicode MCP server
|
|
109
|
+
claude mcp add canicode -- npx -y -p canicode canicode-mcp
|
|
110
|
+
|
|
111
|
+
# 2. Install official Figma MCP (enables token-free analysis)
|
|
112
|
+
claude mcp add -s project -t http figma https://mcp.figma.com/mcp
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Claude Code (with Figma API token):**
|
|
116
|
+
```bash
|
|
117
|
+
claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y -p canicode canicode-mcp
|
|
109
118
|
```
|
|
110
119
|
|
|
111
120
|
**Cursor** (`~/.cursor/mcp.json`):
|
|
@@ -114,7 +123,7 @@ claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y canicode can
|
|
|
114
123
|
"mcpServers": {
|
|
115
124
|
"canicode": {
|
|
116
125
|
"command": "npx",
|
|
117
|
-
"args": ["-y", "canicode", "canicode-mcp"],
|
|
126
|
+
"args": ["-y", "-p", "canicode", "canicode-mcp"],
|
|
118
127
|
"env": {
|
|
119
128
|
"FIGMA_TOKEN": "figd_xxxxxxxxxxxxx"
|
|
120
129
|
}
|
|
@@ -129,7 +138,7 @@ claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y canicode can
|
|
|
129
138
|
"mcpServers": {
|
|
130
139
|
"canicode": {
|
|
131
140
|
"command": "npx",
|
|
132
|
-
"args": ["-y", "canicode", "canicode-mcp"],
|
|
141
|
+
"args": ["-y", "-p", "canicode", "canicode-mcp"],
|
|
133
142
|
"env": {
|
|
134
143
|
"FIGMA_TOKEN": "figd_xxxxxxxxxxxxx"
|
|
135
144
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -5,206 +5,9 @@ import { join, resolve, dirname, basename } from 'path';
|
|
|
5
5
|
import { config } from 'dotenv';
|
|
6
6
|
import cac from 'cac';
|
|
7
7
|
import { z } from 'zod';
|
|
8
|
+
import { randomUUID } from 'crypto';
|
|
8
9
|
import { homedir } from 'os';
|
|
9
10
|
|
|
10
|
-
var __defProp = Object.defineProperty;
|
|
11
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
-
var __esm = (fn, res) => function __init() {
|
|
13
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
14
|
-
};
|
|
15
|
-
var __export = (target, all) => {
|
|
16
|
-
for (var name in all)
|
|
17
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// src/monitoring/browser.ts
|
|
21
|
-
var browser_exports = {};
|
|
22
|
-
__export(browser_exports, {
|
|
23
|
-
initBrowserMonitoring: () => initBrowserMonitoring,
|
|
24
|
-
shutdownBrowserMonitoring: () => shutdownBrowserMonitoring,
|
|
25
|
-
trackBrowserError: () => trackBrowserError,
|
|
26
|
-
trackBrowserEvent: () => trackBrowserEvent
|
|
27
|
-
});
|
|
28
|
-
function getGlobal() {
|
|
29
|
-
return globalThis;
|
|
30
|
-
}
|
|
31
|
-
function injectScript(src) {
|
|
32
|
-
return new Promise((resolve7, reject) => {
|
|
33
|
-
const g = getGlobal();
|
|
34
|
-
const doc = g["document"];
|
|
35
|
-
if (!doc) {
|
|
36
|
-
resolve7();
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const script = doc["createElement"]("script");
|
|
40
|
-
script["src"] = src;
|
|
41
|
-
script["async"] = true;
|
|
42
|
-
script["onload"] = () => resolve7();
|
|
43
|
-
script["onerror"] = () => reject(new Error(`Failed to load script: ${src}`));
|
|
44
|
-
doc["head"]["appendChild"](script);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
async function initBrowserMonitoring(config2) {
|
|
48
|
-
if (config2.enabled === false) return;
|
|
49
|
-
const g = getGlobal();
|
|
50
|
-
if (!g["document"]) return;
|
|
51
|
-
monitoringEnabled = true;
|
|
52
|
-
if (config2.posthogApiKey) {
|
|
53
|
-
try {
|
|
54
|
-
await injectScript("https://us-assets.i.posthog.com/static/array.js");
|
|
55
|
-
g["posthog"]?.["init"]?.(config2.posthogApiKey, {
|
|
56
|
-
api_host: "https://us.i.posthog.com",
|
|
57
|
-
autocapture: false,
|
|
58
|
-
capture_pageview: true
|
|
59
|
-
});
|
|
60
|
-
} catch {
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (config2.sentryDsn) {
|
|
64
|
-
try {
|
|
65
|
-
await injectScript("https://browser.sentry-cdn.com/8.0.0/bundle.min.js");
|
|
66
|
-
g["Sentry"]?.["init"]?.({
|
|
67
|
-
dsn: config2.sentryDsn,
|
|
68
|
-
environment: config2.environment ?? "web",
|
|
69
|
-
release: config2.version,
|
|
70
|
-
tracesSampleRate: 0
|
|
71
|
-
});
|
|
72
|
-
} catch {
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function trackBrowserEvent(event, properties) {
|
|
77
|
-
if (!monitoringEnabled) return;
|
|
78
|
-
try {
|
|
79
|
-
getGlobal()["posthog"]?.["capture"]?.(event, properties);
|
|
80
|
-
} catch {
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function trackBrowserError(error, context) {
|
|
84
|
-
if (!monitoringEnabled) return;
|
|
85
|
-
try {
|
|
86
|
-
getGlobal()["Sentry"]?.["captureException"]?.(
|
|
87
|
-
error,
|
|
88
|
-
context ? { extra: context } : void 0
|
|
89
|
-
);
|
|
90
|
-
} catch {
|
|
91
|
-
}
|
|
92
|
-
try {
|
|
93
|
-
getGlobal()["posthog"]?.["capture"]?.("error", {
|
|
94
|
-
error: error.message,
|
|
95
|
-
...context
|
|
96
|
-
});
|
|
97
|
-
} catch {
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async function shutdownBrowserMonitoring() {
|
|
101
|
-
monitoringEnabled = false;
|
|
102
|
-
}
|
|
103
|
-
var monitoringEnabled;
|
|
104
|
-
var init_browser = __esm({
|
|
105
|
-
"src/monitoring/browser.ts"() {
|
|
106
|
-
monitoringEnabled = false;
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// src/monitoring/node.ts
|
|
111
|
-
var node_exports = {};
|
|
112
|
-
__export(node_exports, {
|
|
113
|
-
initNodeMonitoring: () => initNodeMonitoring,
|
|
114
|
-
shutdownNodeMonitoring: () => shutdownNodeMonitoring,
|
|
115
|
-
trackNodeError: () => trackNodeError,
|
|
116
|
-
trackNodeEvent: () => trackNodeEvent
|
|
117
|
-
});
|
|
118
|
-
async function initNodeMonitoring(config2) {
|
|
119
|
-
if (config2.enabled === false) return;
|
|
120
|
-
monitoringEnabled2 = true;
|
|
121
|
-
commonProps = {
|
|
122
|
-
_sdk: "canicode",
|
|
123
|
-
_sdk_version: config2.version ?? "unknown",
|
|
124
|
-
_env: config2.environment ?? "unknown"
|
|
125
|
-
};
|
|
126
|
-
if (config2.posthogApiKey) {
|
|
127
|
-
try {
|
|
128
|
-
const mod = await import('posthog-node');
|
|
129
|
-
const PostHog = mod.PostHog;
|
|
130
|
-
posthogClient = new PostHog(config2.posthogApiKey, {
|
|
131
|
-
host: "https://us.i.posthog.com",
|
|
132
|
-
flushAt: 10,
|
|
133
|
-
flushInterval: 1e4
|
|
134
|
-
});
|
|
135
|
-
} catch {
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (config2.sentryDsn) {
|
|
139
|
-
try {
|
|
140
|
-
const mod = await import('@sentry/node');
|
|
141
|
-
sentryModule = mod;
|
|
142
|
-
sentryModule.init({
|
|
143
|
-
dsn: config2.sentryDsn,
|
|
144
|
-
environment: config2.environment ?? "cli",
|
|
145
|
-
release: config2.version,
|
|
146
|
-
tracesSampleRate: 0
|
|
147
|
-
});
|
|
148
|
-
} catch {
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
function trackNodeEvent(event, properties) {
|
|
153
|
-
if (!monitoringEnabled2 || !posthogClient) return;
|
|
154
|
-
try {
|
|
155
|
-
const captureOpts = {
|
|
156
|
-
distinctId: "anonymous",
|
|
157
|
-
event
|
|
158
|
-
};
|
|
159
|
-
captureOpts.properties = { ...commonProps, ...properties };
|
|
160
|
-
posthogClient.capture(captureOpts);
|
|
161
|
-
} catch {
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
function trackNodeError(error, context) {
|
|
165
|
-
if (!monitoringEnabled2) return;
|
|
166
|
-
try {
|
|
167
|
-
sentryModule?.captureException(error, context ? { extra: context } : void 0);
|
|
168
|
-
} catch {
|
|
169
|
-
}
|
|
170
|
-
try {
|
|
171
|
-
posthogClient?.capture({
|
|
172
|
-
distinctId: "anonymous",
|
|
173
|
-
event: "cic_error",
|
|
174
|
-
properties: { ...commonProps, error: error.message, ...context }
|
|
175
|
-
});
|
|
176
|
-
} catch {
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
async function shutdownNodeMonitoring() {
|
|
180
|
-
if (!monitoringEnabled2) return;
|
|
181
|
-
const tasks = [];
|
|
182
|
-
if (posthogClient) {
|
|
183
|
-
tasks.push(
|
|
184
|
-
posthogClient.shutdown().catch(() => {
|
|
185
|
-
})
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
if (sentryModule) {
|
|
189
|
-
tasks.push(
|
|
190
|
-
sentryModule.close(2e3).catch(() => {
|
|
191
|
-
})
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
await Promise.allSettled(tasks);
|
|
195
|
-
posthogClient = null;
|
|
196
|
-
sentryModule = null;
|
|
197
|
-
monitoringEnabled2 = false;
|
|
198
|
-
}
|
|
199
|
-
var posthogClient, sentryModule, monitoringEnabled2, commonProps;
|
|
200
|
-
var init_node = __esm({
|
|
201
|
-
"src/monitoring/node.ts"() {
|
|
202
|
-
posthogClient = null;
|
|
203
|
-
sentryModule = null;
|
|
204
|
-
monitoringEnabled2 = false;
|
|
205
|
-
commonProps = {};
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
11
|
z.object({
|
|
209
12
|
fileKey: z.string(),
|
|
210
13
|
nodeId: z.string().optional(),
|
|
@@ -1114,6 +917,14 @@ function getPosthogApiKey() {
|
|
|
1114
917
|
function getSentryDsn() {
|
|
1115
918
|
return process.env["SENTRY_DSN"] ?? readConfig().sentryDsn;
|
|
1116
919
|
}
|
|
920
|
+
function getDeviceId() {
|
|
921
|
+
const config2 = readConfig();
|
|
922
|
+
if (config2.deviceId) return config2.deviceId;
|
|
923
|
+
const id = randomUUID();
|
|
924
|
+
config2.deviceId = id;
|
|
925
|
+
writeConfig(config2);
|
|
926
|
+
return id;
|
|
927
|
+
}
|
|
1117
928
|
function initAiready(token) {
|
|
1118
929
|
setFigmaToken(token);
|
|
1119
930
|
ensureReportsDir();
|
|
@@ -2949,51 +2760,122 @@ var EVENTS = {
|
|
|
2949
2760
|
CLI_INIT: `${EVENT_PREFIX}cli_init`
|
|
2950
2761
|
};
|
|
2951
2762
|
|
|
2952
|
-
// src/monitoring/
|
|
2953
|
-
var
|
|
2954
|
-
|
|
2955
|
-
var
|
|
2956
|
-
|
|
2957
|
-
var
|
|
2958
|
-
function
|
|
2959
|
-
|
|
2960
|
-
|
|
2763
|
+
// src/monitoring/capture.ts
|
|
2764
|
+
var monitoringEnabled = false;
|
|
2765
|
+
var posthogApiKey;
|
|
2766
|
+
var sentryDsn;
|
|
2767
|
+
var distinctId = "anonymous";
|
|
2768
|
+
var commonProps = {};
|
|
2769
|
+
function uuid4() {
|
|
2770
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
2771
|
+
const r = Math.random() * 16 | 0;
|
|
2772
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
2773
|
+
return v.toString(16);
|
|
2774
|
+
});
|
|
2961
2775
|
}
|
|
2962
|
-
|
|
2776
|
+
function parseSentryDsn(dsn) {
|
|
2777
|
+
try {
|
|
2778
|
+
const url = new URL(dsn);
|
|
2779
|
+
const key = url.username;
|
|
2780
|
+
const projectId = url.pathname.slice(1);
|
|
2781
|
+
const host = url.protocol + "//" + url.host;
|
|
2782
|
+
if (!key || !projectId) return null;
|
|
2783
|
+
return { key, host, projectId };
|
|
2784
|
+
} catch {
|
|
2785
|
+
return null;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
function initCapture(config2) {
|
|
2963
2789
|
if (config2.enabled === false) return;
|
|
2964
2790
|
if (!config2.posthogApiKey && !config2.sentryDsn) return;
|
|
2791
|
+
monitoringEnabled = true;
|
|
2792
|
+
posthogApiKey = config2.posthogApiKey;
|
|
2793
|
+
sentryDsn = config2.sentryDsn;
|
|
2794
|
+
distinctId = config2.distinctId ?? "anonymous";
|
|
2795
|
+
commonProps = {
|
|
2796
|
+
_sdk: "canicode",
|
|
2797
|
+
_sdk_version: config2.version ?? "unknown",
|
|
2798
|
+
_env: config2.environment ?? "unknown"
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
function captureEvent(event, properties) {
|
|
2802
|
+
if (!monitoringEnabled || !posthogApiKey) return;
|
|
2965
2803
|
try {
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
}
|
|
2804
|
+
fetch("https://us.i.posthog.com/i/v0/e/", {
|
|
2805
|
+
method: "POST",
|
|
2806
|
+
headers: { "Content-Type": "application/json" },
|
|
2807
|
+
body: JSON.stringify({
|
|
2808
|
+
api_key: posthogApiKey,
|
|
2809
|
+
event,
|
|
2810
|
+
distinct_id: distinctId,
|
|
2811
|
+
properties: { ...commonProps, ...properties },
|
|
2812
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2813
|
+
})
|
|
2814
|
+
}).catch(() => {
|
|
2815
|
+
});
|
|
2979
2816
|
} catch {
|
|
2980
2817
|
}
|
|
2981
2818
|
}
|
|
2819
|
+
function captureError(error, context) {
|
|
2820
|
+
if (!monitoringEnabled) return;
|
|
2821
|
+
if (sentryDsn) {
|
|
2822
|
+
const parsed = parseSentryDsn(sentryDsn);
|
|
2823
|
+
if (parsed) {
|
|
2824
|
+
try {
|
|
2825
|
+
const eventId = uuid4();
|
|
2826
|
+
const envelope = [
|
|
2827
|
+
JSON.stringify({ event_id: eventId, sent_at: (/* @__PURE__ */ new Date()).toISOString(), dsn: sentryDsn }),
|
|
2828
|
+
JSON.stringify({ type: "event", content_type: "application/json" }),
|
|
2829
|
+
JSON.stringify({
|
|
2830
|
+
event_id: eventId,
|
|
2831
|
+
exception: { values: [{ type: error.name, value: error.message }] },
|
|
2832
|
+
platform: "node",
|
|
2833
|
+
timestamp: Date.now() / 1e3,
|
|
2834
|
+
extra: context
|
|
2835
|
+
})
|
|
2836
|
+
].join("\n");
|
|
2837
|
+
fetch(`${parsed.host}/api/${parsed.projectId}/envelope/`, {
|
|
2838
|
+
method: "POST",
|
|
2839
|
+
headers: {
|
|
2840
|
+
"Content-Type": "application/x-sentry-envelope",
|
|
2841
|
+
"X-Sentry-Auth": `Sentry sentry_version=7, sentry_key=${parsed.key}`
|
|
2842
|
+
},
|
|
2843
|
+
body: envelope
|
|
2844
|
+
}).catch(() => {
|
|
2845
|
+
});
|
|
2846
|
+
} catch {
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
captureEvent("cic_error", { error: error.message, ...context });
|
|
2851
|
+
}
|
|
2852
|
+
function shutdownCapture() {
|
|
2853
|
+
monitoringEnabled = false;
|
|
2854
|
+
posthogApiKey = void 0;
|
|
2855
|
+
sentryDsn = void 0;
|
|
2856
|
+
distinctId = "anonymous";
|
|
2857
|
+
commonProps = {};
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
// src/monitoring/index.ts
|
|
2861
|
+
function initMonitoring(config2) {
|
|
2862
|
+
initCapture(config2);
|
|
2863
|
+
}
|
|
2982
2864
|
function trackEvent(event, properties) {
|
|
2983
2865
|
try {
|
|
2984
|
-
|
|
2866
|
+
captureEvent(event, properties);
|
|
2985
2867
|
} catch {
|
|
2986
2868
|
}
|
|
2987
2869
|
}
|
|
2988
2870
|
function trackError(error, context) {
|
|
2989
2871
|
try {
|
|
2990
|
-
|
|
2872
|
+
captureError(error, context);
|
|
2991
2873
|
} catch {
|
|
2992
2874
|
}
|
|
2993
2875
|
}
|
|
2994
|
-
|
|
2876
|
+
function shutdownMonitoring() {
|
|
2995
2877
|
try {
|
|
2996
|
-
|
|
2878
|
+
shutdownCapture();
|
|
2997
2879
|
} catch {
|
|
2998
2880
|
}
|
|
2999
2881
|
}
|
|
@@ -4181,12 +4063,11 @@ var cli = cac("canicode");
|
|
|
4181
4063
|
monitoringConfig.posthogApiKey = phKey;
|
|
4182
4064
|
const sDsn = getSentryDsn() || SENTRY_DSN;
|
|
4183
4065
|
monitoringConfig.sentryDsn = sDsn;
|
|
4184
|
-
|
|
4185
|
-
|
|
4066
|
+
monitoringConfig.distinctId = getDeviceId();
|
|
4067
|
+
initMonitoring(monitoringConfig);
|
|
4186
4068
|
}
|
|
4187
4069
|
process.on("beforeExit", () => {
|
|
4188
|
-
shutdownMonitoring()
|
|
4189
|
-
});
|
|
4070
|
+
shutdownMonitoring();
|
|
4190
4071
|
});
|
|
4191
4072
|
var MAX_NODES_WITHOUT_SCOPE = 500;
|
|
4192
4073
|
function pickRandomScope(root) {
|
|
@@ -4713,7 +4594,7 @@ cli.help((sections) => {
|
|
|
4713
4594
|
title: "\nInstallation",
|
|
4714
4595
|
body: [
|
|
4715
4596
|
` CLI: npm install -g canicode`,
|
|
4716
|
-
` MCP: claude mcp add --
|
|
4597
|
+
` MCP: claude mcp add canicode -- npx -y -p canicode canicode-mcp`,
|
|
4717
4598
|
` Skills: github.com/let-sunny/canicode`
|
|
4718
4599
|
].join("\n")
|
|
4719
4600
|
}
|