canicode 0.6.1 → 0.6.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/README.md CHANGED
@@ -103,7 +103,16 @@ 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
+ ```bash
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):**
107
116
  ```bash
108
117
  claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y -p canicode canicode-mcp
109
118
  ```
@@ -114,7 +123,7 @@ claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y -p canicode
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 -p canicode
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
@@ -2,209 +2,13 @@
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
3
  import { writeFile, readFile, appendFile } from 'fs/promises';
4
4
  import { join, resolve, dirname, basename } from 'path';
5
+ import { createRequire } from 'module';
5
6
  import { config } from 'dotenv';
6
7
  import cac from 'cac';
7
8
  import { z } from 'zod';
9
+ import { randomUUID } from 'crypto';
8
10
  import { homedir } from 'os';
9
11
 
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
12
  z.object({
209
13
  fileKey: z.string(),
210
14
  nodeId: z.string().optional(),
@@ -1114,6 +918,14 @@ function getPosthogApiKey() {
1114
918
  function getSentryDsn() {
1115
919
  return process.env["SENTRY_DSN"] ?? readConfig().sentryDsn;
1116
920
  }
921
+ function getDeviceId() {
922
+ const config2 = readConfig();
923
+ if (config2.deviceId) return config2.deviceId;
924
+ const id = randomUUID();
925
+ config2.deviceId = id;
926
+ writeConfig(config2);
927
+ return id;
928
+ }
1117
929
  function initAiready(token) {
1118
930
  setFigmaToken(token);
1119
931
  ensureReportsDir();
@@ -2949,51 +2761,122 @@ var EVENTS = {
2949
2761
  CLI_INIT: `${EVENT_PREFIX}cli_init`
2950
2762
  };
2951
2763
 
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";
2764
+ // src/monitoring/capture.ts
2765
+ var monitoringEnabled = false;
2766
+ var posthogApiKey;
2767
+ var sentryDsn;
2768
+ var distinctId = "anonymous";
2769
+ var commonProps = {};
2770
+ function uuid4() {
2771
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
2772
+ const r = Math.random() * 16 | 0;
2773
+ const v = c === "x" ? r : r & 3 | 8;
2774
+ return v.toString(16);
2775
+ });
2961
2776
  }
2962
- async function initMonitoring(config2) {
2777
+ function parseSentryDsn(dsn) {
2778
+ try {
2779
+ const url = new URL(dsn);
2780
+ const key = url.username;
2781
+ const projectId = url.pathname.slice(1);
2782
+ const host = url.protocol + "//" + url.host;
2783
+ if (!key || !projectId) return null;
2784
+ return { key, host, projectId };
2785
+ } catch {
2786
+ return null;
2787
+ }
2788
+ }
2789
+ function initCapture(config2) {
2963
2790
  if (config2.enabled === false) return;
2964
2791
  if (!config2.posthogApiKey && !config2.sentryDsn) return;
2792
+ monitoringEnabled = true;
2793
+ posthogApiKey = config2.posthogApiKey;
2794
+ sentryDsn = config2.sentryDsn;
2795
+ distinctId = config2.distinctId ?? "anonymous";
2796
+ commonProps = {
2797
+ _sdk: "canicode",
2798
+ _sdk_version: config2.version ?? "unknown",
2799
+ _env: config2.environment ?? "unknown"
2800
+ };
2801
+ }
2802
+ function captureEvent(event, properties) {
2803
+ if (!monitoringEnabled || !posthogApiKey) return;
2965
2804
  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
- }
2805
+ fetch("https://us.i.posthog.com/i/v0/e/", {
2806
+ method: "POST",
2807
+ headers: { "Content-Type": "application/json" },
2808
+ body: JSON.stringify({
2809
+ api_key: posthogApiKey,
2810
+ event,
2811
+ distinct_id: distinctId,
2812
+ properties: { ...commonProps, ...properties },
2813
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2814
+ })
2815
+ }).catch(() => {
2816
+ });
2979
2817
  } catch {
2980
2818
  }
2981
2819
  }
2820
+ function captureError(error, context) {
2821
+ if (!monitoringEnabled) return;
2822
+ if (sentryDsn) {
2823
+ const parsed = parseSentryDsn(sentryDsn);
2824
+ if (parsed) {
2825
+ try {
2826
+ const eventId = uuid4();
2827
+ const envelope = [
2828
+ JSON.stringify({ event_id: eventId, sent_at: (/* @__PURE__ */ new Date()).toISOString(), dsn: sentryDsn }),
2829
+ JSON.stringify({ type: "event", content_type: "application/json" }),
2830
+ JSON.stringify({
2831
+ event_id: eventId,
2832
+ exception: { values: [{ type: error.name, value: error.message }] },
2833
+ platform: "node",
2834
+ timestamp: Date.now() / 1e3,
2835
+ extra: context
2836
+ })
2837
+ ].join("\n");
2838
+ fetch(`${parsed.host}/api/${parsed.projectId}/envelope/`, {
2839
+ method: "POST",
2840
+ headers: {
2841
+ "Content-Type": "application/x-sentry-envelope",
2842
+ "X-Sentry-Auth": `Sentry sentry_version=7, sentry_key=${parsed.key}`
2843
+ },
2844
+ body: envelope
2845
+ }).catch(() => {
2846
+ });
2847
+ } catch {
2848
+ }
2849
+ }
2850
+ }
2851
+ captureEvent("cic_error", { error: error.message, ...context });
2852
+ }
2853
+ function shutdownCapture() {
2854
+ monitoringEnabled = false;
2855
+ posthogApiKey = void 0;
2856
+ sentryDsn = void 0;
2857
+ distinctId = "anonymous";
2858
+ commonProps = {};
2859
+ }
2860
+
2861
+ // src/monitoring/index.ts
2862
+ function initMonitoring(config2) {
2863
+ initCapture(config2);
2864
+ }
2982
2865
  function trackEvent(event, properties) {
2983
2866
  try {
2984
- _trackEvent(event, properties);
2867
+ captureEvent(event, properties);
2985
2868
  } catch {
2986
2869
  }
2987
2870
  }
2988
2871
  function trackError(error, context) {
2989
2872
  try {
2990
- _trackError(error, context);
2873
+ captureError(error, context);
2991
2874
  } catch {
2992
2875
  }
2993
2876
  }
2994
- async function shutdownMonitoring() {
2877
+ function shutdownMonitoring() {
2995
2878
  try {
2996
- await _shutdown();
2879
+ shutdownCapture();
2997
2880
  } catch {
2998
2881
  }
2999
2882
  }
@@ -4170,23 +4053,24 @@ defineRule({
4170
4053
 
4171
4054
  // src/cli/index.ts
4172
4055
  config();
4056
+ var require2 = createRequire(import.meta.url);
4057
+ var pkg = require2("../../package.json");
4173
4058
  var cli = cac("canicode");
4174
4059
  {
4175
4060
  const monitoringConfig = {
4176
4061
  environment: "cli",
4177
- version: "0.3.3",
4062
+ version: pkg.version,
4178
4063
  enabled: getTelemetryEnabled()
4179
4064
  };
4180
4065
  const phKey = getPosthogApiKey() || POSTHOG_API_KEY;
4181
4066
  monitoringConfig.posthogApiKey = phKey;
4182
4067
  const sDsn = getSentryDsn() || SENTRY_DSN;
4183
4068
  monitoringConfig.sentryDsn = sDsn;
4184
- initMonitoring(monitoringConfig).catch(() => {
4185
- });
4069
+ monitoringConfig.distinctId = getDeviceId();
4070
+ initMonitoring(monitoringConfig);
4186
4071
  }
4187
4072
  process.on("beforeExit", () => {
4188
- shutdownMonitoring().catch(() => {
4189
- });
4073
+ shutdownMonitoring();
4190
4074
  });
4191
4075
  var MAX_NODES_WITHOUT_SCOPE = 500;
4192
4076
  function pickRandomScope(root) {