@code-insights/cli 3.2.0 → 3.3.1

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +10 -6
  3. package/dashboard-dist/assets/index-Rr1JlICa.js +552 -0
  4. package/dashboard-dist/favicon.svg +17 -3
  5. package/dashboard-dist/index.html +1 -1
  6. package/dist/commands/config.js +2 -2
  7. package/dist/commands/config.js.map +1 -1
  8. package/dist/commands/dashboard.d.ts.map +1 -1
  9. package/dist/commands/dashboard.js +10 -2
  10. package/dist/commands/dashboard.js.map +1 -1
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/init.js +5 -2
  13. package/dist/commands/init.js.map +1 -1
  14. package/dist/commands/install-hook.d.ts.map +1 -1
  15. package/dist/commands/install-hook.js +48 -40
  16. package/dist/commands/install-hook.js.map +1 -1
  17. package/dist/commands/open.js +2 -2
  18. package/dist/commands/open.js.map +1 -1
  19. package/dist/commands/reset.d.ts.map +1 -1
  20. package/dist/commands/reset.js +5 -2
  21. package/dist/commands/reset.js.map +1 -1
  22. package/dist/commands/stats/actions/cost.d.ts.map +1 -1
  23. package/dist/commands/stats/actions/cost.js +6 -3
  24. package/dist/commands/stats/actions/cost.js.map +1 -1
  25. package/dist/commands/stats/actions/models.d.ts.map +1 -1
  26. package/dist/commands/stats/actions/models.js +6 -3
  27. package/dist/commands/stats/actions/models.js.map +1 -1
  28. package/dist/commands/stats/actions/overview.d.ts.map +1 -1
  29. package/dist/commands/stats/actions/overview.js +6 -3
  30. package/dist/commands/stats/actions/overview.js.map +1 -1
  31. package/dist/commands/stats/actions/projects.d.ts.map +1 -1
  32. package/dist/commands/stats/actions/projects.js +6 -3
  33. package/dist/commands/stats/actions/projects.js.map +1 -1
  34. package/dist/commands/stats/actions/today.d.ts.map +1 -1
  35. package/dist/commands/stats/actions/today.js +6 -3
  36. package/dist/commands/stats/actions/today.js.map +1 -1
  37. package/dist/commands/status.d.ts.map +1 -1
  38. package/dist/commands/status.js +66 -58
  39. package/dist/commands/status.js.map +1 -1
  40. package/dist/commands/sync.d.ts +1 -0
  41. package/dist/commands/sync.d.ts.map +1 -1
  42. package/dist/commands/sync.js +37 -4
  43. package/dist/commands/sync.js.map +1 -1
  44. package/dist/commands/telemetry.d.ts.map +1 -1
  45. package/dist/commands/telemetry.js +10 -14
  46. package/dist/commands/telemetry.js.map +1 -1
  47. package/dist/utils/banner.d.ts +6 -0
  48. package/dist/utils/banner.d.ts.map +1 -0
  49. package/dist/utils/banner.js +30 -0
  50. package/dist/utils/banner.js.map +1 -0
  51. package/dist/utils/telemetry.d.ts +52 -31
  52. package/dist/utils/telemetry.d.ts.map +1 -1
  53. package/dist/utils/telemetry.js +179 -187
  54. package/dist/utils/telemetry.js.map +1 -1
  55. package/dist/utils/welcome.d.ts.map +1 -1
  56. package/dist/utils/welcome.js +2 -3
  57. package/dist/utils/welcome.js.map +1 -1
  58. package/package.json +3 -2
  59. package/server-dist/index.d.ts.map +1 -1
  60. package/server-dist/index.js +13 -6
  61. package/server-dist/index.js.map +1 -1
  62. package/server-dist/llm/analysis.d.ts +4 -1
  63. package/server-dist/llm/analysis.d.ts.map +1 -1
  64. package/server-dist/llm/analysis.js +23 -8
  65. package/server-dist/llm/analysis.js.map +1 -1
  66. package/server-dist/llm/prompts.d.ts +14 -2
  67. package/server-dist/llm/prompts.d.ts.map +1 -1
  68. package/server-dist/llm/prompts.js +60 -36
  69. package/server-dist/llm/prompts.js.map +1 -1
  70. package/server-dist/routes/analysis.d.ts.map +1 -1
  71. package/server-dist/routes/analysis.js +135 -24
  72. package/server-dist/routes/analysis.js.map +1 -1
  73. package/server-dist/routes/export.js +1 -1
  74. package/server-dist/routes/export.js.map +1 -1
  75. package/server-dist/routes/telemetry.d.ts +4 -0
  76. package/server-dist/routes/telemetry.d.ts.map +1 -0
  77. package/server-dist/routes/telemetry.js +21 -0
  78. package/server-dist/routes/telemetry.js.map +1 -0
  79. package/dashboard-dist/assets/index-DzBnL32c.js +0 -549
