brakit 0.9.0 → 0.9.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/dist/api.d.ts CHANGED
@@ -348,10 +348,12 @@ declare function detectProject(rootDir: string): Promise<DetectedProject>;
348
348
  declare class AdapterRegistry {
349
349
  private adapters;
350
350
  private active;
351
+ private failed;
351
352
  register(adapter: BrakitAdapter): void;
352
353
  patchAll(emit: (event: TelemetryEvent) => void): void;
353
354
  unpatchAll(): void;
354
355
  getActive(): readonly BrakitAdapter[];
356
+ getFailed(): readonly string[];
355
357
  }
356
358
 
357
359
  interface AnalysisUpdate {
package/dist/api.js CHANGED
@@ -54,6 +54,7 @@ var BASELINE_MIN_SESSIONS = 2;
54
54
  var BASELINE_MIN_REQUESTS_PER_SESSION = 3;
55
55
  var ISSUES_FILE = "issues.json";
56
56
  var ISSUES_FLUSH_INTERVAL_MS = 1e4;
57
+ var DETAIL_PREVIEW_LENGTH = 120;
57
58
 
58
59
  // src/utils/log.ts
59
60
  var PREFIX = "[brakit]";
@@ -429,6 +430,7 @@ var AdapterRegistry = class {
429
430
  constructor() {
430
431
  this.adapters = [];
431
432
  this.active = [];
433
+ this.failed = [];
432
434
  }
433
435
  register(adapter) {
434
436
  this.adapters.push(adapter);
@@ -441,6 +443,7 @@ var AdapterRegistry = class {
441
443
  this.active.push(adapter);
442
444
  }
443
445
  } catch {
446
+ this.failed.push(adapter.name);
444
447
  }
445
448
  }
446
449
  }
@@ -456,6 +459,9 @@ var AdapterRegistry = class {
456
459
  getActive() {
457
460
  return this.active;
458
461
  }
462
+ getFailed() {
463
+ return this.failed;
464
+ }
459
465
  };
460
466
 
461
467
  // src/utils/response.ts
@@ -774,7 +780,7 @@ var stackTraceLeakRule = {
774
780
  title: "Stack Trace Leaked to Client",
775
781
  desc: `${ep} \u2014 response exposes internal stack trace`,
776
782
  hint: this.hint,
777
- detail: firstLine ? `Stack trace: ${firstLine.slice(0, 120)}` : void 0,
783
+ detail: firstLine ? `Stack trace: ${firstLine.slice(0, DETAIL_PREVIEW_LENGTH)}` : void 0,
778
784
  endpoint: ep,
779
785
  count: 1
780
786
  }
@@ -1664,7 +1670,7 @@ var n1Rule = {
1664
1670
  title: "N+1 Query Pattern",
1665
1671
  desc: `${endpoint} runs ${shapeGroup.count}x ${info.op} ${info.table} with different params in a single request`,
1666
1672
  hint: "This typically happens when fetching related data in a loop. Use a batch query, JOIN, or include/eager-load to fetch all records at once.",
1667
- detail: `${shapeGroup.count} queries with ${shapeGroup.distinctSql.size} distinct param variations. Example: ${[...shapeGroup.distinctSql][0]?.slice(0, 100) ?? info.op + " " + info.table}`
1673
+ detail: `${shapeGroup.count} queries with ${shapeGroup.distinctSql.size} distinct param variations. Example: ${[...shapeGroup.distinctSql][0]?.slice(0, DETAIL_PREVIEW_LENGTH) ?? info.op + " " + info.table}`
1668
1674
  });
1669
1675
  }
1670
1676
  }
@@ -1703,7 +1709,7 @@ var redundantQueryRule = {
1703
1709
  title: "Redundant Query",
1704
1710
  desc: `${label} runs ${entry.count}x with identical params in ${endpoint}.`,
1705
1711
  hint: "The exact same query with identical parameters runs multiple times in one request. Cache the first result or lift the query to a shared function.",
1706
- detail: entry.first.sql ? `Query: ${entry.first.sql.slice(0, 120)}` : void 0
1712
+ detail: entry.first.sql ? `Query: ${entry.first.sql.slice(0, DETAIL_PREVIEW_LENGTH)}` : void 0
1707
1713
  });
