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 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 (/=>\s*\{/.test(lines[startIndex])) return true;
1385
- if (/=>\s*$/.test(lines[startIndex])) {
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 && /^\s*\{/.test(next)) return true;
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() === "" || /^(?:export\s+)?(?:const|let|var|function|class)\s/.test(l.trim())) break;
1392
- if (/=>\s*\{/.test(l)) return true;
1393
- if (/^\s*\{/.test(l)) return true;
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.0";
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.0";
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-DBG3uXLc.js";
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 (/=>\s*\{/.test(lines[startIndex])) return true;
2400
- if (/=>\s*$/.test(lines[startIndex])) {
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 && /^\s*\{/.test(next)) return true;
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() === "" || /^(?:export\s+)?(?:const|let|var|function|class)\s/.test(l.trim())) break;
2407
- if (/=>\s*\{/.test(l)) return true;
2408
- if (/^\s*\{/.test(l)) return true;
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-DkpW9UQj.js");
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 };
@@ -1,4 +1,4 @@
1
- import { r as APP_VERSION, t as ENGINE_INFO } from "./engine-info-DBG3uXLc.js";
1
+ import { r as APP_VERSION, t as ENGINE_INFO } from "./engine-info-Bw_OOj3G.js";
2
2
 
3
3
  //#region src/output/json.ts
4
4
  const buildJsonOutput = (results, scoreResult, fileCount, elapsedMs) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aislop",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Stop AI slop from shipping. A unified code quality CLI that catches the lazy patterns AI coding tools leave behind.",
5
5
  "type": "module",
6
6
  "bin": {