@@ -3,16 +3,18 @@ import * as path from 'path';
3
3
  import * as os from 'os';
4
4
  import * as crypto from 'crypto';
5
5
  import { createRequire } from 'module';
6
- import { loadConfig, getConfigDir, getClaudeDir } from './config.js';
7
- const TELEMETRY_ENDPOINT = 'https://xrbkoqjfolxiyfxubiom.supabase.co/functions/v1/cli-telemetry-v2';
8
- // HMAC signing key for request integrity.
9
- // This is a deterrent, NOT a security boundary — npm packages are public and this key
10
- // is extractable. The real security is enforced server-side in the Edge Function.
11
- // Rotation: bump the endpoint version (cli-telemetry-v3) to rotate the key.
12
- // The Edge Function accepts keys as a Supabase secret (HMAC_KEYS env var), not hardcoded.
13
- const HMAC_KEY = 'fb73eabc121b8fe0bde2eb6f8a2d09c21bf74708d2148d7a520bb00661f03b7f';
14
- // Touch file path that tracks whether the one-time disclosure has been shown
6
+ import { PostHog } from 'posthog-node';
7
+ import { loadConfig, getConfigDir } from './config.js';
8
+ // PostHog write-only API key (public this is the standard PostHog pattern;
9
+ // write-only keys can only ingest events, not read data).
10
+ const POSTHOG_API_KEY = 'phc_552ZSApq5xuagswylfdw2vx8nckm31jn6LCpTVyVn8j';
11
+ const POSTHOG_HOST = 'https://us.i.posthog.com';
12
+ // Touch file path that tracks whether the disclosure has been shown.
13
+ // Content is the CLI version — if version doesn't match current, notice is re-shown.
15
14
  const NOTICE_FILE = path.join(getConfigDir(), '.telemetry-notice-shown');
15
+ // PostHog client — lazily initialized on first trackEvent call.
16
+ // null when telemetry is disabled or init hasn't happened yet.
17
+ let client = null;
16
18
  /**
17
19
  * Check if telemetry is enabled.
18
20
  *
@@ -28,123 +30,224 @@ export function isTelemetryEnabled() {
28
30
  if (process.env.DO_NOT_TRACK === '1')
29
31
  return false;
30
32
  const config = loadConfig();
31
- // If explicitly set in config, respect it
32
33
  if (config !== null && typeof config.telemetry === 'boolean') {
33
34
  return config.telemetry;
34
35
  }
35
- // Default: enabled (opt-out model)
36
36
  return true;
37
37
  }
38
38
  /**
39
- * Show the one-time telemetry disclosure notice if it hasn't been shown yet.
39
+ * Get (or lazily create) the PostHog client.
40
+ * Returns null when telemetry is disabled.
40
41
  *
41
- * Uses a touch file at ~/.code-insights/.telemetry-notice-shown to track state.
42
- * Only displays if telemetry is currently enabled no point disclosing disabled telemetry.
42
+ * flushAt: 1 flush immediately after each capture(); CLI is short-lived
43
+ * flushInterval: 0 no background timer; avoids keeping process alive
44
+ */
45
+ function getPostHogClient() {
46
+ if (!isTelemetryEnabled())
47
+ return null;
48
+ if (!client) {
49
+ client = new PostHog(POSTHOG_API_KEY, {
50
+ host: POSTHOG_HOST,
51
+ flushAt: 1,
52
+ flushInterval: 0,
53
+ });
54
+ }
55
+ return client;
56
+ }
57
+ /**
58
+ * Flush and shut down the PostHog client.
59
+ * Call this in server SIGINT/SIGTERM handlers before process.exit().
60
+ * No-op if telemetry is disabled or client was never initialized.
61
+ */
62
+ export async function shutdownTelemetry() {
63
+ if (client) {
64
+ await client.shutdown();
65
+ client = null;
66
+ }
67
+ }
68
+ /**
69
+ * Show the telemetry disclosure notice if it hasn't been shown for this CLI version.
43
70
  *
44
- * Returns true if the notice was shown, false if it was already shown or telemetry is off.
71
+ * Uses a version-stamped touch file at ~/.code-insights/.telemetry-notice-shown.
72
+ * Re-shown when the CLI version changes (catches existing users on upgrades).
73
+ * Only displays if telemetry is enabled.
74
+ *
75
+ * Returns true if the notice was shown.
45
76
  */
