aislop 0.1.0 → 0.1.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.
- package/README.md +20 -0
- package/dist/cli.js +136 -10
- package/dist/{engine-info-DBG3uXLc.js → engine-info-Bw_OOj3G.js} +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +135 -10
- package/dist/{json-DkpW9UQj.js → json-SHDiefXX.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -326,6 +326,26 @@ npx aislop scan --staged
|
|
|
326
326
|
|
|
327
327
|
---
|
|
328
328
|
|
|
329
|
+
## Telemetry
|
|
330
|
+
|
|
331
|
+
`aislop` collects anonymous usage analytics to help us understand how the tool is used and prioritize improvements. **No code, file paths, project names, or secrets are ever collected.**
|
|
332
|
+
|
|
333
|
+
What we collect: command run (scan/fix/ci), languages detected, score bucket, issue counts per engine, engine timing, OS, Node version, and aislop version.
|
|
334
|
+
|
|
335
|
+
Telemetry is **off in CI** by default. To opt out anywhere:
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
# Environment variable (any of these)
|
|
339
|
+
AISLOP_NO_TELEMETRY=1 aislop scan
|
|
340
|
+
DO_NOT_TRACK=1 aislop scan
|
|
341
|
+
|
|
342
|
+
# Or in .aislop/config.yml
|
|
343
|
+
telemetry:
|
|
344
|
+
enabled: false
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
329
349
|
## Contributing
|
|
330
350
|
|
|
331
351
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, project architecture, and how to add new rules or engines.
|
package/dist/cli.js
CHANGED
|
@@ -51,7 +51,8 @@ const DEFAULT_CONFIG = {
|
|
|
51
51
|
ci: {
|
|
52
52
|
failBelow: 0,
|
|
53
53
|
format: "json"
|
|
54
|
-
}
|
|
54
|
+
},
|
|
55
|
+
telemetry: { enabled: true }
|
|
55
56
|
};
|
|
56
57
|
const DEFAULT_CONFIG_YAML = `version: 1
|
|
57
58
|
|
|
@@ -88,6 +89,9 @@ scoring:
|
|
|
88
89
|
ci:
|
|
89
90
|
failBelow: 0
|
|
90
91
|
format: json
|
|
92
|
+
|
|
93
|
+
# telemetry:
|
|
94
|
+
# enabled: true # set to false to disable anonymous usage analytics
|
|
91
95
|
`;
|
|
92
96
|
const DEFAULT_RULES_YAML = `# Architecture rules (BYO)
|
|
93
97
|
# Uncomment and customize to enforce your project's conventions.
|
|
@@ -148,6 +152,7 @@ const CiSchema = z.object({
|
|
|
148
152
|
failBelow: z.number().default(0),
|
|
149
153
|
format: z.enum(["json"]).default("json")
|
|
150
154
|
});
|
|
155
|
+
const TelemetrySchema = z.object({ enabled: z.boolean().default(true) });
|
|
151
156
|
const AislopConfigSchema = z.object({
|
|
152
157
|
version: z.number().default(1),
|
|
153
158
|
engines: EnginesSchema.default(() => ({
|
|
@@ -178,7 +183,8 @@ const AislopConfigSchema = z.object({
|
|
|
178
183
|
ci: CiSchema.default(() => ({
|
|
179
184
|
failBelow: 0,
|
|
180
185
|
format: "json"
|
|
181
|
-
}))
|
|
186
|
+
})),
|
|
187
|
+
telemetry: TelemetrySchema.default(() => ({ enabled: true }))
|
|
182
188
|
});
|
|
183
189
|
const defaults = AislopConfigSchema.parse({});
|
|
184
190
|
/**
|
|
@@ -1380,17 +1386,21 @@ const isDataFile = (content) => {
|
|
|
1380
1386
|
const dataLinePattern = /^\s*[{}[\]"']/;
|
|
1381
1387
|
return nonEmpty.filter((l) => dataLinePattern.test(l)).length / nonEmpty.length > .8;
|
|
1382
1388
|
};
|
|
1389
|
+
const ARROW_BLOCK_RE = /* @__PURE__ */ new RegExp("=>\\s*\\{");
|
|
1390
|
+
const ARROW_END_RE = /* @__PURE__ */ new RegExp("=>\\s*$");
|
|
1391
|
+
const BRACE_START_RE = /* @__PURE__ */ new RegExp("^\\s*\\{");
|
|
1392
|
+
const NEW_STATEMENT_RE = /* @__PURE__ */ new RegExp("^(?:export\\s+)?(?:const|let|var|function|class)\\s");
|
|
1383
1393
|
const isBlockArrow = (lines, startIndex) => {
|
|
1384
|
-
if (
|
|
1385
|
-
if (
|
|
1394
|
+
if (ARROW_BLOCK_RE.test(lines[startIndex])) return true;
|
|
1395
|
+
if (ARROW_END_RE.test(lines[startIndex])) {
|
|
1386
1396
|
const next = lines[startIndex + 1];
|
|
1387
|
-
if (next &&
|
|
1397
|
+
if (next && BRACE_START_RE.test(next)) return true;
|
|
1388
1398
|
}
|
|
1389
1399
|
for (let j = startIndex + 1; j < Math.min(startIndex + 3, lines.length); j++) {
|
|
1390
1400
|
const l = lines[j];
|
|
1391
|
-
if (l.trim() === "" ||
|
|
1392
|
-
if (
|
|
1393
|
-
if (
|
|
1401
|
+
if (l.trim() === "" || NEW_STATEMENT_RE.test(l.trim())) break;
|
|
1402
|
+
if (ARROW_BLOCK_RE.test(l)) return true;
|
|
1403
|
+
if (BRACE_START_RE.test(l)) return true;
|
|
1394
1404
|
}
|
|
1395
1405
|
return false;
|
|
1396
1406
|
};
|
|
@@ -3104,7 +3114,7 @@ const logger = {
|
|
|
3104
3114
|
* Application version — injected at build time by tsdown from package.json.
|
|
3105
3115
|
* The fallback should always match the "version" field in package.json.
|
|
3106
3116
|
*/
|
|
3107
|
-
const APP_VERSION = "0.1.
|
|
3117
|
+
const APP_VERSION = "0.1.1";
|
|
3108
3118
|
|
|
3109
3119
|
//#endregion
|
|
3110
3120
|
//#region src/output/layout.ts
|
|
@@ -3613,6 +3623,95 @@ const spinner = (text) => ({ start() {
|
|
|
3613
3623
|
};
|
|
3614
3624
|
} });
|
|
3615
3625
|
|
|
3626
|
+
//#endregion
|
|
3627
|
+
//#region src/utils/telemetry.ts
|
|
3628
|
+
/**
|
|
3629
|
+
* Anonymous, opt-out telemetry for aislop.
|
|
3630
|
+
*
|
|
3631
|
+
* What we collect:
|
|
3632
|
+
* - Command run (scan, fix, ci)
|
|
3633
|
+
* - Languages detected in the project
|
|
3634
|
+
* - Score bucket (0-25, 25-50, 50-75, 75-100)
|
|
3635
|
+
* - Issue counts per engine (not file paths or code)
|
|
3636
|
+
* - Engine timing (milliseconds)
|
|
3637
|
+
* - OS, architecture, and Node version
|
|
3638
|
+
* - aislop version
|
|
3639
|
+
*
|
|
3640
|
+
* What we never collect:
|
|
3641
|
+
* - File paths, file contents, or code snippets
|
|
3642
|
+
* - Project names or directory paths
|
|
3643
|
+
* - Git remotes, branch names, or commit hashes
|
|
3644
|
+
* - Environment variables or secrets
|
|
3645
|
+
* - IP addresses are not stored (PostHog configured to discard)
|
|
3646
|
+
*
|
|
3647
|
+
* How to opt out (any one of these):
|
|
3648
|
+
* - Set AISLOP_NO_TELEMETRY=1
|
|
3649
|
+
* - Set DO_NOT_TRACK=1 (https://consoledonottrack.com)
|
|
3650
|
+
* - Set CI=true (telemetry is off in CI by default)
|
|
3651
|
+
* - Set telemetry.enabled: false in .aislop/config.yml
|
|
3652
|
+
*/
|
|
3653
|
+
const POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
3654
|
+
const POSTHOG_KEY = "phc_eY2cOMFva9q24GrWeOuvuVIOhCIdjOALxeAR3ItrqbJ";
|
|
3655
|
+
/**
|
|
3656
|
+
* Returns true if telemetry should be disabled.
|
|
3657
|
+
* Telemetry is opt-out: it runs unless explicitly disabled.
|
|
3658
|
+
*/
|
|
3659
|
+
const isTelemetryDisabled = (configEnabled) => {
|
|
3660
|
+
if (process.env.AISLOP_NO_TELEMETRY === "1" || process.env.DO_NOT_TRACK === "1") return true;
|
|
3661
|
+
if (process.env.CI === "true" || process.env.CI === "1") return true;
|
|
3662
|
+
if (configEnabled === false) return true;
|
|
3663
|
+
return false;
|
|
3664
|
+
};
|
|
3665
|
+
const getScoreBucket = (score) => {
|
|
3666
|
+
if (score >= 75) return "75-100";
|
|
3667
|
+
if (score >= 50) return "50-75";
|
|
3668
|
+
if (score >= 25) return "25-50";
|
|
3669
|
+
return "0-25";
|
|
3670
|
+
};
|
|
3671
|
+
/**
|
|
3672
|
+
* Returns a stable anonymous device ID derived from hostname + OS.
|
|
3673
|
+
* This is NOT personally identifiable — it's a hash used only to
|
|
3674
|
+
* count unique devices, not to identify users.
|
|
3675
|
+
*/
|
|
3676
|
+
const getAnonymousId = () => {
|
|
3677
|
+
const raw = `${os.hostname()}-${os.platform()}-${os.arch()}`;
|
|
3678
|
+
let hash = 5381;
|
|
3679
|
+
for (let i = 0; i < raw.length; i++) hash = hash * 33 ^ raw.charCodeAt(i);
|
|
3680
|
+
return `aislop_${(hash >>> 0).toString(36)}`;
|
|
3681
|
+
};
|
|
3682
|
+
/**
|
|
3683
|
+
* Fire-and-forget telemetry event to PostHog.
|
|
3684
|
+
* Never throws, never blocks, never affects CLI output or exit code.
|
|
3685
|
+
*/
|
|
3686
|
+
const trackEvent = (event) => {
|
|
3687
|
+
const payload = {
|
|
3688
|
+
api_key: POSTHOG_KEY,
|
|
3689
|
+
event: `cli_${event.command}`,
|
|
3690
|
+
distinct_id: getAnonymousId(),
|
|
3691
|
+
properties: {
|
|
3692
|
+
version: APP_VERSION,
|
|
3693
|
+
node_version: process.version,
|
|
3694
|
+
os: os.platform(),
|
|
3695
|
+
arch: os.arch(),
|
|
3696
|
+
languages: event.languages,
|
|
3697
|
+
score_bucket: event.scoreBucket,
|
|
3698
|
+
engine_issues: event.engineIssues,
|
|
3699
|
+
engine_timings: event.engineTimings,
|
|
3700
|
+
elapsed_ms: event.elapsedMs,
|
|
3701
|
+
file_count: event.fileCount,
|
|
3702
|
+
fix_steps: event.fixSteps,
|
|
3703
|
+
fix_resolved: event.fixResolved
|
|
3704
|
+
},
|
|
3705
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3706
|
+
};
|
|
3707
|
+
fetch(`${POSTHOG_HOST}/capture/`, {
|
|
3708
|
+
method: "POST",
|
|
3709
|
+
headers: { "Content-Type": "application/json" },
|
|
3710
|
+
body: JSON.stringify(payload),
|
|
3711
|
+
signal: AbortSignal.timeout(3e3)
|
|
3712
|
+
}).catch(() => {});
|
|
3713
|
+
};
|
|
3714
|
+
|
|
3616
3715
|
//#endregion
|
|
3617
3716
|
//#region src/commands/scan.ts
|
|
3618
3717
|
const shouldUseSpinner = () => Boolean(process.stderr.isTTY) && process.env.CI !== "true" && process.env.CI !== "1";
|
|
@@ -3670,6 +3769,23 @@ const scanCommand = async (directory, config, options) => {
|
|
|
3670
3769
|
const elapsedMs = performance.now() - startTime;
|
|
3671
3770
|
const scoreResult = calculateScore(allDiagnostics, config.scoring.weights, config.scoring.thresholds);
|
|
3672
3771
|
const exitCode = scoreResult.score < config.ci.failBelow ? 1 : 0;
|
|
3772
|
+
if (!isTelemetryDisabled(config.telemetry?.enabled)) {
|
|
3773
|
+
const engineIssues = {};
|
|
3774
|
+
const engineTimings = {};
|
|
3775
|
+
for (const r of results) {
|
|
3776
|
+
engineIssues[r.engine] = r.diagnostics.length;
|
|
3777
|
+
engineTimings[r.engine] = Math.round(r.elapsed);
|
|
3778
|
+
}
|
|
3779
|
+
trackEvent({
|
|
3780
|
+
command: options.command ?? "scan",
|
|
3781
|
+
languages: projectInfo.languages,
|
|
3782
|
+
scoreBucket: getScoreBucket(scoreResult.score),
|
|
3783
|
+
engineIssues,
|
|
3784
|
+
engineTimings,
|
|
3785
|
+
elapsedMs: Math.round(elapsedMs),
|
|
3786
|
+
fileCount: projectInfo.sourceFileCount
|
|
3787
|
+
});
|
|
3788
|
+
}
|
|
3673
3789
|
if (options.json) {
|
|
3674
3790
|
const { buildJsonOutput } = await import("./json-L5x3hQdy.js");
|
|
3675
3791
|
const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
|
|
@@ -3692,7 +3808,8 @@ const ciCommand = async (directory, config) => {
|
|
|
3692
3808
|
changes: false,
|
|
3693
3809
|
staged: false,
|
|
3694
3810
|
verbose: false,
|
|
3695
|
-
json: true
|
|
3811
|
+
json: true,
|
|
3812
|
+
command: "ci"
|
|
3696
3813
|
});
|
|
3697
3814
|
};
|
|
3698
3815
|
|
|
@@ -3944,6 +4061,15 @@ const fixCommand = async (directory, config, options = {
|
|
|
3944
4061
|
logger.break();
|
|
3945
4062
|
summarizeFixRun(steps);
|
|
3946
4063
|
}
|
|
4064
|
+
if (!isTelemetryDisabled(config.telemetry?.enabled)) {
|
|
4065
|
+
const totalResolved = steps.reduce((sum, s) => sum + s.resolvedIssues, 0);
|
|
4066
|
+
trackEvent({
|
|
4067
|
+
command: "fix",
|
|
4068
|
+
languages: projectInfo.languages,
|
|
4069
|
+
fixSteps: steps.length,
|
|
4070
|
+
fixResolved: totalResolved
|
|
4071
|
+
});
|
|
4072
|
+
}
|
|
3947
4073
|
logger.break();
|
|
3948
4074
|
logger.success(" ✓ Done. Run `aislop scan` to verify.");
|
|
3949
4075
|
logger.break();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Application version — injected at build time by tsdown from package.json.
|
|
4
4
|
* The fallback should always match the "version" field in package.json.
|
|
5
5
|
*/
|
|
6
|
-
const APP_VERSION = "0.1.
|
|
6
|
+
const APP_VERSION = "0.1.1";
|
|
7
7
|
|
|
8
8
|
//#endregion
|
|
9
9
|
//#region src/output/engine-info.ts
|
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,9 @@ declare const AislopConfigSchema: z.ZodObject<{
|
|
|
37
37
|
json: "json";
|
|
38
38
|
}>>;
|
|
39
39
|
}, z.core.$strip>>;
|
|
40
|
+
telemetry: z.ZodDefault<z.ZodObject<{
|
|
41
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
42
|
+
}, z.core.$strip>>;
|
|
40
43
|
}, z.core.$strip>;
|
|
41
44
|
type AislopConfig = z.infer<typeof AislopConfigSchema>;
|
|
42
45
|
//#endregion
|
|
@@ -60,6 +63,8 @@ interface ScanOptions {
|
|
|
60
63
|
verbose: boolean;
|
|
61
64
|
json: boolean;
|
|
62
65
|
showHeader?: boolean;
|
|
66
|
+
/** Used for telemetry to distinguish scan vs ci invocation */
|
|
67
|
+
command?: "scan" | "ci";
|
|
63
68
|
}
|
|
64
69
|
declare const scanCommand: (directory: string, config: AislopConfig, options: ScanOptions) => Promise<{
|
|
65
70
|
exitCode: number;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as getEngineLabel, r as APP_VERSION, t as ENGINE_INFO } from "./engine-info-
|
|
1
|
+
import { n as getEngineLabel, r as APP_VERSION, t as ENGINE_INFO } from "./engine-info-Bw_OOj3G.js";
|
|
2
2
|
import { n as runSubprocess, t as isToolInstalled } from "./subprocess-99puEEGl.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import fs from "node:fs";
|
|
@@ -1022,6 +1022,95 @@ const printMaybePaged = async (text) => {
|
|
|
1022
1022
|
if (!await pipeToPager(pager.command, pager.args, text)) writeToStdout(text);
|
|
1023
1023
|
};
|
|
1024
1024
|
|
|
1025
|
+
//#endregion
|
|
1026
|
+
//#region src/utils/telemetry.ts
|
|
1027
|
+
/**
|
|
1028
|
+
* Anonymous, opt-out telemetry for aislop.
|
|
1029
|
+
*
|
|
1030
|
+
* What we collect:
|
|
1031
|
+
* - Command run (scan, fix, ci)
|
|
1032
|
+
* - Languages detected in the project
|
|
1033
|
+
* - Score bucket (0-25, 25-50, 50-75, 75-100)
|
|
1034
|
+
* - Issue counts per engine (not file paths or code)
|
|
1035
|
+
* - Engine timing (milliseconds)
|
|
1036
|
+
* - OS, architecture, and Node version
|
|
1037
|
+
* - aislop version
|
|
1038
|
+
*
|
|
1039
|
+
* What we never collect:
|
|
1040
|
+
* - File paths, file contents, or code snippets
|
|
1041
|
+
* - Project names or directory paths
|
|
1042
|
+
* - Git remotes, branch names, or commit hashes
|
|
1043
|
+
* - Environment variables or secrets
|
|
1044
|
+
* - IP addresses are not stored (PostHog configured to discard)
|
|
1045
|
+
*
|
|
1046
|
+
* How to opt out (any one of these):
|
|
1047
|
+
* - Set AISLOP_NO_TELEMETRY=1
|
|
1048
|
+
* - Set DO_NOT_TRACK=1 (https://consoledonottrack.com)
|
|
1049
|
+
* - Set CI=true (telemetry is off in CI by default)
|
|
1050
|
+
* - Set telemetry.enabled: false in .aislop/config.yml
|
|
1051
|
+
*/
|
|
1052
|
+
const POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
1053
|
+
const POSTHOG_KEY = "phc_eY2cOMFva9q24GrWeOuvuVIOhCIdjOALxeAR3ItrqbJ";
|
|
1054
|
+
/**
|
|
1055
|
+
* Returns true if telemetry should be disabled.
|
|
1056
|
+
* Telemetry is opt-out: it runs unless explicitly disabled.
|
|
1057
|
+
*/
|
|
1058
|
+
const isTelemetryDisabled = (configEnabled) => {
|
|
1059
|
+
if (process.env.AISLOP_NO_TELEMETRY === "1" || process.env.DO_NOT_TRACK === "1") return true;
|
|
1060
|
+
if (process.env.CI === "true" || process.env.CI === "1") return true;
|
|
1061
|
+
if (configEnabled === false) return true;
|
|
1062
|
+
return false;
|
|
1063
|
+
};
|
|
1064
|
+
const getScoreBucket = (score) => {
|
|
1065
|
+
if (score >= 75) return "75-100";
|
|
1066
|
+
if (score >= 50) return "50-75";
|
|
1067
|
+
if (score >= 25) return "25-50";
|
|
1068
|
+
return "0-25";
|
|
1069
|
+
};
|
|
1070
|
+
/**
|
|
1071
|
+
* Returns a stable anonymous device ID derived from hostname + OS.
|
|
1072
|
+
* This is NOT personally identifiable — it's a hash used only to
|
|
1073
|
+
* count unique devices, not to identify users.
|
|
1074
|
+
*/
|
|
1075
|
+
const getAnonymousId = () => {
|
|
1076
|
+
const raw = `${os.hostname()}-${os.platform()}-${os.arch()}`;
|
|
1077
|
+
let hash = 5381;
|
|
1078
|
+
for (let i = 0; i < raw.length; i++) hash = hash * 33 ^ raw.charCodeAt(i);
|
|
1079
|
+
return `aislop_${(hash >>> 0).toString(36)}`;
|
|
1080
|
+
};
|
|
1081
|
+
/**
|
|
1082
|
+
* Fire-and-forget telemetry event to PostHog.
|
|
1083
|
+
* Never throws, never blocks, never affects CLI output or exit code.
|
|
1084
|
+
*/
|
|
1085
|
+
const trackEvent = (event) => {
|
|
1086
|
+
const payload = {
|
|
1087
|
+
api_key: POSTHOG_KEY,
|
|
1088
|
+
event: `cli_${event.command}`,
|
|
1089
|
+
distinct_id: getAnonymousId(),
|
|
1090
|
+
properties: {
|
|
1091
|
+
version: APP_VERSION,
|
|
1092
|
+
node_version: process.version,
|
|
1093
|
+
os: os.platform(),
|
|
1094
|
+
arch: os.arch(),
|
|
1095
|
+
languages: event.languages,
|
|
1096
|
+
score_bucket: event.scoreBucket,
|
|
1097
|
+
engine_issues: event.engineIssues,
|
|
1098
|
+
engine_timings: event.engineTimings,
|
|
1099
|
+
elapsed_ms: event.elapsedMs,
|
|
1100
|
+
file_count: event.fileCount,
|
|
1101
|
+
fix_steps: event.fixSteps,
|
|
1102
|
+
fix_resolved: event.fixResolved
|
|
1103
|
+
},
|
|
1104
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1105
|
+
};
|
|
1106
|
+
fetch(`${POSTHOG_HOST}/capture/`, {
|
|
1107
|
+
method: "POST",
|
|
1108
|
+
headers: { "Content-Type": "application/json" },
|
|
1109
|
+
body: JSON.stringify(payload),
|
|
1110
|
+
signal: AbortSignal.timeout(3e3)
|
|
1111
|
+
}).catch(() => {});
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1025
1114
|
//#endregion
|
|
1026
1115
|
//#region src/commands/fix.ts
|
|
1027
1116
|
const uniqueFiles = (diagnostics) => [...new Set(diagnostics.map((d) => d.filePath))];
|
|
@@ -1136,6 +1225,15 @@ const fixCommand = async (directory, config, options = {
|
|
|
1136
1225
|
logger.break();
|
|
1137
1226
|
summarizeFixRun(steps);
|
|
1138
1227
|
}
|
|
1228
|
+
if (!isTelemetryDisabled(config.telemetry?.enabled)) {
|
|
1229
|
+
const totalResolved = steps.reduce((sum, s) => sum + s.resolvedIssues, 0);
|
|
1230
|
+
trackEvent({
|
|
1231
|
+
command: "fix",
|
|
1232
|
+
languages: projectInfo.languages,
|
|
1233
|
+
fixSteps: steps.length,
|
|
1234
|
+
fixResolved: totalResolved
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1139
1237
|
logger.break();
|
|
1140
1238
|
logger.success(" ✓ Done. Run `aislop scan` to verify.");
|
|
1141
1239
|
logger.break();
|
|
@@ -1180,7 +1278,8 @@ const DEFAULT_CONFIG = {
|
|
|
1180
1278
|
ci: {
|
|
1181
1279
|
failBelow: 0,
|
|
1182
1280
|
format: "json"
|
|
1183
|
-
}
|
|
1281
|
+
},
|
|
1282
|
+
telemetry: { enabled: true }
|
|
1184
1283
|
};
|
|
1185
1284
|
const DEFAULT_CONFIG_YAML = `version: 1
|
|
1186
1285
|
|
|
@@ -1217,6 +1316,9 @@ scoring:
|
|
|
1217
1316
|
ci:
|
|
1218
1317
|
failBelow: 0
|
|
1219
1318
|
format: json
|
|
1319
|
+
|
|
1320
|
+
# telemetry:
|
|
1321
|
+
# enabled: true # set to false to disable anonymous usage analytics
|
|
1220
1322
|
`;
|
|
1221
1323
|
const DEFAULT_RULES_YAML = `# Architecture rules (BYO)
|
|
1222
1324
|
# Uncomment and customize to enforce your project's conventions.
|
|
@@ -1277,6 +1379,7 @@ const CiSchema = z.object({
|
|
|
1277
1379
|
failBelow: z.number().default(0),
|
|
1278
1380
|
format: z.enum(["json"]).default("json")
|
|
1279
1381
|
});
|
|
1382
|
+
const TelemetrySchema = z.object({ enabled: z.boolean().default(true) });
|
|
1280
1383
|
const AislopConfigSchema = z.object({
|
|
1281
1384
|
version: z.number().default(1),
|
|
1282
1385
|
engines: EnginesSchema.default(() => ({
|
|
@@ -1307,7 +1410,8 @@ const AislopConfigSchema = z.object({
|
|
|
1307
1410
|
ci: CiSchema.default(() => ({
|
|
1308
1411
|
failBelow: 0,
|
|
1309
1412
|
format: "json"
|
|
1310
|
-
}))
|
|
1413
|
+
})),
|
|
1414
|
+
telemetry: TelemetrySchema.default(() => ({ enabled: true }))
|
|
1311
1415
|
});
|
|
1312
1416
|
const defaults = AislopConfigSchema.parse({});
|
|
1313
1417
|
/**
|
|
@@ -2395,17 +2499,21 @@ const isDataFile = (content) => {
|
|
|
2395
2499
|
const dataLinePattern = /^\s*[{}[\]"']/;
|
|
2396
2500
|
return nonEmpty.filter((l) => dataLinePattern.test(l)).length / nonEmpty.length > .8;
|
|
2397
2501
|
};
|
|
2502
|
+
const ARROW_BLOCK_RE = /* @__PURE__ */ new RegExp("=>\\s*\\{");
|
|
2503
|
+
const ARROW_END_RE = /* @__PURE__ */ new RegExp("=>\\s*$");
|
|
2504
|
+
const BRACE_START_RE = /* @__PURE__ */ new RegExp("^\\s*\\{");
|
|
2505
|
+
const NEW_STATEMENT_RE = /* @__PURE__ */ new RegExp("^(?:export\\s+)?(?:const|let|var|function|class)\\s");
|
|
2398
2506
|
const isBlockArrow = (lines, startIndex) => {
|
|
2399
|
-
if (
|
|
2400
|
-
if (
|
|
2507
|
+
if (ARROW_BLOCK_RE.test(lines[startIndex])) return true;
|
|
2508
|
+
if (ARROW_END_RE.test(lines[startIndex])) {
|
|
2401
2509
|
const next = lines[startIndex + 1];
|
|
2402
|
-
if (next &&
|
|
2510
|
+
if (next && BRACE_START_RE.test(next)) return true;
|
|
2403
2511
|
}
|
|
2404
2512
|
for (let j = startIndex + 1; j < Math.min(startIndex + 3, lines.length); j++) {
|
|
2405
2513
|
const l = lines[j];
|
|
2406
|
-
if (l.trim() === "" ||
|
|
2407
|
-
if (
|
|
2408
|
-
if (
|
|
2514
|
+
if (l.trim() === "" || NEW_STATEMENT_RE.test(l.trim())) break;
|
|
2515
|
+
if (ARROW_BLOCK_RE.test(l)) return true;
|
|
2516
|
+
if (BRACE_START_RE.test(l)) return true;
|
|
2409
2517
|
}
|
|
2410
2518
|
return false;
|
|
2411
2519
|
};
|
|
@@ -3861,8 +3969,25 @@ const scanCommand = async (directory, config, options) => {
|
|
|
3861
3969
|
const elapsedMs = performance.now() - startTime;
|
|
3862
3970
|
const scoreResult = calculateScore(allDiagnostics, config.scoring.weights, config.scoring.thresholds);
|
|
3863
3971
|
const exitCode = scoreResult.score < config.ci.failBelow ? 1 : 0;
|
|
3972
|
+
if (!isTelemetryDisabled(config.telemetry?.enabled)) {
|
|
3973
|
+
const engineIssues = {};
|
|
3974
|
+
const engineTimings = {};
|
|
3975
|
+
for (const r of results) {
|
|
3976
|
+
engineIssues[r.engine] = r.diagnostics.length;
|
|
3977
|
+
engineTimings[r.engine] = Math.round(r.elapsed);
|
|
3978
|
+
}
|
|
3979
|
+
trackEvent({
|
|
3980
|
+
command: options.command ?? "scan",
|
|
3981
|
+
languages: projectInfo.languages,
|
|
3982
|
+
scoreBucket: getScoreBucket(scoreResult.score),
|
|
3983
|
+
engineIssues,
|
|
3984
|
+
engineTimings,
|
|
3985
|
+
elapsedMs: Math.round(elapsedMs),
|
|
3986
|
+
fileCount: projectInfo.sourceFileCount
|
|
3987
|
+
});
|
|
3988
|
+
}
|
|
3864
3989
|
if (options.json) {
|
|
3865
|
-
const { buildJsonOutput } = await import("./json-
|
|
3990
|
+
const { buildJsonOutput } = await import("./json-SHDiefXX.js");
|
|
3866
3991
|
const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
|
|
3867
3992
|
console.log(JSON.stringify(jsonOut, null, 2));
|
|
3868
3993
|
return { exitCode };
|