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 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
- claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y canicode canicode-mcp
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/index.ts
2953
- var _trackEvent = () => {
2954
- };
2955
- var _trackError = () => {
2956
- };
2957
- var _shutdown = () => Promise.resolve();
2958
- function isBrowser() {
2959
- const g = globalThis;
2960
- return typeof g["window"] !== "undefined" && typeof g["document"] !== "undefined";
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
- async function initMonitoring(config2) {
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
- if (isBrowser()) {
2967
- const { initBrowserMonitoring: initBrowserMonitoring2, trackBrowserEvent: trackBrowserEvent2, trackBrowserError: trackBrowserError2, shutdownBrowserMonitoring: shutdownBrowserMonitoring2 } = await Promise.resolve().then(() => (init_browser(), browser_exports));
2968
- await initBrowserMonitoring2(config2);
2969
- _trackEvent = trackBrowserEvent2;
2970
- _trackError = trackBrowserError2;
2971
- _shutdown = shutdownBrowserMonitoring2;
2972
- } else {
2973
- const { initNodeMonitoring: initNodeMonitoring2, trackNodeEvent: trackNodeEvent2, trackNodeError: trackNodeError2, shutdownNodeMonitoring: shutdownNodeMonitoring2 } = await Promise.resolve().then(() => (init_node(), node_exports));
2974
- await initNodeMonitoring2(config2);
2975
- _trackEvent = trackNodeEvent2;
2976
- _trackError = trackNodeError2;
2977
- _shutdown = shutdownNodeMonitoring2;
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
- _trackEvent(event, properties);
2866
+ captureEvent(event, properties);
2985
2867
  } catch {
2986
2868
  }
2987
2869
  }
2988
2870
  function trackError(error, context) {
2989
2871
  try {
2990
- _trackError(error, context);
2872
+ captureError(error, context);
2991
2873
  } catch {
2992
2874
  }
2993
2875
  }
2994
- async function shutdownMonitoring() {
2876
+ function shutdownMonitoring() {
2995
2877
  try {
2996
- await _shutdown();
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
- initMonitoring(monitoringConfig).catch(() => {
4185
- });
4066
+ monitoringConfig.distinctId = getDeviceId();
4067
+ initMonitoring(monitoringConfig);
4186
4068
  }
4187
4069
  process.on("beforeExit", () => {
4188
- shutdownMonitoring().catch(() => {
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 --transport stdio canicode npx canicode-mcp`,
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
  }