46
77
  export function showTelemetryNoticeIfNeeded() {
47
78
  if (!isTelemetryEnabled())
48
79
  return false;
49
- if (fs.existsSync(NOTICE_FILE))
80
+ const currentVersion = getCliVersion();
81
+ let shownVersion = null;
82
+ if (fs.existsSync(NOTICE_FILE)) {
83
+ try {
84
+ shownVersion = fs.readFileSync(NOTICE_FILE, 'utf-8').trim();
85
+ }
86
+ catch {
87
+ // Can't read — treat as not shown
88
+ }
89
+ }
90
+ if (shownVersion === currentVersion)
50
91
  return false;
51
- // Show the disclosure banner
92
+ // Show the updated disclosure banner
52
93
  console.log('');
53
- console.log(' Code Insights collects anonymous usage data to improve the CLI.');
54
- console.log(' Includes: command name, OS, CLI version, AI tool types.');
55
- console.log(' Never includes: file paths, project names, session content, or personal data.');
94
+ console.log(' Code Insights collects anonymous usage data to improve the CLI and dashboard.');
95
+ console.log(' Includes: commands, page views, OS, CLI version, AI tool types, session counts,');
96
+ console.log(' LLM provider, performance timing.');
97
+ console.log(' Never includes: file paths, project names, session content, API keys, or personal data.');
56
98
  console.log('');
57
99
  console.log(' Disable: code-insights telemetry disable');
58
100
  console.log(' Details: code-insights telemetry status');
59
101
  console.log('');
60
- // Mark notice as shown — best-effort write, ignore failures
102
+ // Write the current version as content — best-effort, non-fatal
61
103
  try {
62
- // Ensure config dir exists before writing touch file
63
104
  const configDir = getConfigDir();
64
105
  if (!fs.existsSync(configDir)) {
65
106
  fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
66
107
  }
67
- fs.writeFileSync(NOTICE_FILE, '', { encoding: 'utf-8', mode: 0o600 });
108
+ fs.writeFileSync(NOTICE_FILE, currentVersion, { encoding: 'utf-8', mode: 0o600 });
68
109
  }
69
110
  catch {
70
- // Non-fatal — if we can't write the touch file, we'll show the notice again
71
- // next time. That's acceptable; we don't want to break the CLI over this.
111
+ // Non-fatal — if we can't write, we'll show the notice again next time
72
112
  }
73
113
  return true;
74
114
  }
75
115
  /**
76
- * Fire-and-forget telemetry event. Does NOT block the calling command.
116
+ * Classify an error into a structured error_type + error_message pair.
117
+ * Used to enrich trackEvent calls and captureError calls with consistent error metadata.
118
+ */
119
+ export function classifyError(error) {
120
+ if (error instanceof Error) {
121
+ if (error.name === 'AbortError') {
122
+ return { error_type: 'abort', error_message: error.message };
123
+ }
124
+ // SyntaxError from JSON.parse
125
+ if (error instanceof SyntaxError) {
126
+ return { error_type: 'json_parse_error', error_message: error.message };
127
+ }
128
+ return { error_type: 'api_error', error_message: error.message };
129
+ }
130
+ return { error_type: 'unknown', error_message: String(error) };
131
+ }
132
+ /**
133
+ * Capture an exception in PostHog. Never throws — telemetry must never break the CLI.
134
+ * Respects the same opt-out as trackEvent.
77
135
  *
78
- * Design principles:
79
- * - Never awaited: the caller doesn't wait for this
80
- * - 2s AbortController timeout: we don't hang if the endpoint is slow
81
- * - Swallows ALL errors: network failures, JSON errors, anything — telemetry
82
- * must never cause a CLI command to fail
83
- * - No retries: if it fails, it fails. Reliability of individual events
84
- * matters less than not disrupting the user's workflow.
136
+ * @param error - The caught error (or unknown value)
137
+ * @param properties - Additional context properties (provider, model, etc.)
85
138
  */
