codeant-cli 0.2.6 → 0.2.7
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/178.index.js +1 -1
- package/dist/239.index.js +1 -1
- package/dist/701.index.js +1 -1
- package/dist/880.index.js +1 -1
- package/dist/935.index.js +1 -1
- package/dist/966.index.js +1 -1
- package/dist/index.js +11 -4
- package/package.json +2 -1
- package/src/commands/review.js +20 -1
- package/src/index.js +21 -0
- package/src/reviewHeadless.js +16 -1
- package/src/utils/analytics.js +45 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeant-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Code review CLI tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"ink": "^5.0.1",
|
|
42
42
|
"minimatch": "^10.1.1",
|
|
43
43
|
"open": "^10.1.0",
|
|
44
|
+
"posthog-node": "^5.28.5",
|
|
44
45
|
"react": "^18.3.1"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
package/src/commands/review.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fetchApi } from '../utils/fetchApi.js';
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { executeTool } from '../tools/executeTool.js';
|
|
9
|
+
import { track, shutdown as analyticsShutdown } from '../utils/analytics.js';
|
|
9
10
|
import {
|
|
10
11
|
renderInitializing,
|
|
11
12
|
renderFetchingDiff,
|
|
@@ -51,6 +52,9 @@ export default function Review({ scanType = 'all', lastNCommits = 1, failOn = 'C
|
|
|
51
52
|
let cancelled = false;
|
|
52
53
|
|
|
53
54
|
async function runReview() {
|
|
55
|
+
const reviewStartTime = Date.now();
|
|
56
|
+
track('review_triggered', { scan_type: scanType, source: 'cli_interactive' });
|
|
57
|
+
|
|
54
58
|
try {
|
|
55
59
|
// ── Fetch diff ─────────────────────────────────────────────────────
|
|
56
60
|
setStatus('fetching_diff');
|
|
@@ -140,9 +144,24 @@ export default function Review({ scanType = 'all', lastNCommits = 1, failOn = 'C
|
|
|
140
144
|
setSummary(null);
|
|
141
145
|
setStatus('done');
|
|
142
146
|
|
|
147
|
+
const labelCounts = {};
|
|
148
|
+
for (const i of reviewIssues) { labelCounts[i.label] = (labelCounts[i.label] || 0) + 1; }
|
|
149
|
+
track('review_completed', {
|
|
150
|
+
scan_type: scanType,
|
|
151
|
+
source: 'cli_interactive',
|
|
152
|
+
issue_count: reviewIssues.length,
|
|
153
|
+
file_count: perFileRequests.length,
|
|
154
|
+
duration_ms: Date.now() - reviewStartTime,
|
|
155
|
+
label_counts: labelCounts,
|
|
156
|
+
});
|
|
157
|
+
await analyticsShutdown();
|
|
158
|
+
|
|
143
159
|
} catch (err) {
|
|
144
160
|
if (!cancelled) {
|
|
145
|
-
|
|
161
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
162
|
+
track('review_error', { scan_type: scanType, source: 'cli_interactive', error: errMsg });
|
|
163
|
+
await analyticsShutdown();
|
|
164
|
+
setError(errMsg);
|
|
146
165
|
setStatus('error');
|
|
147
166
|
}
|
|
148
167
|
}
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import Review from './commands/review.js';
|
|
|
13
13
|
import Welcome from './components/Welcome.js';
|
|
14
14
|
import * as scm from './scm/index.js';
|
|
15
15
|
import { setConfigValue } from './utils/config.js';
|
|
16
|
+
import { track, shutdown as analyticsShutdown } from './utils/analytics.js';
|
|
16
17
|
|
|
17
18
|
// Read version from package.json — createRequire works with ncc bundling
|
|
18
19
|
const require = createRequire(import.meta.url);
|
|
@@ -324,5 +325,25 @@ program
|
|
|
324
325
|
}));
|
|
325
326
|
});
|
|
326
327
|
|
|
328
|
+
// ─── Analytics tracking (for external callers like Claude Code skills) ───
|
|
329
|
+
program
|
|
330
|
+
.command('track')
|
|
331
|
+
.description('Send an analytics event')
|
|
332
|
+
.requiredOption('--event <name>', 'Event name (e.g. skill_invoked, suggestions_applied)')
|
|
333
|
+
.option('--props <json>', 'JSON string of event properties', '{}')
|
|
334
|
+
.action(async (options) => {
|
|
335
|
+
try {
|
|
336
|
+
const props = JSON.parse(options.props);
|
|
337
|
+
if (typeof props !== 'object' || props === null || Array.isArray(props)) {
|
|
338
|
+
process.stderr.write('--props must be a JSON object\n');
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
track(options.event, props);
|
|
342
|
+
await analyticsShutdown();
|
|
343
|
+
} catch (err) {
|
|
344
|
+
process.stderr.write(`Invalid --props JSON: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
327
348
|
program.parse();
|
|
328
349
|
}
|
package/src/reviewHeadless.js
CHANGED
|
@@ -2,6 +2,7 @@ import ReviewApiHelper from './utils/reviewApiHelper.js';
|
|
|
2
2
|
import { fetchApi } from './utils/fetchApi.js';
|
|
3
3
|
import { executeTool } from './tools/executeTool.js';
|
|
4
4
|
import { getConfigValue, setConfigValue } from './utils/config.js';
|
|
5
|
+
import { track } from './utils/analytics.js';
|
|
5
6
|
|
|
6
7
|
const MAX_TURNS = 5;
|
|
7
8
|
|
|
@@ -97,6 +98,9 @@ export async function runReviewHeadless(options = {}) {
|
|
|
97
98
|
if (apiKey) process.env.CODEANT_API_TOKEN = apiKey;
|
|
98
99
|
if (baseUrl) process.env.CODEANT_API_URL = baseUrl;
|
|
99
100
|
|
|
101
|
+
const reviewStartTime = Date.now();
|
|
102
|
+
track('review_triggered', { scan_type: scanType, source: 'headless' });
|
|
103
|
+
|
|
100
104
|
try {
|
|
101
105
|
// ── Fetch diff ──────────────────────────────────────────────────────
|
|
102
106
|
onProgress('Fetching diff...');
|
|
@@ -113,6 +117,7 @@ export async function runReviewHeadless(options = {}) {
|
|
|
113
117
|
onFilesReady(meta?.reviewed_files || [], meta);
|
|
114
118
|
|
|
115
119
|
if (!requestBody?.diff_content?.length) {
|
|
120
|
+
track('review_completed', { scan_type: scanType, source: 'headless', issue_count: 0, file_count: 0, duration_ms: Date.now() - reviewStartTime, no_files: true });
|
|
116
121
|
return { issues: [], meta, error: null, noFiles: true };
|
|
117
122
|
}
|
|
118
123
|
|
|
@@ -163,9 +168,19 @@ export async function runReviewHeadless(options = {}) {
|
|
|
163
168
|
label: issue.label || 'Code Quality',
|
|
164
169
|
}));
|
|
165
170
|
|
|
171
|
+
const labelCounts = {};
|
|
172
|
+
for (const i of issues) { labelCounts[i.label] = (labelCounts[i.label] || 0) + 1; }
|
|
173
|
+
track('review_completed', {
|
|
174
|
+
scan_type: scanType, source: 'headless', issue_count: issues.length,
|
|
175
|
+
file_count: perFileRequests.length, duration_ms: Date.now() - reviewStartTime,
|
|
176
|
+
label_counts: labelCounts,
|
|
177
|
+
});
|
|
178
|
+
|
|
166
179
|
return { issues, meta, error: null, noFiles: false };
|
|
167
180
|
} catch (err) {
|
|
168
|
-
|
|
181
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
182
|
+
track('review_error', { scan_type: scanType, source: 'headless', error: errMsg });
|
|
183
|
+
return { issues: [], meta: null, error: errMsg, noFiles: false };
|
|
169
184
|
} finally {
|
|
170
185
|
// Restore env vars
|
|
171
186
|
if (prevToken !== undefined) process.env.CODEANT_API_TOKEN = prevToken;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PostHog } from 'posthog-node';
|
|
2
|
+
import { getConfigValue } from './config.js';
|
|
3
|
+
|
|
4
|
+
const POSTHOG_API_KEY = 'phc_TpsLE5AMwHYsrSQIYgI7uqvorzlDMsin3lDvTEv2DxO';
|
|
5
|
+
const POSTHOG_HOST = 'https://r.codeant.ai';
|
|
6
|
+
|
|
7
|
+
let client = null;
|
|
8
|
+
|
|
9
|
+
function getClient() {
|
|
10
|
+
if (!client) {
|
|
11
|
+
client = new PostHog(POSTHOG_API_KEY, { host: POSTHOG_HOST, flushAt: 1, flushInterval: 0 });
|
|
12
|
+
}
|
|
13
|
+
return client;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Use the API key directly as distinct ID */
|
|
17
|
+
function getDistinctId() {
|
|
18
|
+
return process.env.CODEANT_API_TOKEN || getConfigValue('apiKey') || 'anonymous';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Track an analytics event. Fire-and-forget — never throws or blocks the CLI.
|
|
23
|
+
* @param {string} event - Event name (e.g. 'review_triggered')
|
|
24
|
+
* @param {Object} [properties={}] - Event properties
|
|
25
|
+
*/
|
|
26
|
+
export function track(event, properties = {}) {
|
|
27
|
+
try {
|
|
28
|
+
getClient().capture({
|
|
29
|
+
distinctId: getDistinctId(),
|
|
30
|
+
event,
|
|
31
|
+
properties: { source: 'cli', cli_version: process.env.npm_package_version || 'unknown', ...properties },
|
|
32
|
+
});
|
|
33
|
+
} catch (_) {
|
|
34
|
+
// Analytics should never break the CLI
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Flush pending events. Call before process exits. */
|
|
39
|
+
export async function shutdown() {
|
|
40
|
+
try {
|
|
41
|
+
if (client) await client.shutdown();
|
|
42
|
+
} catch (_) {
|
|
43
|
+
// noop
|
|
44
|
+
}
|
|
45
|
+
}
|