1708
1714
  }
1709
1715
  }
@@ -2235,7 +2241,7 @@ var AnalysisEngine = class {
2235
2241
  };
2236
2242
 
2237
2243
  // src/index.ts
2238
- var VERSION = "0.9.0";
2244
+ var VERSION = "0.9.2";
2239
2245
  export {
2240
2246
  AdapterRegistry,
2241
2247
  AnalysisEngine,
@@ -10,7 +10,7 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/constants/config.ts
13
- var PROJECT_HASH_LENGTH, SECRET_SCAN_ARRAY_LIMIT, PII_SCAN_ARRAY_LIMIT, MIN_SECRET_VALUE_LENGTH, FULL_RECORD_MIN_FIELDS, LIST_PII_MIN_ITEMS, MAX_OBJECT_SCAN_DEPTH, ISSUE_PRUNE_TTL_MS, OVERFETCH_UNWRAP_MIN_SIZE, STALE_ISSUE_TTL_MS, METRICS_DIR, PORT_FILE, VALID_ISSUE_STATES, VALID_AI_FIX_STATUSES, VALID_SECURITY_SEVERITIES;
13
+ var PROJECT_HASH_LENGTH, SECRET_SCAN_ARRAY_LIMIT, PII_SCAN_ARRAY_LIMIT, MIN_SECRET_VALUE_LENGTH, FULL_RECORD_MIN_FIELDS, LIST_PII_MIN_ITEMS, MAX_OBJECT_SCAN_DEPTH, ISSUE_PRUNE_TTL_MS, OVERFETCH_UNWRAP_MIN_SIZE, STALE_ISSUE_TTL_MS, METRICS_DIR, PORT_FILE, VALID_ISSUE_STATES, VALID_AI_FIX_STATUSES, VALID_SECURITY_SEVERITIES, TELEMETRY_EVENT_CLI_INVOKED, TELEMETRY_EVENT_CLI_UNINSTALL, DETAIL_PREVIEW_LENGTH;
14
14
  var init_config = __esm({
15
15
  "src/constants/config.ts"() {
16
16
  "use strict";
@@ -29,6 +29,9 @@ var init_config = __esm({
29
29
  VALID_ISSUE_STATES = /* @__PURE__ */ new Set(["open", "fixing", "resolved", "stale", "regressed"]);
30
30
  VALID_AI_FIX_STATUSES = /* @__PURE__ */ new Set(["fixed", "wont_fix"]);
31
31
  VALID_SECURITY_SEVERITIES = /* @__PURE__ */ new Set(["critical", "warning"]);
32
+ TELEMETRY_EVENT_CLI_INVOKED = "cli_invoked";
33
+ TELEMETRY_EVENT_CLI_UNINSTALL = "cli_uninstall";
34
+ DETAIL_PREVIEW_LENGTH = 120;
32
35
  }
33
36
  });
34
37
 
@@ -70,7 +73,7 @@ var init_type_guards = __esm({
70
73
  });
71
74
 
72
75
  // src/constants/labels.ts
73
- var DASHBOARD_PREFIX, DASHBOARD_API_REQUESTS, DASHBOARD_API_EVENTS, DASHBOARD_API_FLOWS, DASHBOARD_API_CLEAR, DASHBOARD_API_LOGS, DASHBOARD_API_FETCHES, DASHBOARD_API_ERRORS, DASHBOARD_API_QUERIES, DASHBOARD_API_INGEST, DASHBOARD_API_METRICS, DASHBOARD_API_ACTIVITY, DASHBOARD_API_METRICS_LIVE, DASHBOARD_API_INSIGHTS, DASHBOARD_API_SECURITY, DASHBOARD_API_TAB, DASHBOARD_API_FINDINGS, DASHBOARD_API_FINDINGS_REPORT, VALID_TABS_TUPLE, VALID_TABS;
76
+ var DASHBOARD_PREFIX, DASHBOARD_API_REQUESTS, DASHBOARD_API_EVENTS, DASHBOARD_API_FLOWS, DASHBOARD_API_CLEAR, DASHBOARD_API_LOGS, DASHBOARD_API_FETCHES, DASHBOARD_API_ERRORS, DASHBOARD_API_QUERIES, DASHBOARD_API_INGEST, DASHBOARD_API_METRICS, DASHBOARD_API_ACTIVITY, DASHBOARD_API_METRICS_LIVE, DASHBOARD_API_INSIGHTS, DASHBOARD_API_SECURITY, DASHBOARD_API_TAB, DASHBOARD_API_FINDINGS, DASHBOARD_API_FINDINGS_REPORT, VALID_TABS_TUPLE, VALID_TABS, POSTHOG_HOST, POSTHOG_CAPTURE_PATH, POSTHOG_REQUEST_TIMEOUT_MS;
74
77
  var init_labels = __esm({
75
78
  "src/constants/labels.ts"() {
76
79
  "use strict";
@@ -104,11 +107,14 @@ var init_labels = __esm({
104
107
  "security"
105
108
  ];
106
109
  VALID_TABS = new Set(VALID_TABS_TUPLE);
110
+ POSTHOG_HOST = "https://us.i.posthog.com";
111
+ POSTHOG_CAPTURE_PATH = "/i/v0/e/";
112
+ POSTHOG_REQUEST_TIMEOUT_MS = 3e3;
107
113
  }
108
114
  });
109
115
 
110
116
  // src/constants/features.ts
111
- var SUPPORTED_SOURCE_EXTENSIONS, BUILD_CACHE_DIRS, FALLBACK_SCAN_DIRS, MCP_SERVER_NAME, INITIAL_DISCOVERY_TIMEOUT_MS, LAZY_DISCOVERY_TIMEOUT_MS, CLIENT_FETCH_TIMEOUT_MS, HEALTH_CHECK_TIMEOUT_MS, DISCOVERY_POLL_INTERVAL_MS, MAX_DISCOVERY_DEPTH, MAX_TIMELINE_EVENTS, MAX_RESOLVED_DISPLAY, ENRICHMENT_SEVERITY_FILTER, MCP_SERVER_VERSION, RECOVERY_WINDOW_MS, PORT_MIN, PORT_MAX;
117
+ var SUPPORTED_SOURCE_EXTENSIONS, BUILD_CACHE_DIRS, FALLBACK_SCAN_DIRS, MCP_SERVER_NAME, INITIAL_DISCOVERY_TIMEOUT_MS, LAZY_DISCOVERY_TIMEOUT_MS, CLIENT_FETCH_TIMEOUT_MS, HEALTH_CHECK_TIMEOUT_MS, DISCOVERY_POLL_INTERVAL_MS, MAX_DISCOVERY_DEPTH, MAX_TIMELINE_EVENTS, MAX_RESOLVED_DISPLAY, ENRICHMENT_SEVERITY_FILTER, MCP_SERVER_VERSION, RECOVERY_WINDOW_MS, PORT_MIN, PORT_MAX, DIR_MODE_OWNER_ONLY, FILE_MODE_OWNER_ONLY;
112
118
  var init_features = __esm({
113
119
  "src/constants/features.ts"() {
114
120
  "use strict";
@@ -132,10 +138,12 @@ var init_features = __esm({
132
138
  MAX_TIMELINE_EVENTS = 20;
133
139
  MAX_RESOLVED_DISPLAY = 5;
134
140
  ENRICHMENT_SEVERITY_FILTER = ["critical", "warning"];
135
- MCP_SERVER_VERSION = "0.9.0";
141
+ MCP_SERVER_VERSION = "0.9.2";
136
142
  RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
137
143
  PORT_MIN = 1;
138
144
  PORT_MAX = 65535;
145
+ DIR_MODE_OWNER_ONLY = 448;
146
+ FILE_MODE_OWNER_ONLY = 384;
139
147
  }
140
148
  });
141
149
 
@@ -307,8 +315,8 @@ async function searchForPort(startDir) {
307
315
  }
308
316
  return null;
309
317
  }
310
- async function discoverBrakitPort(cwd) {
311
- const port = await searchForPort(cwd ?? process.cwd());
318
+ async function discoverBrakitPort(cwd2) {
319
+ const port = await searchForPort(cwd2 ?? process.cwd());
312
320
  if (!port) {
313
321
  throw new Error(
314
322
  "Brakit is not running. Start your app with brakit enabled first."
@@ -316,11 +324,11 @@ async function discoverBrakitPort(cwd) {
316
324
  }
317
325
  return { port, baseUrl: `http://localhost:${port}` };
318
326
  }
319
- async function waitForBrakit(cwd, timeoutMs = 1e4, pollMs = DISCOVERY_POLL_INTERVAL_MS) {
327
+ async function waitForBrakit(cwd2, timeoutMs = 1e4, pollMs = DISCOVERY_POLL_INTERVAL_MS) {
320
328
  const deadline = Date.now() + timeoutMs;
321
329
  while (Date.now() < deadline) {
322
330
  try {
323
- const result = await discoverBrakitPort(cwd);
331
+ const result = await discoverBrakitPort(cwd2);
324
332
  const res = await fetch(`${result.baseUrl}${DASHBOARD_API_REQUESTS}?limit=1`);
325
333
  if (res.ok) return result;
326
334
  } catch {
@@ -1047,13 +1055,15 @@ var init_server = __esm({
1047
1055
 
1048
1056
  // bin/brakit.ts
1049
1057
  import { runMain } from "citty";
1058
+ import { existsSync as existsSync7 } from "fs";
1059
+ import { resolve as resolve6 } from "path";
1050
1060
 
1051
1061
  // src/cli/commands/install.ts
1052
1062
  import { defineCommand } from "citty";
1053
1063
  import { resolve as resolve3, join as join3, dirname } from "path";
1054
1064
  import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
1055
1065
  import { execSync } from "child_process";
1056
- import { existsSync as existsSync5 } from "fs";
1066
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
1057
1067
  import pc from "picocolors";
1058
1068
 
1059
1069
  // src/store/issue-store.ts
@@ -1563,7 +1573,7 @@ var stackTraceLeakRule = {
1563
1573
  title: "Stack Trace Leaked to Client",
1564
1574
  desc: `${ep} \u2014 response exposes internal stack trace`,
1565
1575
  hint: this.hint,
1566
- detail: firstLine ? `Stack trace: ${firstLine.slice(0, 120)}` : void 0,
1576
+ detail: firstLine ? `Stack trace: ${firstLine.slice(0, DETAIL_PREVIEW_LENGTH)}` : void 0,
1567
1577
  endpoint: ep,
1568
1578
  count: 1
1569
1579
  }
@@ -1802,7 +1812,7 @@ init_constants();
1802
1812
  init_endpoint();
1803
1813
 
1804
1814
  // src/index.ts
1805
- var VERSION = "0.9.0";
1815
+ var VERSION = "0.9.2";
1806
1816
 
1807
1817
  // src/cli/commands/install.ts
1808
1818
  init_constants();
@@ -1905,6 +1915,14 @@ var install_default = defineCommand({
1905
1915
  }
1906
1916
  process.exit(1);
1907
1917
  }
1918
+ const firstNode = nodeProjects[0];
1919
+ if (isFrontendOnly(firstNode.dir)) {
1920
+ console.log(pc.yellow(" \u26A0 This looks like a frontend-only app (React, Vue, etc.)."));
1921
+ console.log(pc.dim(" Brakit instruments your backend server, not the browser."));
1922
+ console.log(pc.dim(" Install brakit in your backend project instead (Express, Fastify, Next.js, etc.)."));
1923
+ console.log();
1924
+ process.exit(1);
1925
+ }
1908
1926
  for (const p of nodeProjects) {
1909
1927
  const node = p.node;
1910
1928
  const suffix = p.relDir === "." ? "" : ` in ${p.relDir}`;
@@ -1941,9 +1959,8 @@ var install_default = defineCommand({
1941
1959
  }
1942
1960
  }
1943
1961
  console.log();
1944
- const port = nodeProjects[0].node?.defaultPort ?? 3e3;
1945
1962
  console.log(pc.dim(" Start your app and visit:"));
1946
- console.log(pc.bold(` http://localhost:${port}/__brakit`));
1963
+ console.log(pc.bold(" http://localhost:<your-port>/__brakit"));
1947
1964
  if (pythonProjects.length > 0) {
1948
1965
  const pyLabel = pythonProjects.map((p) => p.relDir).join(", ");
1949
1966
  console.log();
@@ -2014,8 +2031,8 @@ async function setupNuxt(rootDir) {
2014
2031
  }
2015
2032
  const content = BRAKIT_TEMPLATES.nuxt + "\n";
2016
2033
  const dir = join3(rootDir, "server/plugins");
2017
- const { mkdirSync: mkdirSync3 } = await import("fs");
2018
- mkdirSync3(dir, { recursive: true });
2034
+ const { mkdirSync: mkdirSync4 } = await import("fs");
2035
+ mkdirSync4(dir, { recursive: true });
2019
2036
  await writeFile3(absPath, content);
2020
2037
  return { action: "created", file: relPath, content };
2021
2038
  }
@@ -2095,6 +2112,20 @@ function findGitRoot(startDir) {
2095
2112
  dir = parent;
2096
2113
  }
2097
2114
  }
2115
+ var FRONTEND_ONLY_DEPS = ["react", "vue", "svelte", "@angular/core", "solid-js", "preact"];
2116
+ var SERVER_DEPS = ["express", "fastify", "hono", "koa", "nest", "next", "@remix-run/dev", "nuxt", "astro"];
2117
+ function isFrontendOnly(rootDir) {
2118
+ try {
2119
+ const raw = readFileSync3(join3(rootDir, "package.json"), "utf-8");
2120
+ const pkg = JSON.parse(raw);
2121
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
2122
+ const hasFrontend = FRONTEND_ONLY_DEPS.some((d) => d in allDeps);
2123
+ const hasServer = SERVER_DEPS.some((d) => d in allDeps);
2124
+ return hasFrontend && !hasServer;
2125
+ } catch {
2126
+ return false;
2127
+ }
2128
+ }
2098
2129
  function printManualInstructions(framework) {
2099
2130
  console.log(pc.yellow(" \u26A0 Could not auto-detect entry file."));
2100
2131
  console.log();
@@ -2114,13 +2145,118 @@ function printManualInstructions(framework) {
2114
2145
 
2115
2146
  // src/cli/commands/uninstall.ts
2116
2147
  import { defineCommand as defineCommand2 } from "citty";
2117
- import { resolve as resolve4, join as join4, relative as relative2 } from "path";
2148
+ import { resolve as resolve4, join as join5, relative as relative2 } from "path";
2118
2149
  import { readFile as readFile5, writeFile as writeFile4, unlink, rm, readdir as readdir2 } from "fs/promises";
2119
2150
  import { execSync as execSync2 } from "child_process";
2120
2151
  import pc2 from "picocolors";
2121
2152
  init_constants();
2122
2153
  init_log();
2123
2154
  init_type_guards();
2155
+
2156
+ // src/telemetry/index.ts
2157
+ import { platform as platform2, release, arch } from "os";
2158
+ import { spawn } from "child_process";
2159
+
2160
+ // src/telemetry/config.ts
2161
+ init_features();
2162
+ import { homedir as homedir2, platform } from "os";
2163
+ import { join as join4 } from "path";
2164
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
2165
+ import { randomUUID as randomUUID2 } from "crypto";
2166
+ var IS_WINDOWS = platform() === "win32";
2167
+ var CONFIG_DIR = join4(homedir2(), ".brakit");
2168
+ var CONFIG_PATH = join4(CONFIG_DIR, "config.json");
2169
+ function readConfig() {
2170
+ try {
2171
+ if (!existsSync6(CONFIG_PATH)) return null;
2172
+ return JSON.parse(readFileSync4(CONFIG_PATH, "utf-8"));
2173
+ } catch {
2174
+ return null;
2175
+ }
2176
+ }
2177
+ function writeConfig(config) {
2178
+ try {
2179
+ if (!existsSync6(CONFIG_DIR))
2180
+ mkdirSync3(CONFIG_DIR, { recursive: true, ...IS_WINDOWS ? {} : { mode: DIR_MODE_OWNER_ONLY } });
2181
+ writeFileSync3(
2182
+ CONFIG_PATH,
2183
+ JSON.stringify(config, null, 2) + "\n",
2184
+ IS_WINDOWS ? {} : { mode: FILE_MODE_OWNER_ONLY }
2185
+ );
2186
+ } catch (err) {
2187
+ if (process.env.BRAKIT_DEBUG) {
2188
+ process.stderr.write(`[brakit] config write failed: ${err?.message ?? err}
2189
+ `);
2190
+ }
2191
+ }
2192
+ }
2193
+ function getOrCreateConfig() {
2194
+ const existing = readConfig();
2195
+ if (existing && typeof existing.telemetry === "boolean" && existing.anonymousId) {
2196
+ return existing;
2197
+ }
2198
+ const config = { telemetry: true, anonymousId: randomUUID2() };
2199
+ writeConfig(config);
2200
+ return config;
2201
+ }
2202
+ var cachedEnabled = null;
2203
+ function isTelemetryEnabled() {
2204
+ if (cachedEnabled !== null) return cachedEnabled;
2205
+ const env = process.env.BRAKIT_TELEMETRY;
2206
+ if (env !== void 0) {
2207
+ cachedEnabled = env !== "false" && env !== "0" && env !== "off";
2208
+ return cachedEnabled;
2209
+ }
2210
+ cachedEnabled = readConfig()?.telemetry ?? true;
2211
+ return cachedEnabled;
2212
+ }
2213
+
2214
+ // src/telemetry/index.ts
2215
+ init_labels();
2216
+ init_config();
2217
+ var POSTHOG_KEY = "phc_E9TwydCGnSfPLIUhNxChpeg32TSowjk31KiPhnLPP0x";
2218
+ function commonProperties() {
2219
+ return {
2220
+ brakit_version: VERSION,
2221
+ node_version: process.version,
2222
+ os: `${platform2()}-${release()}`,
2223
+ arch: arch(),
2224
+ $lib: "brakit",
2225
+ $process_person_profile: false,
2226
+ $geoip_disable: true
2227
+ };
2228
+ }
2229
+ function sendToPosthog(event, properties) {
2230
+ if (!isTelemetryEnabled()) return;
2231
+ const config = getOrCreateConfig();
2232
+ const payload = {
2233
+ api_key: POSTHOG_KEY,
2234
+ event,
2235
+ distinct_id: config.anonymousId,
2236
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2237
+ properties: { ...commonProperties(), ...properties }
2238
+ };
2239
+ try {
2240
+ const body = JSON.stringify(payload);
2241
+ const url = `${POSTHOG_HOST}${POSTHOG_CAPTURE_PATH}`;
2242
+ const child = spawn(
2243
+ process.execPath,
2244
+ [
2245
+ "-e",
2246
+ `fetch(${JSON.stringify(url)},{method:"POST",headers:{"content-type":"application/json"},body:${JSON.stringify(body)},signal:AbortSignal.timeout(${POSTHOG_REQUEST_TIMEOUT_MS})}).catch(()=>{})`
2247
+ ],
2248
+ { detached: true, stdio: "ignore" }
2249
+ );
2250
+ child.unref();
2251
+ } catch {
2252
+ }
2253
+ }
2254
+ function trackEvent(event, properties) {
2255
+ sendToPosthog(event, { sdk: "node", ...properties });
2256
+ }
2257
+
2258
+ // src/cli/commands/uninstall.ts
2259
+ init_config();
2124
2260
  var PREPENDED_FILES = [
2125
2261
  "app/entry.server.tsx",
2126
2262
  "app/entry.server.ts",
@@ -2155,16 +2291,20 @@ var uninstall_default = defineCommand2({
2155
2291
  console.log();
2156
2292
  console.log(pc2.bold(" \u25C6 brakit uninstall"));
2157
2293
  console.log();
2294
+ let anyInstrumentationRemoved = false;
2295
+ let anyPackageRemoved = false;
2158
2296
  for (const project of projects) {
2159
2297
  const suffix = projects.length > 1 ? ` in ${relative2(rootDir, project.dir) || "."}` : "";
2160
2298
  const removed = await removeInstrumentation(project.dir);
2161
2299
  if (removed) {
2300
+ anyInstrumentationRemoved = true;
2162
2301
  console.log(pc2.green(` \u2713 ${removed}${suffix}`));
2163
2302
  } else {
2164
2303
  console.log(pc2.dim(` No brakit instrumentation files found${suffix}.`));
2165
2304
  }
2166
2305
  const uninstalled = await uninstallPackage(project.dir, project.pm);
2167
2306
  if (uninstalled === true) {
2307
+ anyPackageRemoved = true;
2168
2308
  console.log(pc2.green(` \u2713 Removed brakit from devDependencies${suffix}`));
2169
2309
  } else if (uninstalled === "failed") {
2170
2310
  }
@@ -2185,6 +2325,12 @@ var uninstall_default = defineCommand2({
2185
2325
  if (cacheCleared) {
2186
2326
  console.log(pc2.green(" \u2713 Cleared build cache"));
2187
2327
  }
2328
+ trackEvent(TELEMETRY_EVENT_CLI_UNINSTALL, {
2329
+ instrumentation_removed: anyInstrumentationRemoved,
2330
+ package_removed: anyPackageRemoved,
2331
+ mcp_removed: mcpRemoved,
2332
+ data_removed: dataRemoved
2333
+ });
2188
2334
  console.log();
2189
2335
  }
2190
2336
  });
@@ -2195,7 +2341,7 @@ async function removeInstrumentation(projectDir) {
2195
2341
  }
2196
2342
  const candidates = [...PREPENDED_FILES];
2197
2343
  try {
2198
- const pkgRaw = await readFile5(join4(projectDir, "package.json"), "utf-8");
2344
+ const pkgRaw = await readFile5(join5(projectDir, "package.json"), "utf-8");
2199
2345
  const pkg = JSON.parse(pkgRaw);
2200
2346
  if (pkg.main) candidates.unshift(pkg.main);
2201
2347
  } catch (err) {
@@ -2210,7 +2356,7 @@ async function removeInstrumentation(projectDir) {
2210
2356
  return null;
2211
2357
  }
2212
2358
  async function tryRemoveBrakitFromFile(projectDir, relPath) {
2213
- const absPath = join4(projectDir, relPath);
2359
+ const absPath = join5(projectDir, relPath);
2214
2360
  if (!await fileExists(absPath)) return null;
2215
2361
  const content = await readFile5(absPath, "utf-8");
2216
2362
  if (!content.includes("brakit")) return null;
@@ -2227,7 +2373,7 @@ async function tryRemoveBrakitFromFile(projectDir, relPath) {
2227
2373
  return null;
2228
2374
  }
2229
2375
  async function tryRemoveImportLine(projectDir, relPath) {
2230
- const absPath = join4(projectDir, relPath);
2376
+ const absPath = join5(projectDir, relPath);
2231
2377
  if (!await fileExists(absPath)) return null;
2232
2378
  const content = await readFile5(absPath, "utf-8");
2233
2379
  if (!content.includes(IMPORT_LINE)) return null;
@@ -2238,7 +2384,7 @@ async function tryRemoveImportLine(projectDir, relPath) {
2238
2384
  async function fallbackSearchAndRemove(projectDir) {
2239
2385
  const dirsToScan = FALLBACK_SCAN_DIRS;
2240
2386
  for (const dir of dirsToScan) {
2241
- const absDir = join4(projectDir, dir);
2387
+ const absDir = join5(projectDir, dir);
2242
2388
  if (!await fileExists(absDir)) continue;
2243
2389
  let entries;
2244
2390
  try {
@@ -2251,7 +2397,7 @@ async function fallbackSearchAndRemove(projectDir) {
2251
2397
  const ext = entry.slice(entry.lastIndexOf("."));
2252
2398
  if (!SUPPORTED_SOURCE_EXTENSIONS.has(ext)) continue;
2253
2399
  const relPath = dir === "." ? entry : `${dir}/${entry}`;
2254
- const absPath = join4(projectDir, relPath);
2400
+ const absPath = join5(projectDir, relPath);
2255
2401
  try {
2256
2402
  const content = await readFile5(absPath, "utf-8");
2257
2403
  if (!containsBrakitImport(content)) continue;
@@ -2274,7 +2420,7 @@ async function fallbackSearchAndRemove(projectDir) {
2274
2420
  return null;
2275
2421
  }
2276
2422
  async function removeMcpConfig(rootDir) {
2277
- const mcpPath = join4(rootDir, ".mcp.json");
2423
+ const mcpPath = join5(rootDir, ".mcp.json");
2278
2424
  if (!await fileExists(mcpPath)) return false;
2279
2425
  try {
2280
2426
  const raw = await readFile5(mcpPath, "utf-8");
@@ -2294,7 +2440,7 @@ async function removeMcpConfig(rootDir) {
2294
2440
  }
2295
2441
  async function uninstallPackage(rootDir, pm) {
2296
2442
  try {
2297
- const pkgRaw = await readFile5(join4(rootDir, "package.json"), "utf-8");
2443
+ const pkgRaw = await readFile5(join5(rootDir, "package.json"), "utf-8");
2298
2444
  const pkg = JSON.parse(pkgRaw);
2299
2445
  if (!pkg.devDependencies?.brakit && !pkg.dependencies?.brakit) return false;
2300
2446
  } catch (err) {
@@ -2318,7 +2464,7 @@ async function uninstallPackage(rootDir, pm) {
2318
2464
  }
2319
2465
  async function removeBrakitData(rootDir) {
2320
2466
  let removed = false;
2321
- const projectDir = join4(rootDir, METRICS_DIR);
2467
+ const projectDir = join5(rootDir, METRICS_DIR);
2322
2468
  if (await fileExists(projectDir)) {
2323
2469
  try {
2324
2470
  await rm(projectDir, { recursive: true, force: true });
@@ -2339,7 +2485,7 @@ async function removeBrakitData(rootDir) {
2339
2485
  return removed;
2340
2486
  }
2341
2487
  async function cleanGitignore(rootDir) {
2342
- const gitignorePath = join4(rootDir, ".gitignore");
2488
+ const gitignorePath = join5(rootDir, ".gitignore");
2343
2489
  if (!await fileExists(gitignorePath)) return false;
2344
2490
  try {
2345
2491
  const content = await readFile5(gitignorePath, "utf-8");
@@ -2356,7 +2502,7 @@ async function cleanGitignore(rootDir) {
2356
2502
  async function clearBuildCaches(rootDir) {
2357
2503
  let cleared = false;
2358
2504
  for (const dir of BUILD_CACHE_DIRS) {
2359
- const absDir = join4(rootDir, dir);
2505
+ const absDir = join5(rootDir, dir);
2360
2506
  if (!await fileExists(absDir)) continue;
2361
2507
  try {
2362
2508
  await rm(absDir, { recursive: true, force: true });
@@ -2369,7 +2515,15 @@ async function clearBuildCaches(rootDir) {
2369
2515
  }
2370
2516
 
2371
2517
  // bin/brakit.ts
2518
+ init_config();
2372
2519
  var sub = process.argv[2];
2520
+ var command = sub === "uninstall" ? "uninstall" : sub === "mcp" ? "mcp" : "install";
2521
+ var cwd = process.cwd();
2522
+ trackEvent(TELEMETRY_EVENT_CLI_INVOKED, {
2523
+ command,
2524
+ has_package_json: existsSync7(resolve6(cwd, "package.json")),
2525
+ cwd_has_node_modules: existsSync7(resolve6(cwd, "node_modules"))
2526
+ });
2373
2527
  if (sub === "uninstall") {
2374
2528
  process.argv.splice(2, 1);
2375
2529
  runMain(uninstall_default);