aislop 0.1.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.
@@ -0,0 +1,39 @@
1
+ //#region src/version.ts
2
+ /**
3
+ * Application version — injected at build time by tsdown from package.json.
4
+ * The fallback should always match the "version" field in package.json.
5
+ */
6
+ const APP_VERSION = "0.1.0";
7
+
8
+ //#endregion
9
+ //#region src/output/engine-info.ts
10
+ const ENGINE_INFO = {
11
+ format: {
12
+ label: "Formatting",
13
+ description: "Whitespace, indentation, line wrapping, and import ordering"
14
+ },
15
+ lint: {
16
+ label: "Linting",
17
+ description: "Static analysis for likely bugs and bad patterns"
18
+ },
19
+ "code-quality": {
20
+ label: "Code Quality",
21
+ description: "Complexity limits, dead code detection, and duplication checks"
22
+ },
23
+ "ai-slop": {
24
+ label: "Maintainability",
25
+ description: "Over-abstraction, swallowed errors, and low-signal code patterns"
26
+ },
27
+ architecture: {
28
+ label: "Architecture",
29
+ description: "Project-specific import and layering rules"
30
+ },
31
+ security: {
32
+ label: "Security",
33
+ description: "Secret leaks, risky APIs, and dependency vulnerabilities"
34
+ }
35
+ };
36
+ const getEngineLabel = (engine) => ENGINE_INFO[engine].label;
37
+
38
+ //#endregion
39
+ export { getEngineLabel as n, APP_VERSION as r, ENGINE_INFO as t };
@@ -0,0 +1,126 @@
1
+ import { n as runSubprocess } from "./subprocess-99puEEGl.js";
2
+ import { createRequire } from "node:module";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+
6
+ //#region src/engines/lint/expo-doctor.ts
7
+ const esmRequire = createRequire(import.meta.url);
8
+ const ISSUE_PREFIX = "✖ ";
9
+ const resolveExpoDoctorScript = () => {
10
+ try {
11
+ const packageJsonPath = esmRequire.resolve("expo-doctor/package.json");
12
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
13
+ const binRelativePath = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["expo-doctor"];
14
+ if (!binRelativePath) return null;
15
+ return path.join(path.dirname(packageJsonPath), binRelativePath);
16
+ } catch {
17
+ return null;
18
+ }
19
+ };
20
+ const toRuleSuffix = (title) => {
21
+ const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
22
+ return slug.length > 0 ? slug : "issue";
23
+ };
24
+ const parseIssues = (output) => {
25
+ const lines = output.split("\n").map((line) => line.trimEnd());
26
+ const startIndex = lines.findIndex((line) => line.includes("Possible issues detected:"));
27
+ if (startIndex < 0) return [];
28
+ const issues = [];
29
+ let current = null;
30
+ let inAdvice = false;
31
+ for (let i = startIndex + 1; i < lines.length; i += 1) {
32
+ const line = lines[i].trim();
33
+ if (/^\d+\s+checks failed/.test(line)) break;
34
+ if (line.length === 0) continue;
35
+ if (line.startsWith(ISSUE_PREFIX)) {
36
+ if (current) issues.push(current);
37
+ current = {
38
+ title: line.slice(2).trim(),
39
+ details: [],
40
+ advice: []
41
+ };
42
+ inAdvice = false;
43
+ continue;
44
+ }
45
+ if (!current) continue;
46
+ if (line === "Advice:") {
47
+ inAdvice = true;
48
+ continue;
49
+ }
50
+ if (inAdvice) current.advice.push(line);
51
+ else current.details.push(line);
52
+ }
53
+ if (current) issues.push(current);
54
+ return issues;
55
+ };
56
+ const parseConfigError = (output) => {
57
+ const line = output.split("\n").find((candidate) => candidate.trim().startsWith("ConfigError:"));
58
+ return line ? line.trim() : null;
59
+ };
60
+ const toDiagnostics = (issues) => issues.map((issue) => {
61
+ const helpParts = [issue.details.join(" ").trim(), issue.advice.join(" ").trim()].filter((part) => part.length > 0);
62
+ return {
63
+ filePath: "package.json",
64
+ engine: "lint",
65
+ rule: `expo-doctor/${toRuleSuffix(issue.title)}`,
66
+ severity: "warning",
67
+ message: `Expo Doctor: ${issue.title}`,
68
+ help: helpParts.join(" "),
69
+ line: 0,
70
+ column: 0,
71
+ category: "Expo",
72
+ fixable: false
73
+ };
74
+ });
75
+ const runExpoDoctor = async (context) => {
76
+ const scriptPath = resolveExpoDoctorScript();
77
+ let stdout = "";
78
+ let stderr = "";
79
+ try {
80
+ if (scriptPath) {
81
+ const result = await runSubprocess(process.execPath, [
82
+ scriptPath,
83
+ context.rootDirectory,
84
+ "--verbose"
85
+ ], {
86
+ cwd: context.rootDirectory,
87
+ timeout: 12e4
88
+ });
89
+ stdout = result.stdout;
90
+ stderr = result.stderr;
91
+ } else {
92
+ const result = await runSubprocess("npx", [
93
+ "--yes",
94
+ "expo-doctor",
95
+ context.rootDirectory,
96
+ "--verbose"
97
+ ], {
98
+ cwd: context.rootDirectory,
99
+ timeout: 12e4
100
+ });
101
+ stdout = result.stdout;
102
+ stderr = result.stderr;
103
+ }
104
+ } catch {
105
+ return [];
106
+ }
107
+ const output = [stdout, stderr].filter(Boolean).join("\n");
108
+ if (!output) return [];
109
+ const configError = parseConfigError(output);
110
+ if (configError) return [{
111
+ filePath: "package.json",
112
+ engine: "lint",
113
+ rule: "expo-doctor/config-error",
114
+ severity: "warning",
115
+ message: configError,
116
+ help: "Install project dependencies, then re-run `aislop scan`.",
117
+ line: 0,
118
+ column: 0,
119
+ category: "Expo",
120
+ fixable: false
121
+ }];
122
+ return toDiagnostics(parseIssues(output));
123
+ };
124
+
125
+ //#endregion
126
+ export { runExpoDoctor };
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ import { r as runSubprocess } from "./cli.js";
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+ import fs from "node:fs";
6
+
7
+ //#region src/engines/lint/expo-doctor.ts
8
+ const esmRequire = createRequire(import.meta.url);
9
+ const ISSUE_PREFIX = "✖ ";
10
+ const resolveExpoDoctorScript = () => {
11
+ try {
12
+ const packageJsonPath = esmRequire.resolve("expo-doctor/package.json");
13
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
14
+ const binRelativePath = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["expo-doctor"];
15
+ if (!binRelativePath) return null;
16
+ return path.join(path.dirname(packageJsonPath), binRelativePath);
17
+ } catch {
18
+ return null;
19
+ }
20
+ };
21
+ const toRuleSuffix = (title) => {
22
+ const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
23
+ return slug.length > 0 ? slug : "issue";
24
+ };
25
+ const parseIssues = (output) => {
26
+ const lines = output.split("\n").map((line) => line.trimEnd());
27
+ const startIndex = lines.findIndex((line) => line.includes("Possible issues detected:"));
28
+ if (startIndex < 0) return [];
29
+ const issues = [];
30
+ let current = null;
31
+ let inAdvice = false;
32
+ for (let i = startIndex + 1; i < lines.length; i += 1) {
33
+ const line = lines[i].trim();
34
+ if (/^\d+\s+checks failed/.test(line)) break;
35
+ if (line.length === 0) continue;
36
+ if (line.startsWith(ISSUE_PREFIX)) {
37
+ if (current) issues.push(current);
38
+ current = {
39
+ title: line.slice(2).trim(),
40
+ details: [],
41
+ advice: []
42
+ };
43
+ inAdvice = false;
44
+ continue;
45
+ }
46
+ if (!current) continue;
47
+ if (line === "Advice:") {
48
+ inAdvice = true;
49
+ continue;
50
+ }
51
+ if (inAdvice) current.advice.push(line);
52
+ else current.details.push(line);
53
+ }
54
+ if (current) issues.push(current);
55
+ return issues;
56
+ };
57
+ const parseConfigError = (output) => {
58
+ const line = output.split("\n").find((candidate) => candidate.trim().startsWith("ConfigError:"));
59
+ return line ? line.trim() : null;
60
+ };
61
+ const toDiagnostics = (issues) => issues.map((issue) => {
62
+ const helpParts = [issue.details.join(" ").trim(), issue.advice.join(" ").trim()].filter((part) => part.length > 0);
63
+ return {
64
+ filePath: "package.json",
65
+ engine: "lint",
66
+ rule: `expo-doctor/${toRuleSuffix(issue.title)}`,
67
+ severity: "warning",
68
+ message: `Expo Doctor: ${issue.title}`,
69
+ help: helpParts.join(" "),
70
+ line: 0,
71
+ column: 0,
72
+ category: "Expo",
73
+ fixable: false
74
+ };
75
+ });
76
+ const runExpoDoctor = async (context) => {
77
+ const scriptPath = resolveExpoDoctorScript();
78
+ let stdout = "";
79
+ let stderr = "";
80
+ try {
81
+ if (scriptPath) {
82
+ const result = await runSubprocess(process.execPath, [
83
+ scriptPath,
84
+ context.rootDirectory,
85
+ "--verbose"
86
+ ], {
87
+ cwd: context.rootDirectory,
88
+ timeout: 12e4
89
+ });
90
+ stdout = result.stdout;
91
+ stderr = result.stderr;
92
+ } else {
93
+ const result = await runSubprocess("npx", [
94
+ "--yes",
95
+ "expo-doctor",
96
+ context.rootDirectory,
97
+ "--verbose"
98
+ ], {
99
+ cwd: context.rootDirectory,
100
+ timeout: 12e4
101
+ });
102
+ stdout = result.stdout;
103
+ stderr = result.stderr;
104
+ }
105
+ } catch {
106
+ return [];
107
+ }
108
+ const output = [stdout, stderr].filter(Boolean).join("\n");
109
+ if (!output) return [];
110
+ const configError = parseConfigError(output);
111
+ if (configError) return [{
112
+ filePath: "package.json",
113
+ engine: "lint",
114
+ rule: "expo-doctor/config-error",
115
+ severity: "warning",
116
+ message: configError,
117
+ help: "Install project dependencies, then re-run `aislop scan`.",
118
+ line: 0,
119
+ column: 0,
120
+ category: "Expo",
121
+ fixable: false
122
+ }];
123
+ return toDiagnostics(parseIssues(output));
124
+ };
125
+
126
+ //#endregion
127
+ export { runExpoDoctor };
@@ -0,0 +1,114 @@
1
+ import { z } from "zod/v4";
2
+
3
+ //#region src/commands/doctor.d.ts
4
+ declare const doctorCommand: (directory: string) => Promise<void>;
5
+ //#endregion
6
+ //#region src/config/schema.d.ts
7
+ declare const AislopConfigSchema: z.ZodObject<{
8
+ version: z.ZodDefault<z.ZodNumber>;
9
+ engines: z.ZodDefault<z.ZodObject<{
10
+ format: z.ZodDefault<z.ZodBoolean>;
11
+ lint: z.ZodDefault<z.ZodBoolean>;
12
+ "code-quality": z.ZodDefault<z.ZodBoolean>;
13
+ "ai-slop": z.ZodDefault<z.ZodBoolean>;
14
+ architecture: z.ZodDefault<z.ZodBoolean>;
15
+ security: z.ZodDefault<z.ZodBoolean>;
16
+ }, z.core.$strip>>;
17
+ quality: z.ZodDefault<z.ZodObject<{
18
+ maxFunctionLoc: z.ZodDefault<z.ZodNumber>;
19
+ maxFileLoc: z.ZodDefault<z.ZodNumber>;
20
+ maxNesting: z.ZodDefault<z.ZodNumber>;
21
+ maxParams: z.ZodDefault<z.ZodNumber>;
22
+ }, z.core.$strip>>;
23
+ security: z.ZodDefault<z.ZodObject<{
24
+ audit: z.ZodDefault<z.ZodBoolean>;
25
+ auditTimeout: z.ZodDefault<z.ZodNumber>;
26
+ }, z.core.$strip>>;
27
+ scoring: z.ZodDefault<z.ZodObject<{
28
+ weights: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
29
+ thresholds: z.ZodDefault<z.ZodObject<{
30
+ good: z.ZodDefault<z.ZodNumber>;
31
+ ok: z.ZodDefault<z.ZodNumber>;
32
+ }, z.core.$strip>>;
33
+ }, z.core.$strip>>;
34
+ ci: z.ZodDefault<z.ZodObject<{
35
+ failBelow: z.ZodDefault<z.ZodNumber>;
36
+ format: z.ZodDefault<z.ZodEnum<{
37
+ json: "json";
38
+ }>>;
39
+ }, z.core.$strip>>;
40
+ }, z.core.$strip>;
41
+ type AislopConfig = z.infer<typeof AislopConfigSchema>;
42
+ //#endregion
43
+ //#region src/config/index.d.ts
44
+ declare const loadConfig: (directory: string) => AislopConfig;
45
+ //#endregion
46
+ //#region src/commands/fix.d.ts
47
+ interface FixOptions {
48
+ verbose: boolean;
49
+ showHeader?: boolean;
50
+ }
51
+ declare const fixCommand: (directory: string, config: AislopConfig, options?: FixOptions) => Promise<void>;
52
+ //#endregion
53
+ //#region src/commands/init.d.ts
54
+ declare const initCommand: (directory: string) => Promise<void>;
55
+ //#endregion
56
+ //#region src/commands/scan.d.ts
57
+ interface ScanOptions {
58
+ changes: boolean;
59
+ staged: boolean;
60
+ verbose: boolean;
61
+ json: boolean;
62
+ showHeader?: boolean;
63
+ }
64
+ declare const scanCommand: (directory: string, config: AislopConfig, options: ScanOptions) => Promise<{
65
+ exitCode: number;
66
+ }>;
67
+ //#endregion
68
+ //#region src/utils/discover.d.ts
69
+ type Language = "typescript" | "javascript" | "python" | "go" | "rust" | "java" | "ruby" | "php";
70
+ type Framework = "nextjs" | "react" | "vite" | "remix" | "expo" | "django" | "flask" | "fastapi" | "none";
71
+ interface ProjectInfo {
72
+ rootDirectory: string;
73
+ projectName: string;
74
+ languages: Language[];
75
+ frameworks: Framework[];
76
+ sourceFileCount: number;
77
+ installedTools: Record<string, boolean>;
78
+ }
79
+ declare const discoverProject: (directory: string) => Promise<ProjectInfo>;
80
+ //#endregion
81
+ //#region src/engines/types.d.ts
82
+ type Severity = "error" | "warning" | "info";
83
+ type EngineName = "format" | "lint" | "code-quality" | "ai-slop" | "architecture" | "security";
84
+ interface Diagnostic {
85
+ filePath: string;
86
+ engine: EngineName;
87
+ rule: string;
88
+ severity: Severity;
89
+ message: string;
90
+ help: string;
91
+ line: number;
92
+ column: number;
93
+ category: string;
94
+ fixable: boolean;
95
+ }
96
+ interface EngineResult {
97
+ engine: EngineName;
98
+ diagnostics: Diagnostic[];
99
+ elapsed: number;
100
+ skipped: boolean;
101
+ skipReason?: string;
102
+ }
103
+ //#endregion
104
+ //#region src/scoring/index.d.ts
105
+ interface ScoreResult {
106
+ score: number;
107
+ label: string;
108
+ }
109
+ declare const calculateScore: (diagnostics: Diagnostic[], weights: Record<string, number>, thresholds: {
110
+ good: number;
111
+ ok: number;
112
+ }) => ScoreResult;
113
+ //#endregion
114
+ export { type AislopConfig, type Diagnostic, type EngineName, type EngineResult, type Framework, type Language, type ProjectInfo, type ScoreResult, type Severity, calculateScore, discoverProject, doctorCommand, fixCommand, initCommand, loadConfig, scanCommand };