aislop 0.8.3 → 0.9.0
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/cli.js +533 -126
- package/dist/index.js +394 -75
- package/dist/{json-D8h2EZW6.js → json-DZfGz2xa.js} +1 -1
- package/dist/{json-BbMwrgyd.js → json-OIzja7OM.js} +1 -1
- package/dist/mcp.js +248 -8
- package/dist/{typecheck-B1MXNAy-.js → typecheck-wVSohmOX.js} +1 -1
- package/dist/{version-BynHxO1X.js → version-D_rqBdyj.js} +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,14 +4,14 @@ import { Command } from "commander";
|
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
|
+
import crypto, { randomUUID } from "node:crypto";
|
|
8
|
+
import { performance } from "node:perf_hooks";
|
|
7
9
|
import YAML from "yaml";
|
|
8
10
|
import { z } from "zod/v4";
|
|
9
|
-
import { performance } from "node:perf_hooks";
|
|
10
11
|
import { execSync, spawn, spawnSync } from "node:child_process";
|
|
11
12
|
import micromatch from "micromatch";
|
|
12
13
|
import { fileURLToPath } from "node:url";
|
|
13
14
|
import ts from "typescript";
|
|
14
|
-
import crypto from "node:crypto";
|
|
15
15
|
import { isCancel, multiselect, select, text } from "@clack/prompts";
|
|
16
16
|
import pc from "picocolors";
|
|
17
17
|
import wcwidth from "wcwidth";
|
|
@@ -32,6 +32,366 @@ var __exportAll = (all, no_symbols) => {
|
|
|
32
32
|
return target;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/version.ts
|
|
37
|
+
const APP_VERSION = "0.9.0";
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/telemetry/env.ts
|
|
41
|
+
const detectPackageManager$1 = (env = process.env) => {
|
|
42
|
+
const execPath = env.npm_execpath ?? "";
|
|
43
|
+
if (execPath.includes("npx")) return "npx";
|
|
44
|
+
const userAgent = env.npm_config_user_agent ?? "";
|
|
45
|
+
if (userAgent.startsWith("pnpm/")) return "pnpm";
|
|
46
|
+
if (userAgent.startsWith("yarn/")) return "yarn";
|
|
47
|
+
if (userAgent.startsWith("bun/")) return "bun";
|
|
48
|
+
if (userAgent.startsWith("npm/")) return "npm";
|
|
49
|
+
if (execPath.includes("pnpm")) return "pnpm";
|
|
50
|
+
if (execPath.includes("yarn")) return "yarn";
|
|
51
|
+
if (execPath.includes("bun")) return "bun";
|
|
52
|
+
if (execPath.includes("npm")) return "npm";
|
|
53
|
+
return "unknown";
|
|
54
|
+
};
|
|
55
|
+
const CI_ENV_KEYS = [
|
|
56
|
+
"CI",
|
|
57
|
+
"GITHUB_ACTIONS",
|
|
58
|
+
"GITLAB_CI",
|
|
59
|
+
"CIRCLECI",
|
|
60
|
+
"TRAVIS",
|
|
61
|
+
"BUILDKITE",
|
|
62
|
+
"DRONE",
|
|
63
|
+
"TEAMCITY_VERSION",
|
|
64
|
+
"TF_BUILD"
|
|
65
|
+
];
|
|
66
|
+
const isCiEnv = (env = process.env) => CI_ENV_KEYS.some((k) => {
|
|
67
|
+
const v = env[k];
|
|
68
|
+
return v === "true" || v === "1" || v != null && v.length > 0 && k !== "CI";
|
|
69
|
+
}) || env.CI === "true" || env.CI === "1";
|
|
70
|
+
const fileCountBucket = (count) => {
|
|
71
|
+
if (count < 10) return "0-10";
|
|
72
|
+
if (count < 50) return "10-50";
|
|
73
|
+
if (count < 100) return "50-100";
|
|
74
|
+
if (count < 500) return "100-500";
|
|
75
|
+
if (count < 1e3) return "500-1000";
|
|
76
|
+
return "1000+";
|
|
77
|
+
};
|
|
78
|
+
const scoreBucket = (score) => {
|
|
79
|
+
if (score >= 75) return "75-100";
|
|
80
|
+
if (score >= 50) return "50-75";
|
|
81
|
+
if (score >= 25) return "25-50";
|
|
82
|
+
return "0-25";
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/telemetry/identity.ts
|
|
87
|
+
const FILE_BASENAME = "install_id";
|
|
88
|
+
const resolveInstallIdPath = (homedir = os.homedir(), env = process.env) => {
|
|
89
|
+
if (process.platform === "linux" && env.XDG_STATE_HOME) return path.join(env.XDG_STATE_HOME, "aislop", FILE_BASENAME);
|
|
90
|
+
return path.join(homedir, ".aislop", FILE_BASENAME);
|
|
91
|
+
};
|
|
92
|
+
const ensureInstallId = (idPath = resolveInstallIdPath()) => {
|
|
93
|
+
if (fs.existsSync(idPath)) {
|
|
94
|
+
const existing = fs.readFileSync(idPath, "utf-8").trim();
|
|
95
|
+
if (existing.length > 0) return {
|
|
96
|
+
installId: existing,
|
|
97
|
+
created: false
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const dir = path.dirname(idPath);
|
|
101
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
102
|
+
const installId = randomUUID();
|
|
103
|
+
const tmpPath = `${idPath}.${process.pid}.tmp`;
|
|
104
|
+
fs.writeFileSync(tmpPath, `${installId}\n`, { mode: 384 });
|
|
105
|
+
try {
|
|
106
|
+
fs.renameSync(tmpPath, idPath);
|
|
107
|
+
return {
|
|
108
|
+
installId,
|
|
109
|
+
created: true
|
|
110
|
+
};
|
|
111
|
+
} catch {
|
|
112
|
+
fs.rmSync(tmpPath, { force: true });
|
|
113
|
+
return {
|
|
114
|
+
installId: fs.readFileSync(idPath, "utf-8").trim(),
|
|
115
|
+
created: false
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/telemetry/redaction.ts
|
|
122
|
+
const SAFE_PROPERTY_NAMES = new Set([
|
|
123
|
+
"aislop_version",
|
|
124
|
+
"node_version",
|
|
125
|
+
"os",
|
|
126
|
+
"arch",
|
|
127
|
+
"schema_version",
|
|
128
|
+
"anonymous_install_id",
|
|
129
|
+
"package_manager",
|
|
130
|
+
"is_ci",
|
|
131
|
+
"command",
|
|
132
|
+
"language_summary",
|
|
133
|
+
"lang_typescript",
|
|
134
|
+
"lang_javascript",
|
|
135
|
+
"lang_python",
|
|
136
|
+
"lang_java",
|
|
137
|
+
"file_count_bucket",
|
|
138
|
+
"exit_code",
|
|
139
|
+
"duration_ms",
|
|
140
|
+
"error_kind",
|
|
141
|
+
"score",
|
|
142
|
+
"score_bucket",
|
|
143
|
+
"finding_count",
|
|
144
|
+
"error_count",
|
|
145
|
+
"warning_count",
|
|
146
|
+
"fixable_count",
|
|
147
|
+
"fix_steps",
|
|
148
|
+
"fix_resolved",
|
|
149
|
+
"fix_score_delta",
|
|
150
|
+
"engine_format_issues",
|
|
151
|
+
"engine_format_ms",
|
|
152
|
+
"engine_lint_issues",
|
|
153
|
+
"engine_lint_ms",
|
|
154
|
+
"engine_code_quality_issues",
|
|
155
|
+
"engine_code_quality_ms",
|
|
156
|
+
"engine_ai_slop_issues",
|
|
157
|
+
"engine_ai_slop_ms",
|
|
158
|
+
"engine_architecture_issues",
|
|
159
|
+
"engine_architecture_ms",
|
|
160
|
+
"engine_security_issues",
|
|
161
|
+
"engine_security_ms",
|
|
162
|
+
"tool",
|
|
163
|
+
"ok",
|
|
164
|
+
"agent",
|
|
165
|
+
"score_delta"
|
|
166
|
+
]);
|
|
167
|
+
const redactProperties = (props) => {
|
|
168
|
+
const clean = {};
|
|
169
|
+
const dropped = [];
|
|
170
|
+
for (const [key, value] of Object.entries(props)) {
|
|
171
|
+
if (value === void 0) continue;
|
|
172
|
+
if (SAFE_PROPERTY_NAMES.has(key)) clean[key] = value;
|
|
173
|
+
else dropped.push(key);
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
clean,
|
|
177
|
+
dropped
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/telemetry/client.ts
|
|
183
|
+
const POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
184
|
+
const POSTHOG_KEY = "phc_eY2cOMFva9q24GrWeOuvuVIOhCIdjOALxeAR3ItrqbJ";
|
|
185
|
+
const SCHEMA_VERSION = "v2";
|
|
186
|
+
const REQUEST_TIMEOUT_MS = 3e3;
|
|
187
|
+
const isTelemetryDisabled = (config) => {
|
|
188
|
+
const env = process.env;
|
|
189
|
+
if (env.AISLOP_NO_TELEMETRY === "1" || env.DO_NOT_TRACK === "1") return true;
|
|
190
|
+
if (config?.enabled === false) return true;
|
|
191
|
+
if (config?.enabled === true) return false;
|
|
192
|
+
if (env.CI === "true" || env.CI === "1") return true;
|
|
193
|
+
return false;
|
|
194
|
+
};
|
|
195
|
+
const isDebug = () => process.env.AISLOP_TELEMETRY_DEBUG === "1";
|
|
196
|
+
const pendingRequests = /* @__PURE__ */ new Set();
|
|
197
|
+
let cachedInstallId = null;
|
|
198
|
+
let installCreated = false;
|
|
199
|
+
const baseProperties = (installId) => ({
|
|
200
|
+
aislop_version: APP_VERSION,
|
|
201
|
+
node_version: process.version,
|
|
202
|
+
os: os.platform(),
|
|
203
|
+
arch: os.arch(),
|
|
204
|
+
schema_version: SCHEMA_VERSION,
|
|
205
|
+
anonymous_install_id: installId,
|
|
206
|
+
package_manager: detectPackageManager$1(),
|
|
207
|
+
is_ci: isCiEnv()
|
|
208
|
+
});
|
|
209
|
+
const track = (input) => {
|
|
210
|
+
if (isTelemetryDisabled(input.config)) return { installCreated: false };
|
|
211
|
+
if (cachedInstallId == null) {
|
|
212
|
+
const ensured = ensureInstallId(resolveInstallIdPath());
|
|
213
|
+
cachedInstallId = ensured.installId;
|
|
214
|
+
installCreated = ensured.created;
|
|
215
|
+
}
|
|
216
|
+
const { clean, dropped } = redactProperties({
|
|
217
|
+
...baseProperties(cachedInstallId),
|
|
218
|
+
...input.properties
|
|
219
|
+
});
|
|
220
|
+
if (isDebug()) {
|
|
221
|
+
const compact = JSON.stringify({
|
|
222
|
+
event: input.event,
|
|
223
|
+
properties: clean
|
|
224
|
+
});
|
|
225
|
+
process.stderr.write(`[telemetry] ${compact}\n`);
|
|
226
|
+
if (dropped.length > 0) for (const key of dropped) process.stderr.write(`[telemetry] dropped non-allowlisted property: ${key}\n`);
|
|
227
|
+
}
|
|
228
|
+
if (process.env.AISLOP_TELEMETRY_DRY_RUN === "1") return { installCreated };
|
|
229
|
+
const payload = {
|
|
230
|
+
api_key: POSTHOG_KEY,
|
|
231
|
+
event: input.event,
|
|
232
|
+
distinct_id: cachedInstallId,
|
|
233
|
+
properties: clean,
|
|
234
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
235
|
+
};
|
|
236
|
+
const request = fetch(`${POSTHOG_HOST}/capture/`, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers: { "Content-Type": "application/json" },
|
|
239
|
+
body: JSON.stringify(payload),
|
|
240
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
241
|
+
}).then(() => {}).catch(() => {}).finally(() => {
|
|
242
|
+
pendingRequests.delete(request);
|
|
243
|
+
});
|
|
244
|
+
pendingRequests.add(request);
|
|
245
|
+
return { installCreated };
|
|
246
|
+
};
|
|
247
|
+
const flushTelemetry = async () => {
|
|
248
|
+
if (pendingRequests.size === 0) return;
|
|
249
|
+
await Promise.all(pendingRequests);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region src/telemetry/language.ts
|
|
254
|
+
const ALL_LANGUAGES = [
|
|
255
|
+
"typescript",
|
|
256
|
+
"javascript",
|
|
257
|
+
"python",
|
|
258
|
+
"java"
|
|
259
|
+
];
|
|
260
|
+
const buildLanguageProperties = (detected) => {
|
|
261
|
+
const present = new Set(detected);
|
|
262
|
+
const summary = [...present].filter((l) => ALL_LANGUAGES.includes(l));
|
|
263
|
+
summary.sort();
|
|
264
|
+
return {
|
|
265
|
+
language_summary: summary.join(","),
|
|
266
|
+
lang_typescript: present.has("typescript"),
|
|
267
|
+
lang_javascript: present.has("javascript"),
|
|
268
|
+
lang_python: present.has("python"),
|
|
269
|
+
lang_java: present.has("java")
|
|
270
|
+
};
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/telemetry/events.ts
|
|
275
|
+
const buildCommandStartedProps = (input) => {
|
|
276
|
+
const props = { command: input.command };
|
|
277
|
+
if (input.languages) Object.assign(props, buildLanguageProperties(input.languages));
|
|
278
|
+
if (typeof input.fileCount === "number") props.file_count_bucket = fileCountBucket(input.fileCount);
|
|
279
|
+
return props;
|
|
280
|
+
};
|
|
281
|
+
const ENGINE_KEY_MAP = {
|
|
282
|
+
format: "engine_format",
|
|
283
|
+
lint: "engine_lint",
|
|
284
|
+
"code-quality": "engine_code_quality",
|
|
285
|
+
"ai-slop": "engine_ai_slop",
|
|
286
|
+
architecture: "engine_architecture",
|
|
287
|
+
security: "engine_security"
|
|
288
|
+
};
|
|
289
|
+
const flattenEngineStats = (issues, timings) => {
|
|
290
|
+
const out = {};
|
|
291
|
+
for (const [engine, count] of Object.entries(issues)) {
|
|
292
|
+
const key = ENGINE_KEY_MAP[engine];
|
|
293
|
+
if (key != null && typeof count === "number") out[`${key}_issues`] = count;
|
|
294
|
+
}
|
|
295
|
+
for (const [engine, ms] of Object.entries(timings)) {
|
|
296
|
+
const key = ENGINE_KEY_MAP[engine];
|
|
297
|
+
if (key != null && typeof ms === "number") out[`${key}_ms`] = Math.round(ms);
|
|
298
|
+
}
|
|
299
|
+
return out;
|
|
300
|
+
};
|
|
301
|
+
const buildCommandCompletedProps = (input) => {
|
|
302
|
+
const props = {
|
|
303
|
+
...input.startProps,
|
|
304
|
+
exit_code: input.exitCode,
|
|
305
|
+
duration_ms: Math.round(input.durationMs)
|
|
306
|
+
};
|
|
307
|
+
if (input.errorKind) props.error_kind = input.errorKind;
|
|
308
|
+
if (typeof input.score === "number") {
|
|
309
|
+
props.score = input.score;
|
|
310
|
+
props.score_bucket = scoreBucket(input.score);
|
|
311
|
+
}
|
|
312
|
+
if (typeof input.findingCount === "number") props.finding_count = input.findingCount;
|
|
313
|
+
if (typeof input.errorCount === "number") props.error_count = input.errorCount;
|
|
314
|
+
if (typeof input.warningCount === "number") props.warning_count = input.warningCount;
|
|
315
|
+
if (typeof input.fixableCount === "number") props.fixable_count = input.fixableCount;
|
|
316
|
+
if (input.engineIssues && input.engineTimings) Object.assign(props, flattenEngineStats(input.engineIssues, input.engineTimings));
|
|
317
|
+
if (typeof input.fixSteps === "number") props.fix_steps = input.fixSteps;
|
|
318
|
+
if (typeof input.fixResolved === "number") props.fix_resolved = input.fixResolved;
|
|
319
|
+
if (typeof input.fixScoreDelta === "number") props.fix_score_delta = input.fixScoreDelta;
|
|
320
|
+
return props;
|
|
321
|
+
};
|
|
322
|
+
const buildHookScanCompletedProps = (input) => {
|
|
323
|
+
const props = {
|
|
324
|
+
agent: input.agent,
|
|
325
|
+
score: input.score,
|
|
326
|
+
score_bucket: scoreBucket(input.score),
|
|
327
|
+
finding_count: input.findingCount,
|
|
328
|
+
file_count_bucket: fileCountBucket(input.fileCount)
|
|
329
|
+
};
|
|
330
|
+
if (typeof input.scoreDelta === "number") props.score_delta = input.scoreDelta;
|
|
331
|
+
return props;
|
|
332
|
+
};
|
|
333
|
+
const errorKindFromException = (error) => {
|
|
334
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
335
|
+
if (message.includes("timeout") || message.includes("timed out")) return "timeout";
|
|
336
|
+
if (message.includes("invalid config") || message.includes("config_invalid")) return "config_invalid";
|
|
337
|
+
if (message.includes("engine") && message.includes("crash")) return "engine_crash";
|
|
338
|
+
return "unknown";
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region src/telemetry/lifecycle.ts
|
|
343
|
+
const withCommandLifecycle = async (start, run) => {
|
|
344
|
+
const startProps = buildCommandStartedProps({
|
|
345
|
+
command: start.command,
|
|
346
|
+
languages: start.languages,
|
|
347
|
+
fileCount: start.fileCount
|
|
348
|
+
});
|
|
349
|
+
track({
|
|
350
|
+
event: "cli_command_started",
|
|
351
|
+
properties: startProps,
|
|
352
|
+
config: start.config
|
|
353
|
+
});
|
|
354
|
+
const startedAt = performance.now();
|
|
355
|
+
try {
|
|
356
|
+
const result = await run();
|
|
357
|
+
const durationMs = performance.now() - startedAt;
|
|
358
|
+
track({
|
|
359
|
+
event: "cli_command_completed",
|
|
360
|
+
properties: buildCommandCompletedProps({
|
|
361
|
+
startProps,
|
|
362
|
+
exitCode: result.exitCode,
|
|
363
|
+
durationMs,
|
|
364
|
+
score: result.score,
|
|
365
|
+
findingCount: result.findingCount,
|
|
366
|
+
errorCount: result.errorCount,
|
|
367
|
+
warningCount: result.warningCount,
|
|
368
|
+
fixableCount: result.fixableCount,
|
|
369
|
+
engineIssues: result.engineIssues,
|
|
370
|
+
engineTimings: result.engineTimings,
|
|
371
|
+
fixSteps: result.fixSteps,
|
|
372
|
+
fixResolved: result.fixResolved,
|
|
373
|
+
fixScoreDelta: result.fixScoreDelta
|
|
374
|
+
}),
|
|
375
|
+
config: start.config
|
|
376
|
+
});
|
|
377
|
+
await flushTelemetry();
|
|
378
|
+
return result;
|
|
379
|
+
} catch (error) {
|
|
380
|
+
track({
|
|
381
|
+
event: "cli_command_completed",
|
|
382
|
+
properties: buildCommandCompletedProps({
|
|
383
|
+
startProps,
|
|
384
|
+
exitCode: 1,
|
|
385
|
+
durationMs: performance.now() - startedAt,
|
|
386
|
+
errorKind: errorKindFromException(error)
|
|
387
|
+
}),
|
|
388
|
+
config: start.config
|
|
389
|
+
});
|
|
390
|
+
await flushTelemetry();
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
35
395
|
//#endregion
|
|
36
396
|
//#region src/hooks/feedback.ts
|
|
37
397
|
const fingerprintFinding = (f) => `${f.file}:${f.line}:${f.ruleId}`;
|
|
@@ -4636,7 +4996,7 @@ const lintEngine = {
|
|
|
4636
4996
|
const promises = [];
|
|
4637
4997
|
if (languages.includes("typescript") || languages.includes("javascript")) {
|
|
4638
4998
|
promises.push(runOxlint(context));
|
|
4639
|
-
if (context.config.lint.typecheck) promises.push(import("./typecheck-
|
|
4999
|
+
if (context.config.lint.typecheck) promises.push(import("./typecheck-wVSohmOX.js").then((mod) => mod.runTypecheck(context)));
|
|
4640
5000
|
}
|
|
4641
5001
|
if (context.frameworks.includes("expo")) promises.push(Promise.resolve().then(() => expo_doctor_exports).then((mod) => mod.runExpoDoctor(context)));
|
|
4642
5002
|
if (languages.includes("python") && installedTools["ruff"]) promises.push(runRuffLint(context));
|
|
@@ -5855,6 +6215,16 @@ const runClaudeHook = async (deps = {}) => {
|
|
|
5855
6215
|
score: baseline.score,
|
|
5856
6216
|
findingFingerprints: baseline.findingFingerprints
|
|
5857
6217
|
} : void 0);
|
|
6218
|
+
track({
|
|
6219
|
+
event: "hook_scan_completed",
|
|
6220
|
+
properties: buildHookScanCompletedProps({
|
|
6221
|
+
agent: "claude",
|
|
6222
|
+
score,
|
|
6223
|
+
scoreDelta: baseline ? score - baseline.score : null,
|
|
6224
|
+
findingCount: diagnostics.length,
|
|
6225
|
+
fileCount: files.length
|
|
6226
|
+
})
|
|
6227
|
+
});
|
|
5858
6228
|
const envelope = renderClaudeOutput(JSON.stringify(feedback));
|
|
5859
6229
|
write(JSON.stringify(envelope));
|
|
5860
6230
|
return 0;
|
|
@@ -5986,6 +6356,15 @@ const runCursorHook = async (deps = {}) => {
|
|
|
5986
6356
|
try {
|
|
5987
6357
|
const { diagnostics, score, rootDirectory } = await runScopedScan(cwd, files);
|
|
5988
6358
|
const feedback = buildFeedback(diagnostics, score, rootDirectory);
|
|
6359
|
+
track({
|
|
6360
|
+
event: "hook_scan_completed",
|
|
6361
|
+
properties: buildHookScanCompletedProps({
|
|
6362
|
+
agent: "cursor",
|
|
6363
|
+
score,
|
|
6364
|
+
findingCount: diagnostics.length,
|
|
6365
|
+
fileCount: files.length
|
|
6366
|
+
})
|
|
6367
|
+
});
|
|
5989
6368
|
const serialized = JSON.stringify(feedback);
|
|
5990
6369
|
write(JSON.stringify(renderCursorOutput(serialized)));
|
|
5991
6370
|
writeErr(`${serialized}\n`);
|
|
@@ -6036,6 +6415,15 @@ const runGeminiHook = async (deps = {}) => {
|
|
|
6036
6415
|
try {
|
|
6037
6416
|
const { diagnostics, score, rootDirectory } = await runScopedScan(cwd, files);
|
|
6038
6417
|
const feedback = buildFeedback(diagnostics, score, rootDirectory);
|
|
6418
|
+
track({
|
|
6419
|
+
event: "hook_scan_completed",
|
|
6420
|
+
properties: buildHookScanCompletedProps({
|
|
6421
|
+
agent: "gemini",
|
|
6422
|
+
score,
|
|
6423
|
+
findingCount: diagnostics.length,
|
|
6424
|
+
fileCount: files.length
|
|
6425
|
+
})
|
|
6426
|
+
});
|
|
6039
6427
|
write(JSON.stringify(renderGeminiOutput(JSON.stringify(feedback))));
|
|
6040
6428
|
return 0;
|
|
6041
6429
|
} catch {
|
|
@@ -7050,12 +7438,18 @@ const registerInstall = (hook) => {
|
|
|
7050
7438
|
install.action(async (positional, opts) => {
|
|
7051
7439
|
const agents = await pickAgents("install", opts, positional);
|
|
7052
7440
|
if (agents === null || agents.length === 0) return;
|
|
7053
|
-
await
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7441
|
+
await withCommandLifecycle({
|
|
7442
|
+
command: "hook_install",
|
|
7443
|
+
config: loadConfig(process.cwd()).telemetry
|
|
7444
|
+
}, async () => {
|
|
7445
|
+
await hookInstall({
|
|
7446
|
+
agents,
|
|
7447
|
+
scope: resolveScope(opts),
|
|
7448
|
+
dryRun: Boolean(opts.dryRun),
|
|
7449
|
+
yes: Boolean(opts.yes),
|
|
7450
|
+
qualityGate: Boolean(opts.qualityGate)
|
|
7451
|
+
});
|
|
7452
|
+
return { exitCode: 0 };
|
|
7059
7453
|
});
|
|
7060
7454
|
});
|
|
7061
7455
|
};
|
|
@@ -7065,21 +7459,39 @@ const registerUninstall = (hook) => {
|
|
|
7065
7459
|
uninstall.action(async (positional, opts) => {
|
|
7066
7460
|
const agents = await pickAgents("uninstall", opts, positional);
|
|
7067
7461
|
if (agents === null || agents.length === 0) return;
|
|
7068
|
-
await
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7462
|
+
await withCommandLifecycle({
|
|
7463
|
+
command: "hook_uninstall",
|
|
7464
|
+
config: loadConfig(process.cwd()).telemetry
|
|
7465
|
+
}, async () => {
|
|
7466
|
+
await hookUninstall({
|
|
7467
|
+
agents,
|
|
7468
|
+
scope: resolveScope(opts),
|
|
7469
|
+
dryRun: Boolean(opts.dryRun),
|
|
7470
|
+
yes: true,
|
|
7471
|
+
qualityGate: false
|
|
7472
|
+
});
|
|
7473
|
+
return { exitCode: 0 };
|
|
7074
7474
|
});
|
|
7075
7475
|
});
|
|
7076
7476
|
};
|
|
7077
7477
|
const registerCallbacks = (hook) => {
|
|
7078
7478
|
hook.command("status").description("Show which agent hooks are installed").action(async () => {
|
|
7079
|
-
await
|
|
7479
|
+
await withCommandLifecycle({
|
|
7480
|
+
command: "hook_status",
|
|
7481
|
+
config: loadConfig(process.cwd()).telemetry
|
|
7482
|
+
}, async () => {
|
|
7483
|
+
await hookStatus();
|
|
7484
|
+
return { exitCode: 0 };
|
|
7485
|
+
});
|
|
7080
7486
|
});
|
|
7081
7487
|
hook.command("baseline").description("Capture the current project score as the quality-gate baseline").action(async () => {
|
|
7082
|
-
await
|
|
7488
|
+
await withCommandLifecycle({
|
|
7489
|
+
command: "hook_baseline",
|
|
7490
|
+
config: loadConfig(process.cwd()).telemetry
|
|
7491
|
+
}, async () => {
|
|
7492
|
+
await hookBaseline();
|
|
7493
|
+
return { exitCode: 0 };
|
|
7494
|
+
});
|
|
7083
7495
|
});
|
|
7084
7496
|
hook.command("claude").description("Internal: Claude Code PostToolUse / Stop / FileChanged callback (reads stdin)").option("--stop", "run in Stop-hook mode for the quality gate").option("--on-file-changed", "run in FileChanged mode (refresh baseline on watched file change)").action(async (opts) => {
|
|
7085
7497
|
await hookRun("claude", {
|
|
@@ -7592,73 +8004,6 @@ const renderCleanRun = (input, deps = {}) => {
|
|
|
7592
8004
|
return `\n ${parts.join(` ${sep} `)}\n`;
|
|
7593
8005
|
};
|
|
7594
8006
|
|
|
7595
|
-
//#endregion
|
|
7596
|
-
//#region src/version.ts
|
|
7597
|
-
const APP_VERSION = "0.8.3";
|
|
7598
|
-
|
|
7599
|
-
//#endregion
|
|
7600
|
-
//#region src/utils/telemetry.ts
|
|
7601
|
-
const POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
7602
|
-
const POSTHOG_KEY = "phc_eY2cOMFva9q24GrWeOuvuVIOhCIdjOALxeAR3ItrqbJ";
|
|
7603
|
-
/**
|
|
7604
|
-
* Returns true if telemetry should be disabled.
|
|
7605
|
-
* Telemetry is opt-out: it runs unless explicitly disabled.
|
|
7606
|
-
*/
|
|
7607
|
-
const isTelemetryDisabled = (configEnabled) => {
|
|
7608
|
-
if (process.env.AISLOP_NO_TELEMETRY === "1" || process.env.DO_NOT_TRACK === "1") return true;
|
|
7609
|
-
if (process.env.CI === "true" || process.env.CI === "1") return true;
|
|
7610
|
-
if (configEnabled === false) return true;
|
|
7611
|
-
return false;
|
|
7612
|
-
};
|
|
7613
|
-
const getScoreBucket = (score) => {
|
|
7614
|
-
if (score >= 75) return "75-100";
|
|
7615
|
-
if (score >= 50) return "50-75";
|
|
7616
|
-
if (score >= 25) return "25-50";
|
|
7617
|
-
return "0-25";
|
|
7618
|
-
};
|
|
7619
|
-
const getAnonymousId = () => {
|
|
7620
|
-
const raw = `${os.hostname()}-${os.platform()}-${os.arch()}`;
|
|
7621
|
-
let hash = 5381;
|
|
7622
|
-
for (let i = 0; i < raw.length; i++) hash = hash * 33 ^ raw.charCodeAt(i);
|
|
7623
|
-
return `aislop_${(hash >>> 0).toString(36)}`;
|
|
7624
|
-
};
|
|
7625
|
-
/** Pending telemetry request — kept alive so Node doesn't exit before it completes. */
|
|
7626
|
-
let pendingRequest = null;
|
|
7627
|
-
const trackEvent = (event) => {
|
|
7628
|
-
const payload = {
|
|
7629
|
-
api_key: POSTHOG_KEY,
|
|
7630
|
-
event: `cli_${event.command}`,
|
|
7631
|
-
distinct_id: getAnonymousId(),
|
|
7632
|
-
properties: {
|
|
7633
|
-
version: APP_VERSION,
|
|
7634
|
-
node_version: process.version,
|
|
7635
|
-
os: os.platform(),
|
|
7636
|
-
arch: os.arch(),
|
|
7637
|
-
languages: event.languages,
|
|
7638
|
-
score_bucket: event.scoreBucket,
|
|
7639
|
-
engine_issues: event.engineIssues,
|
|
7640
|
-
engine_timings: event.engineTimings,
|
|
7641
|
-
elapsed_ms: event.elapsedMs,
|
|
7642
|
-
file_count: event.fileCount,
|
|
7643
|
-
fix_steps: event.fixSteps,
|
|
7644
|
-
fix_resolved: event.fixResolved
|
|
7645
|
-
},
|
|
7646
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7647
|
-
};
|
|
7648
|
-
pendingRequest = fetch(`${POSTHOG_HOST}/capture/`, {
|
|
7649
|
-
method: "POST",
|
|
7650
|
-
headers: { "Content-Type": "application/json" },
|
|
7651
|
-
body: JSON.stringify(payload),
|
|
7652
|
-
signal: AbortSignal.timeout(3e3)
|
|
7653
|
-
}).then(() => {}).catch(() => {});
|
|
7654
|
-
};
|
|
7655
|
-
const flushTelemetry = async () => {
|
|
7656
|
-
if (pendingRequest) {
|
|
7657
|
-
await pendingRequest;
|
|
7658
|
-
pendingRequest = null;
|
|
7659
|
-
}
|
|
7660
|
-
};
|
|
7661
|
-
|
|
7662
8007
|
//#endregion
|
|
7663
8008
|
//#region src/commands/scan.ts
|
|
7664
8009
|
const shouldUseSpinner = () => Boolean(process.stderr.isTTY) && process.env.CI !== "true" && process.env.CI !== "1";
|
|
@@ -7716,7 +8061,6 @@ const buildScanRender = (input) => {
|
|
|
7716
8061
|
}, deps)}`;
|
|
7717
8062
|
};
|
|
7718
8063
|
const scanCommand = async (directory, config, options) => {
|
|
7719
|
-
const startTime = performance.now();
|
|
7720
8064
|
const resolvedDir = path.resolve(directory);
|
|
7721
8065
|
if (!fs.existsSync(resolvedDir)) {
|
|
7722
8066
|
const msg = `Path does not exist: ${resolvedDir}`;
|
|
@@ -7730,9 +8074,18 @@ const scanCommand = async (directory, config, options) => {
|
|
|
7730
8074
|
else log.error(msg);
|
|
7731
8075
|
return { exitCode: 1 };
|
|
7732
8076
|
}
|
|
8077
|
+
const projectInfo = await discoverProject(resolvedDir);
|
|
8078
|
+
return withCommandLifecycle({
|
|
8079
|
+
command: options.command ?? "scan",
|
|
8080
|
+
config: config.telemetry,
|
|
8081
|
+
languages: projectInfo.languages,
|
|
8082
|
+
fileCount: projectInfo.sourceFileCount
|
|
8083
|
+
}, () => runScanBody(resolvedDir, config, options, projectInfo));
|
|
8084
|
+
};
|
|
8085
|
+
const runScanBody = async (resolvedDir, config, options, projectInfo) => {
|
|
8086
|
+
const startTime = performance.now();
|
|
7733
8087
|
const showHeader = options.showHeader !== false;
|
|
7734
8088
|
const useLiveProgress = !options.json && shouldUseSpinner();
|
|
7735
|
-
const projectInfo = await discoverProject(resolvedDir);
|
|
7736
8089
|
let files;
|
|
7737
8090
|
if (options.staged) {
|
|
7738
8091
|
files = filterProjectFiles(resolvedDir, getStagedFiles(resolvedDir), [], config.exclude);
|
|
@@ -7799,28 +8152,27 @@ const scanCommand = async (directory, config, options) => {
|
|
|
7799
8152
|
const elapsedMs = performance.now() - startTime;
|
|
7800
8153
|
const scoreResult = calculateScore(allDiagnostics, config.scoring.weights, config.scoring.thresholds, projectInfo.sourceFileCount, config.scoring.smoothing);
|
|
7801
8154
|
const exitCode = allDiagnostics.some((d) => d.severity === "error") || scoreResult.score < config.ci.failBelow ? 1 : 0;
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
}
|
|
8155
|
+
const engineIssues = {};
|
|
8156
|
+
const engineTimings = {};
|
|
8157
|
+
for (const r of results) {
|
|
8158
|
+
engineIssues[r.engine] = r.diagnostics.length;
|
|
8159
|
+
engineTimings[r.engine] = Math.round(r.elapsed);
|
|
8160
|
+
}
|
|
8161
|
+
const completion = {
|
|
8162
|
+
exitCode,
|
|
8163
|
+
score: scoreResult.score,
|
|
8164
|
+
findingCount: allDiagnostics.length,
|
|
8165
|
+
errorCount: allDiagnostics.filter((d) => d.severity === "error").length,
|
|
8166
|
+
warningCount: allDiagnostics.filter((d) => d.severity === "warning").length,
|
|
8167
|
+
fixableCount: allDiagnostics.filter((d) => d.fixable).length,
|
|
8168
|
+
engineIssues,
|
|
8169
|
+
engineTimings
|
|
8170
|
+
};
|
|
7819
8171
|
if (options.json) {
|
|
7820
|
-
const { buildJsonOutput } = await import("./json-
|
|
8172
|
+
const { buildJsonOutput } = await import("./json-OIzja7OM.js");
|
|
7821
8173
|
const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
|
|
7822
8174
|
console.log(JSON.stringify(jsonOut, null, 2));
|
|
7823
|
-
return
|
|
8175
|
+
return completion;
|
|
7824
8176
|
}
|
|
7825
8177
|
const projectName = projectInfo.projectName ?? "project";
|
|
7826
8178
|
const language = projectInfo.languages[0] ?? "unknown";
|
|
@@ -7837,7 +8189,7 @@ const scanCommand = async (directory, config, options) => {
|
|
|
7837
8189
|
includeHeader: showHeader,
|
|
7838
8190
|
printBrand: options.printBrand
|
|
7839
8191
|
}));
|
|
7840
|
-
return
|
|
8192
|
+
return completion;
|
|
7841
8193
|
};
|
|
7842
8194
|
|
|
7843
8195
|
//#endregion
|
|
@@ -9785,15 +10137,23 @@ const fixCommand = async (directory, config, options = {
|
|
|
9785
10137
|
verbose: false,
|
|
9786
10138
|
showHeader: true
|
|
9787
10139
|
}) => {
|
|
9788
|
-
const startTime = performance.now();
|
|
9789
10140
|
const resolvedDir = path.resolve(directory);
|
|
9790
10141
|
if (!fs.existsSync(resolvedDir) || !fs.statSync(resolvedDir).isDirectory()) {
|
|
9791
10142
|
const msg = !fs.existsSync(resolvedDir) ? `Path does not exist: ${resolvedDir}` : `Not a directory: ${resolvedDir}`;
|
|
9792
10143
|
log.error(msg);
|
|
9793
10144
|
return;
|
|
9794
10145
|
}
|
|
9795
|
-
const showHeader = options.showHeader !== false;
|
|
9796
10146
|
const projectInfo = await discoverProject(resolvedDir);
|
|
10147
|
+
await withCommandLifecycle({
|
|
10148
|
+
command: "fix",
|
|
10149
|
+
config: config.telemetry,
|
|
10150
|
+
languages: projectInfo.languages,
|
|
10151
|
+
fileCount: projectInfo.sourceFileCount
|
|
10152
|
+
}, () => runFixBody(resolvedDir, config, options, projectInfo));
|
|
10153
|
+
};
|
|
10154
|
+
const runFixBody = async (resolvedDir, config, options, projectInfo) => {
|
|
10155
|
+
const startTime = performance.now();
|
|
10156
|
+
const showHeader = options.showHeader !== false;
|
|
9797
10157
|
const projectName = projectInfo.projectName ?? "project";
|
|
9798
10158
|
if (showHeader) process.stdout.write(renderHeader({
|
|
9799
10159
|
version: APP_VERSION,
|
|
@@ -9830,12 +10190,6 @@ const fixCommand = async (directory, config, options = {
|
|
|
9830
10190
|
await runFormattingStep(pipelineDeps);
|
|
9831
10191
|
await runForceSteps(pipelineDeps);
|
|
9832
10192
|
const totalResolved = steps.reduce((sum, s) => sum + s.resolvedIssues, 0);
|
|
9833
|
-
if (!isTelemetryDisabled(config.telemetry?.enabled)) trackEvent({
|
|
9834
|
-
command: "fix",
|
|
9835
|
-
languages: projectInfo.languages,
|
|
9836
|
-
fixSteps: steps.length,
|
|
9837
|
-
fixResolved: totalResolved
|
|
9838
|
-
});
|
|
9839
10193
|
const configDir = findConfigDir(resolvedDir);
|
|
9840
10194
|
const rulesPath = configDir ? path.join(configDir, RULES_FILE) : void 0;
|
|
9841
10195
|
const engineConfig = {
|
|
@@ -9858,7 +10212,9 @@ const fixCommand = async (directory, config, options = {
|
|
|
9858
10212
|
});
|
|
9859
10213
|
const allDiagnostics = scanResults.flatMap((r) => r.diagnostics);
|
|
9860
10214
|
const scoreResult = calculateScore(allDiagnostics, config.scoring.weights, config.scoring.thresholds, projectInfo.sourceFileCount, config.scoring.smoothing);
|
|
9861
|
-
const
|
|
10215
|
+
const errors = allDiagnostics.filter((d) => d.severity === "error").length;
|
|
10216
|
+
const warnings = allDiagnostics.filter((d) => d.severity === "warning").length;
|
|
10217
|
+
const remaining = errors + warnings;
|
|
9862
10218
|
if (steps.length === 0) rail.complete({
|
|
9863
10219
|
status: "skipped",
|
|
9864
10220
|
label: "No applicable auto-fixers found"
|
|
@@ -9887,12 +10243,31 @@ const fixCommand = async (directory, config, options = {
|
|
|
9887
10243
|
}
|
|
9888
10244
|
if (options.agent) {
|
|
9889
10245
|
launchAgent(options.agent, resolvedDir, allDiagnostics, scoreResult.score);
|
|
9890
|
-
return
|
|
10246
|
+
return {
|
|
10247
|
+
exitCode: 0,
|
|
10248
|
+
score: scoreResult.score,
|
|
10249
|
+
fixSteps: steps.length,
|
|
10250
|
+
fixResolved: totalResolved
|
|
10251
|
+
};
|
|
9891
10252
|
}
|
|
9892
10253
|
if (options.prompt) {
|
|
9893
10254
|
printPrompt(resolvedDir, allDiagnostics, scoreResult.score);
|
|
9894
|
-
return
|
|
10255
|
+
return {
|
|
10256
|
+
exitCode: 0,
|
|
10257
|
+
score: scoreResult.score,
|
|
10258
|
+
fixSteps: steps.length,
|
|
10259
|
+
fixResolved: totalResolved
|
|
10260
|
+
};
|
|
9895
10261
|
}
|
|
10262
|
+
return {
|
|
10263
|
+
exitCode: 0,
|
|
10264
|
+
score: scoreResult.score,
|
|
10265
|
+
findingCount: allDiagnostics.length,
|
|
10266
|
+
errorCount: errors,
|
|
10267
|
+
warningCount: warnings,
|
|
10268
|
+
fixSteps: steps.length,
|
|
10269
|
+
fixResolved: totalResolved
|
|
10270
|
+
};
|
|
9896
10271
|
};
|
|
9897
10272
|
|
|
9898
10273
|
//#endregion
|
|
@@ -10375,6 +10750,13 @@ const interactiveCommand = async (directory, config) => {
|
|
|
10375
10750
|
//#region src/cli.ts
|
|
10376
10751
|
process.on("SIGINT", () => process.exit(0));
|
|
10377
10752
|
process.on("SIGTERM", () => process.exit(0));
|
|
10753
|
+
const fireInstalledOnce = () => {
|
|
10754
|
+
if (isTelemetryDisabled(loadConfig(process.cwd()).telemetry)) return;
|
|
10755
|
+
if (ensureInstallId(resolveInstallIdPath()).created) track({
|
|
10756
|
+
event: "cli_installed",
|
|
10757
|
+
config: loadConfig(process.cwd()).telemetry
|
|
10758
|
+
});
|
|
10759
|
+
};
|
|
10378
10760
|
const excludeParser = (value, previous = []) => {
|
|
10379
10761
|
const parts = value.split(",").map((v) => v.trim()).filter(Boolean);
|
|
10380
10762
|
return [...previous, ...parts];
|
|
@@ -10523,10 +10905,22 @@ fixProgram.action(async (directory = ".", _flags, command) => {
|
|
|
10523
10905
|
});
|
|
10524
10906
|
});
|
|
10525
10907
|
program.command("init [directory]").description("Initialize aislop config in project").action(async (directory = ".") => {
|
|
10526
|
-
await
|
|
10908
|
+
await withCommandLifecycle({
|
|
10909
|
+
command: "init",
|
|
10910
|
+
config: loadConfig(directory).telemetry
|
|
10911
|
+
}, async () => {
|
|
10912
|
+
await initCommand(directory);
|
|
10913
|
+
return { exitCode: 0 };
|
|
10914
|
+
});
|
|
10527
10915
|
});
|
|
10528
10916
|
program.command("doctor [directory]").description("Check installed tools and environment").action(async (directory = ".") => {
|
|
10529
|
-
await
|
|
10917
|
+
await withCommandLifecycle({
|
|
10918
|
+
command: "doctor",
|
|
10919
|
+
config: loadConfig(directory).telemetry
|
|
10920
|
+
}, async () => {
|
|
10921
|
+
await doctorCommand(directory);
|
|
10922
|
+
return { exitCode: 0 };
|
|
10923
|
+
});
|
|
10530
10924
|
});
|
|
10531
10925
|
program.command("ci [directory]").description("CI-friendly JSON output with exit codes").option("--human", "render the human-friendly scan design instead of JSON").action(async (directory = ".", _flags, command) => {
|
|
10532
10926
|
const flags = command.optsWithGlobals();
|
|
@@ -10537,16 +10931,28 @@ program.command("ci [directory]").description("CI-friendly JSON output with exit
|
|
|
10537
10931
|
}
|
|
10538
10932
|
});
|
|
10539
10933
|
program.command("rules [directory]").description("List all available rules").action(async (directory = ".") => {
|
|
10540
|
-
await
|
|
10934
|
+
await withCommandLifecycle({
|
|
10935
|
+
command: "rules",
|
|
10936
|
+
config: loadConfig(directory).telemetry
|
|
10937
|
+
}, async () => {
|
|
10938
|
+
await rulesCommand(directory);
|
|
10939
|
+
return { exitCode: 0 };
|
|
10940
|
+
});
|
|
10541
10941
|
});
|
|
10542
10942
|
program.command("badge [directory]").description("Print the public score badge URL + README markdown for this repo").option("--owner <owner>", "GitHub owner (auto-detected from git remote if omitted)").option("--repo <repo>", "GitHub repo name (auto-detected from git remote if omitted)").option("--json", "emit machine-readable JSON instead of the rendered output").action(async (directory = ".", _flags, command) => {
|
|
10543
10943
|
const flags = command.optsWithGlobals();
|
|
10544
10944
|
try {
|
|
10545
|
-
await
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10945
|
+
await withCommandLifecycle({
|
|
10946
|
+
command: "badge",
|
|
10947
|
+
config: loadConfig(directory).telemetry
|
|
10948
|
+
}, async () => {
|
|
10949
|
+
await badgeCommand({
|
|
10950
|
+
directory,
|
|
10951
|
+
owner: flags.owner,
|
|
10952
|
+
repo: flags.repo,
|
|
10953
|
+
json: Boolean(flags.json)
|
|
10954
|
+
});
|
|
10955
|
+
return { exitCode: 0 };
|
|
10550
10956
|
});
|
|
10551
10957
|
} catch (err) {
|
|
10552
10958
|
process.stderr.write(`${err?.message ?? "Failed to print badge"}\n`);
|
|
@@ -10555,10 +10961,11 @@ program.command("badge [directory]").description("Print the public score badge U
|
|
|
10555
10961
|
});
|
|
10556
10962
|
registerHookCommand(program);
|
|
10557
10963
|
const main = async () => {
|
|
10964
|
+
fireInstalledOnce();
|
|
10558
10965
|
await program.parseAsync();
|
|
10559
10966
|
await flushTelemetry();
|
|
10560
10967
|
};
|
|
10561
10968
|
main();
|
|
10562
10969
|
|
|
10563
10970
|
//#endregion
|
|
10564
|
-
export {
|
|
10971
|
+
export { runSubprocess as n, APP_VERSION as r, ENGINE_INFO as t };
|