86
- export function trackEvent(command, success, subcommand) {
87
- if (!isTelemetryEnabled())
139
+ export function captureError(error, properties) {
140
+ const ph = getPostHogClient();
141
+ if (!ph)
88
142
  return;
89
- // Build the event synchronously — filesystem reads happen here
90
- let event;
91
143
  try {
92
- event = buildEvent(command, success, subcommand);
144
+ const { error_type, error_message } = classifyError(error);
145
+ ph.capture({
146
+ distinctId: getStableMachineId(),
147
+ event: '$exception',
148
+ properties: {
149
+ $exception_message: error_message,
150
+ $exception_type: error_type,
151
+ ...(properties ?? {}),
152
+ },
153
+ });
93
154
  }
94
155
  catch {
95
- // If building the event fails (e.g., filesystem read error), silently skip
156
+ // Swallow all errors telemetry failures are silent
157
+ }
158
+ }
159
+ /**
160
+ * Send a telemetry event. Never throws — telemetry must never break the CLI.
161
+ *
162
+ * @param event - Event name from TelemetryEventName union
163
+ * @param properties - Arbitrary event properties (success, duration_ms, etc.)
164
+ */
165
+ export function trackEvent(event, properties) {
166
+ const ph = getPostHogClient();
167
+ if (!ph)
96
168
  return;
169
+ try {
170
+ ph.capture({
171
+ distinctId: getStableMachineId(),
172
+ event,
173
+ properties: properties ?? {},
174
+ });
175
+ }
176
+ catch {
177
+ // Swallow all errors — telemetry failures are silent
97
178
  }
98
- // Fire-and-forget: intentionally not awaited
99
- // The void cast suppresses the "floating promise" lint warning
100
- void sendEvent(event);
101
179
  }
102
180
  /**
103
- * Build the event payload that would be sent for the given command.
104
- * Exported for use by `telemetry status` to show a preview without sending.
181
+ * Set person-level properties via PostHog identify().
182
+ * Call once after the DB is open (so total_sessions can be queried).
183
+ *
184
+ * Commands that never open the DB (init, config, telemetry) can skip this —
185
+ * PostHog retains person properties from previous calls.
105
186
  */
106
- export function buildEventPreview(command) {
107
- return buildEvent(command, true);
187
+ export async function identifyUser() {
188
+ const ph = getPostHogClient();
189
+ if (!ph)
190
+ return;
191
+ try {
192
+ const { getDb } = await import('../db/client.js');
193
+ const db = getDb();
194
+ const row = db.prepare('SELECT COUNT(*) as count FROM sessions').get();
195
+ ph.identify({
196
+ distinctId: getStableMachineId(),
197
+ properties: {
198
+ cli_version: getCliVersion(),
199
+ node_version: process.version.replace('v', ''),
200
+ os: process.platform,
201
+ arch: process.arch,
202
+ installed_providers: detectProviders(),
203
+ has_hook: detectHook(),
204
+ total_sessions: row.count,
205
+ },
206
+ });
207
+ }
208
+ catch {
209
+ // Non-fatal — identify failure doesn't affect event tracking
210
+ }
108
211
  }
109
- // ---------------------------------------------------------------------------
110
- // Internal helpers
111
- // ---------------------------------------------------------------------------
112
212
  /**
113
- * Build a TelemetryEvent with all fields populated.
114
- * Separated from sendEvent so buildEventPreview can reuse it without sending.
213
+ * Build a preview of what would be collected and sent.
214
+ * Used by `code-insights telemetry status` to show users what is collected.
115
215
  */
116
- function buildEvent(command, success, subcommand) {
216
+ export function buildEventPreview() {
117
217
  return {
118
- machineId: getMachineId(),
119
- command,
120
- subcommand,
121
- success,
122
- cliVersion: getCliVersion(),
123
- nodeVersion: process.version.replace('v', ''),
218
+ distinct_id: getStableMachineId(),
219
+ cli_version: getCliVersion(),
220
+ node_version: process.version.replace('v', ''),
124
221
  os: process.platform,
125
222
  arch: process.arch,
126
- providers: detectProviders(),
127
- sessionCountBucket: getSessionCountBucket(),
128
- dataSource: getDataSource(),
129
- hasHook: detectHook(),
130
- // Day precision only — avoids time-of-day behavioral fingerprinting
131
- timestamp: new Date().toISOString().slice(0, 10),
223
+ installed_providers: detectProviders(),
224
+ has_hook: detectHook(),
225
+ total_sessions: '(queried from SQLite when DB is open)',
226
+ sample_event: {
227
+ event: 'cli_sync',
228
+ properties: {
229
+ duration_ms: 1234,
230
+ sessions_synced: 5,
231
+ sessions_by_provider: { 'claude-code': 4, cursor: 1 },
232
+ errors: 0,
233
+ source_filter: null,
234
+ success: true,
235
+ },
236
+ },
132
237
  };
133
238
  }
239
+ // ---------------------------------------------------------------------------
240
+ // Internal helpers
241
+ // ---------------------------------------------------------------------------
134
242
  /**
135
- * Anonymous machine ID, rotated monthly.
243
+ * Stable machine ID does NOT rotate monthly.
136
244
  *
137
- * Format: SHA-256(hostname:username:code-insights-YYYY-MM).slice(0, 16)
245
+ * Format: SHA-256(hostname:username:code-insights).slice(0, 16)
138
246
  *
139
- * Monthly rotation ensures:
140
- * - Long-term tracking is not possible across months
141
- * - Events within a month can be correlated for "unique users" metrics
142
- * - No PII: hostname and username are never sent, only their hash
247
+ * No PII: hostname and username are never transmitted, only their hash.
248
+ * Deterministic: same machine always produces the same ID (survives reinstalls).
143
249
  */
144
- function getMachineId() {
145
- const now = new Date();
146
- // YYYY-MM format for monthly rotation
147
- const monthSalt = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
250
+ export function getStableMachineId() {
148
251
  let username;
149
252
  try {
150
253
  username = os.userInfo().username;
@@ -153,18 +256,15 @@ function getMachineId() {
153
256
  // os.userInfo() throws in Docker/CI when UID has no /etc/passwd entry
154
257
  username = `uid-${process.getuid?.() ?? 'unknown'}`;
155
258
  }
156
- const input = [os.hostname(), username, `code-insights-${monthSalt}`].join(':');
259
+ const input = [os.hostname(), username, 'code-insights'].join(':');
157
260
  return crypto.createHash('sha256').update(input).digest('hex').slice(0, 16);
158
261
  }
159
262
  /**
160
263
  * Read CLI version from package.json.
161
- * Uses createRequire for JSON imports since this is ESM and JSON imports
162
- * have inconsistent support across Node versions and bundlers.
163
264
  */
164
265
  function getCliVersion() {
165
266
  try {
166
267
  const require = createRequire(import.meta.url);
167
- // package.json is two levels up from src/utils/ -> src/ -> cli/
168
268
  const pkg = require('../../package.json');
169
269
  return pkg.version;
170
270
  }
@@ -174,96 +274,32 @@ function getCliVersion() {
174
274
  }
175
275
  /**
176
276
  * Detect which AI coding tool data directories exist on this machine.
177
- * We check for the existence of known directories we never read their contents.
178
- *
179
- * Returns only the tools that are actually present. This tells us which tools
180
- * users are running alongside code-insights, helping us prioritize provider support.
277
+ * Checks directory existence only — never reads file contents.
181
278
  */
182
279
  function detectProviders() {
183
280
  const home = os.homedir();
184
281
  const detected = [];
185
- // Claude Code: ~/.claude/projects/
186
282
  if (fs.existsSync(path.join(home, '.claude', 'projects'))) {
187
283
  detected.push('claude-code');
188
284
  }
189
- // Cursor: workspace storage directory (cross-platform path)
190
285
  const cursorStoragePaths = [
191
- // macOS
192
286
  path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'workspaceStorage'),
193
- // Linux
194
287
  path.join(home, '.config', 'Cursor', 'User', 'workspaceStorage'),
195
- // Windows
196
288
  path.join(home, 'AppData', 'Roaming', 'Cursor', 'User', 'workspaceStorage'),
197
289
  ];
198
290
  if (cursorStoragePaths.some((p) => fs.existsSync(p))) {
199
291
  detected.push('cursor');
200
292
  }
201
- // Codex CLI: ~/.codex/sessions
202
293
  if (fs.existsSync(path.join(home, '.codex', 'sessions'))) {
203
294
  detected.push('codex-cli');
204
295
  }
205
- // GitHub Copilot CLI: ~/.copilot/session-state
206
296
  if (fs.existsSync(path.join(home, '.copilot', 'session-state'))) {
207
297
  detected.push('copilot-cli');
208
298
  }
209
299
  return detected;
210
300
  }
211
- /**
212
- * Count .jsonl files under ~/.claude/projects/ and bucket the count.
213
- *
214
- * Buckets are intentionally coarse — we want to understand "heavy vs light"
215
- * usage without counting exact files, which could vary wildly and feels more
216
- * private than a range.
217
- */
218
- function getSessionCountBucket() {
219
- try {
220
- const claudeDir = getClaudeDir();
221
- if (!fs.existsSync(claudeDir))
222
- return '0';
223
- // Count all .jsonl files recursively under the Claude projects directory
224
- let count = 0;
225
- const walk = (dir) => {
226
- const entries = fs.readdirSync(dir, { withFileTypes: true });
227
- for (const entry of entries) {
228
- if (entry.isDirectory()) {
229
- walk(path.join(dir, entry.name));
230
- }
231
- else if (entry.name.endsWith('.jsonl')) {
232
- count++;
233
- }
234
- }
235
- };
236
- walk(claudeDir);
237
- // Coarse buckets for privacy — exact session counts are never sent
238
- if (count === 0)
239
- return '0';
240
- if (count <= 10)
241
- return '1-10';
242
- if (count <= 50)
243
- return '11-50';
244
- if (count <= 200)
245
- return '51-200';
246
- return '200+';
247
- }
248
- catch {
249
- return 'unknown';
250
- }
251
- }
252
- /**
253
- * Determine the configured data source.
254
- * Always 'local' in Phase 2+ (SQLite-only).
255
- */
256
- function getDataSource() {
257
- const config = loadConfig();
258
- if (!config)
259
- return 'none';
260
- return 'local';
261
- }
262
301
  /**
263
302
  * Check if code-insights is registered as a Claude Code hook.
264
- *
265
- * Reads ~/.claude/settings.json and looks for 'code-insights' anywhere in the
266
- * file content. A hook registration means the user has automated sync on session end.
267
303
  */
268
304
  function detectHook() {
269
305
  try {
@@ -277,48 +313,4 @@ function detectHook() {
277
313
  return false;
278
314
  }
279
315
  }
280
- /**
281
- * Sign the request body with HMAC-SHA256.
282
- * Returns the hex digest prefixed with 'sha256=' for the X-Signature header.
283
- *
284
- * Note: the key is public (extractable from the npm package) — this is a deterrent
285
- * against casual data poisoning, not a cryptographic secret. Security is enforced
286
- * server-side in the Edge Function using constant-time comparison.
287
- */
288
- function signPayload(body) {
289
- const digest = crypto.createHmac('sha256', HMAC_KEY).update(body).digest('hex');
290
- return `sha256=${digest}`;
291
- }
292
- /**
293
- * Internal: Send the event to the telemetry endpoint.
294
- * AbortController ensures we don't hang longer than 2 seconds.
295
- * All errors are swallowed — telemetry failures must never propagate.
296
- */
297
- async function sendEvent(event) {
298
- const controller = new AbortController();
299
- // 2s timeout — enough for a healthy network, short enough to not delay anything
300
- const timer = setTimeout(() => controller.abort(), 2000);
301
- try {
302
- const body = JSON.stringify(event);
303
- await fetch(TELEMETRY_ENDPOINT, {
304
- method: 'POST',
305
- headers: {
306
- 'Content-Type': 'application/json',
307
- // HMAC signature — lets the Edge Function verify the payload wasn't tampered.
308
- // The v2 endpoint accepts but does not require this header initially, allowing
309
- // older CLI versions (which don't send it) to continue working.
310
- 'X-Signature': signPayload(body),
311
- },
312
- body,
313
- signal: controller.signal,
314
- });
315
- }
316
- catch {
317
- // Swallow everything: network errors, AbortError, JSON serialization errors.
318
- // Telemetry failures are silent — the CLI command already completed.
319
- }
320
- finally {
321
- clearTimeout(timer);
322
- }
323
- }
324
316
  //# sourceMappingURL=telemetry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/utils/telemetry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/utils/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEvD,6EAA6E;AAC7E,0DAA0D;AAC1D,MAAM,eAAe,GAAG,iDAAiD,CAAC;AAC1E,MAAM,YAAY,GAAG,0BAA0B,CAAC;AAEhD,qEAAqE;AACrE,qFAAqF;AACrF,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,yBAAyB,CAAC,CAAC;AAkBzE,gEAAgE;AAChE,+DAA+D;AAC/D,IAAI,MAAM,GAAmB,IAAI,CAAC;AAElC;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,OAAO,CAAC,GAAG,CAAC,gCAAgC,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IACvE,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAEnD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7D,OAAO,MAAM,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB;IACvB,IAAI,CAAC,kBAAkB,EAAE;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAI,OAAO,CAAC,eAAe,EAAE;YACpC,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,CAAC;YACV,aAAa,EAAE,CAAC;SACjB,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,2BAA2B;IACzC,IAAI,CAAC,kBAAkB,EAAE;QAAE,OAAO,KAAK,CAAC;IAExC,MAAM,cAAc,GAAG,aAAa,EAAE,CAAC;IACvC,IAAI,YAAY,GAAkB,IAAI,CAAC;IAEvC,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;IACH,CAAC;IAED,IAAI,YAAY,KAAK,cAAc;QAAE,OAAO,KAAK,CAAC;IAElD,qCAAqC;IACrC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,iFAAiF,CAAC,CAAC;IAC/F,OAAO,CAAC,GAAG,CAAC,mFAAmF,CAAC,CAAC;IACjG,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,2FAA2F,CAAC,CAAC;IACzG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,gEAAgE;IAChE,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;IACzE,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAChC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC/D,CAAC;QACD,8BAA8B;QAC9B,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YACjC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAC1E,CAAC;QACD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACjE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc,EAAE,UAAoC;IAC/E,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE;QAAE,OAAO;IAEhB,IAAI,CAAC;QACH,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3D,EAAE,CAAC,OAAO,CAAC;YACT,UAAU,EAAE,kBAAkB,EAAE;YAChC,KAAK,EAAE,YAAY;YACnB,UAAU,EAAE;gBACV,kBAAkB,EAAE,aAAa;gBACjC,eAAe,EAAE,UAAU;gBAC3B,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;aACtB;SACF,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;IACvD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAyB,EAAE,UAAoC;IACxF,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE;QAAE,OAAO;IAEhB,IAAI,CAAC;QACH,EAAE,CAAC,OAAO,CAAC;YACT,UAAU,EAAE,kBAAkB,EAAE;YAChC,KAAK;YACL,UAAU,EAAE,UAAU,IAAI,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;IACvD,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE;QAAE,OAAO;IAEhB,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAClD,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;QACnB,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC,GAAG,EAAuB,CAAC;QAE5F,EAAE,CAAC,QAAQ,CAAC;YACV,UAAU,EAAE,kBAAkB,EAAE;YAChC,UAAU,EAAE;gBACV,WAAW,EAAE,aAAa,EAAE;gBAC5B,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;gBAC9C,EAAE,EAAE,OAAO,CAAC,QAAQ;gBACpB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,mBAAmB,EAAE,eAAe,EAAE;gBACtC,QAAQ,EAAE,UAAU,EAAE;gBACtB,cAAc,EAAE,GAAG,CAAC,KAAK;aAC1B;SACF,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,WAAW,EAAE,kBAAkB,EAAE;QACjC,WAAW,EAAE,aAAa,EAAE;QAC5B,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;QAC9C,EAAE,EAAE,OAAO,CAAC,QAAQ;QACpB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,mBAAmB,EAAE,eAAe,EAAE;QACtC,QAAQ,EAAE,UAAU,EAAE;QACtB,cAAc,EAAE,uCAAuC;QACvD,YAAY,EAAE;YACZ,KAAK,EAAE,UAAU;YACjB,UAAU,EAAE;gBACV,WAAW,EAAE,IAAI;gBACjB,eAAe,EAAE,CAAC;gBAClB,oBAAoB,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;gBACrD,MAAM,EAAE,CAAC;gBACT,aAAa,EAAE,IAAI;gBACnB,OAAO,EAAE,IAAI;aACd;SACF;KACF,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,QAAQ,GAAG,OAAO,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,SAAS,EAAE,CAAC;IACtD,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnE,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,SAAS,aAAa;IACpB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,oBAAoB,CAAwB,CAAC;QACjE,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe;IACtB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;QAC1D,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,kBAAkB,GAAG;QACzB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,CAAC;QACvF,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,CAAC;QAChE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,kBAAkB,CAAC;KAC5E,CAAC;IACF,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;QACzD,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC;QAChE,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QACzE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;YAAE,OAAO,KAAK,CAAC;QAC/C,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACvD,OAAO,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/utils/welcome.ts"],"names":[],"mappings":"AAQA;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAmC9D"}
1
+ {"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/utils/welcome.ts"],"names":[],"mappings":"AASA;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAiC9D"}
@@ -3,6 +3,7 @@ import * as path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import { ensureConfigDir, getConfigDir } from './config.js';
5
5
  import { getAllProviders } from '../providers/registry.js';
6
+ import { printBanner } from './banner.js';
6
7
  const WELCOME_MARKER = '.welcome-shown';
7
8
  /**
8
9
  * Show a one-time welcome banner for first-time users.
@@ -21,9 +22,7 @@ export async function showWelcomeIfFirstRun() {
21
22
  return false;
22
23
  }
23
24
  const sessionCount = await countAllSessions();
24
- console.log('');
25
- console.log(chalk.bold.cyan(' Welcome to Code Insights!'));
26
- console.log('');
25
+ printBanner();
27
26
  if (sessionCount > 0) {
28
27
  console.log(chalk.dim(' Found ') +
29
28
  chalk.white.bold(sessionCount) +
@@ -1 +1 @@
1
- {"version":3,"file":"welcome.js","sourceRoot":"","sources":["../../src/utils/welcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAExC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,cAAc,CAAC,CAAC;QAE7D,4CAA4C;QAC5C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,gBAAgB,EAAE,CAAC;QAE9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;QAC5D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;gBACrB,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;gBAC9B,KAAK,CAAC,GAAG,CAAC,WAAW,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,wBAAwB,CAAC,CAC5E,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC,CAAC;QAC1E,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,+CAA+C;QAC/C,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAE/B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,wDAAwD;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,gBAAgB;IAC7B,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IACpC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,MAAM,OAAO,CAAC,UAAU,CACtB,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACxC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,eAAe,EAAE,CAAC;IAClB,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"welcome.js","sourceRoot":"","sources":["../../src/utils/welcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAExC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,cAAc,CAAC,CAAC;QAE7D,4CAA4C;QAC5C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,gBAAgB,EAAE,CAAC;QAE9C,WAAW,EAAE,CAAC;QAEd,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;gBACrB,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;gBAC9B,KAAK,CAAC,GAAG,CAAC,WAAW,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,wBAAwB,CAAC,CAC5E,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC,CAAC;QAC1E,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,+CAA+C;QAC/C,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAE/B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,wDAAwD;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,gBAAgB;IAC7B,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IACpC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,MAAM,OAAO,CAAC,UAAU,CACtB,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACxC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,eAAe,EAAE,CAAC;IAClB,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACpD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-insights/cli",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "AI coding session analytics with built-in dashboard",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -72,7 +72,8 @@
72
72
  "commander": "^12.1.0",
73
73
  "hono": "^4.7.4",
74
74
  "inquirer": "^12.6.1",
75
- "ora": "^8.2.0"
75
+ "ora": "^8.2.0",
76
+ "posthog-node": "^4.18.0"
76
77
  },
77
78
  "devDependencies": {
78
79
  "@types/better-sqlite3": "^7.6.13",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAa5B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAgChC;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDvE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAe5B,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAiChC;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDvE"}
@@ -4,6 +4,7 @@ import { Hono } from 'hono';
4
4
  import { existsSync, readFileSync } from 'fs';
5
5
  import { relative, join } from 'path';
6
6
  import { openUrl } from '@code-insights/cli/utils/browser';
7
+ import { shutdownTelemetry } from '@code-insights/cli/utils/telemetry';
7
8
  import projectsRouter from './routes/projects.js';
8
9
  import sessionsRouter from './routes/sessions.js';
9
10
  import messagesRouter from './routes/messages.js';
@@ -12,6 +13,7 @@ import analysisRouter from './routes/analysis.js';
12
13
  import analyticsRouter from './routes/analytics.js';
13
14
  import configRouter from './routes/config.js';
14
15
  import exportRouter from './routes/export.js';
16
+ import telemetryRouter from './routes/telemetry.js';
15
17
  /**
16
18
  * Create the Hono app with all API routes mounted.
17
19
  * Does NOT add static file serving or call serve() — that's startServer's job.
@@ -37,6 +39,7 @@ export function createApp() {
37
39
  app.route('/api/analytics', analyticsRouter);
38
40
  app.route('/api/config', configRouter);
39
41
  app.route('/api/export', exportRouter);
42
+ app.route('/api/telemetry', telemetryRouter);
40
43
  // Health check
41
44
  app.get('/api/health', (c) => c.json({ ok: true, version: '0.1.0' }));
42
45
  // API 404 catch-all — must come AFTER all /api sub-routers and BEFORE static serving.
@@ -83,14 +86,18 @@ export async function startServer(options) {
83
86
  </body></html>
84
87
  `));
85
88
  }
86
- // Graceful shutdown: just call process.exit(0).
87
- // The process 'exit' handler in cli/src/db/client.ts already calls closeDb()
88
- // which runs WAL checkpoint. Calling closeDb() here would double-close.
89
- const shutdown = () => {
89
+ // Graceful shutdown: flush PostHog events before exit, then let the process
90
+ // 'exit' handler in cli/src/db/client.ts run WAL checkpoint via closeDb().
91
+ // 3s timeout guards against PostHog SDK stalling on network issues.
92
+ const shutdown = async () => {
93
+ await Promise.race([
94
+ shutdownTelemetry(),
95
+ new Promise((resolve) => setTimeout(resolve, 3000)),
96
+ ]);
90
97
  process.exit(0);
91
98
  };
92
- process.on('SIGINT', shutdown);
93
- process.on('SIGTERM', shutdown);
99
+ process.on('SIGINT', () => { void shutdown(); });
100
+ process.on('SIGTERM', () => { void shutdown(); });
94
101
  serve({ fetch: app.fetch, port }, (info) => {
95
102
  const url = `http://localhost:${info.port}`;
96
103
  console.log(` Code Insights dashboard running at ${url}`);