aislop 0.6.2 → 0.7.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/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/aislop)
|
|
6
6
|
[](https://www.npmjs.com/package/aislop)
|
|
7
7
|
[](https://github.com/scanaislop/aislop/actions/workflows/ci.yml)
|
|
8
|
+
[](https://scanaislop.com/scanaislop/aislop)
|
|
8
9
|
[](https://opensource.org/licenses/MIT)
|
|
9
10
|
[](https://nodejs.org)
|
|
10
11
|
|
|
@@ -34,7 +35,7 @@ npx aislop fix
|
|
|
34
35
|
# CI mode (JSON output + quality gate)
|
|
35
36
|
npx aislop ci
|
|
36
37
|
|
|
37
|
-
# wire aislop into your agent so it runs on every edit
|
|
38
|
+
# wire aislop into your agent so it runs on every edit
|
|
38
39
|
npx aislop hook install --claude
|
|
39
40
|
```
|
|
40
41
|
|
|
@@ -46,7 +47,7 @@ Sample output:
|
|
|
46
47
|
[!] Code Quality: done (2 warnings, 812ms)
|
|
47
48
|
[!] AI Slop: done (4 warnings, 455ms)
|
|
48
49
|
[ok] Security: done (0 issues, 1.3s)
|
|
49
|
-
aislop 0.
|
|
50
|
+
aislop 0.7.0 · the quality gate for agentic coding
|
|
50
51
|
|
|
51
52
|
scan · my-app · typescript · 142 files
|
|
52
53
|
|
|
@@ -78,7 +79,7 @@ AI coding tools generate code that compiles and passes tests but ships with patt
|
|
|
78
79
|
|
|
79
80
|
- **One score, one gate**: a 0-100 number you can enforce in CI with `aislop ci`. Weighted so sloppy patterns (dead code, `as any`, swallowed errors) hit harder than style noise.
|
|
80
81
|
- **Auto-fix first, agent second**: `aislop fix` clears what's mechanically safe (formatters, unused imports, trivial comments, dead patterns). For the rest, one flag hands off to Claude Code, Codex, Cursor, Gemini, Windsurf, Amp, Aider, Goose, and 7 more, with full diagnostic context pre-filled.
|
|
81
|
-
- **Wire it into your agent
|
|
82
|
+
- **Wire it into your agent**: `aislop hook install` plugs aislop into Claude Code, Cursor, Gemini CLI (runtime), plus Codex, Windsurf, Cline, Kilo Code, Antigravity, and Copilot (rules-only). The agent gets score + findings on the turn it wrote the code, not after.
|
|
82
83
|
- **Deterministic**: regex, AST, and standard tooling. No LLMs, no API keys, no network dependency. Same repo in, same score out.
|
|
83
84
|
- **Zero-config start**: `npx aislop scan` works on any repo. Add `.aislop/config.yml` when you want to tune thresholds or enable the architecture engine.
|
|
84
85
|
- **Works across stacks**: TypeScript, JavaScript, Python, Go, Rust, Ruby, PHP, Expo / React Native.
|
|
@@ -154,6 +155,18 @@ aislop scan --exclude "src/generated" --exclude "**/*.spec.*"
|
|
|
154
155
|
|
|
155
156
|
CLI flags beat config; config beats defaults.
|
|
156
157
|
|
|
158
|
+
**Extend a shared config.** A project config can extend a parent and override specific keys. Useful for org-wide baselines: ship one strict config, let each repo soften or tighten as needed.
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
# .aislop/config.yml
|
|
162
|
+
extends: ../../.aislop/base.yml # relative path to a parent config
|
|
163
|
+
|
|
164
|
+
ci:
|
|
165
|
+
failBelow: 80 # override just this key, inherit the rest
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`extends:` accepts a single path or an array of paths. Later entries win. Deep-merged: nested objects (`scoring.weights`, `engines`) are merged key-by-key; arrays are replaced. Circular references and depths beyond 5 are rejected with a clear error.
|
|
169
|
+
|
|
157
170
|
### Fix issues automatically
|
|
158
171
|
|
|
159
172
|
```bash
|
|
@@ -237,6 +250,7 @@ aislop scan
|
|
|
237
250
|
aislop init # create .aislop/config.yml
|
|
238
251
|
aislop doctor # check which tools are available
|
|
239
252
|
aislop rules # list all built-in rules
|
|
253
|
+
aislop badge # print the public score badge URL + README snippet
|
|
240
254
|
aislop hook install # wire aislop into your coding agent
|
|
241
255
|
aislop # interactive menu
|
|
242
256
|
```
|
|
@@ -313,6 +327,18 @@ ci:
|
|
|
313
327
|
|
|
314
328
|
The CLI is MIT-licensed and always will be. [Learn more about the platform →](https://scanaislop.com)
|
|
315
329
|
|
|
330
|
+
## Public score badge
|
|
331
|
+
|
|
332
|
+
Show your aislop score on a README. Free for any project that opts in on [scanaislop.com](https://scanaislop.com).
|
|
333
|
+
|
|
334
|
+
```markdown
|
|
335
|
+
[](https://scanaislop.com)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Shields-compatible SVG, edge-cached on Cloudflare. Colour-coded: green ≥ 85, amber 70-84, red < 70, grey if no scans yet.
|
|
339
|
+
|
|
340
|
+
Run `aislop badge` to print the snippet pre-filled with your repo's owner/name, auto-detected from `git remote get-url origin`.
|
|
341
|
+
|
|
316
342
|
## Contributing
|
|
317
343
|
|
|
318
344
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and how to add new rules. AI coding assistants can find project context in [AGENTS.md](AGENTS.md).
|
|
@@ -330,7 +356,15 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and how to add new
|
|
|
330
356
|
|
|
331
357
|
## Contributors
|
|
332
358
|
|
|
333
|
-
|
|
359
|
+
Thanks to everyone who has shipped code, ideas, docs, or bug reports.
|
|
360
|
+
|
|
361
|
+
<!-- CONTRIBUTORS-START -->
|
|
362
|
+
- [@heavykenny](https://github.com/heavykenny)
|
|
363
|
+
- [@myke-awoniran](https://github.com/myke-awoniran)
|
|
364
|
+
- [@yashrajoria](https://github.com/yashrajoria)
|
|
365
|
+
<!-- CONTRIBUTORS-END -->
|
|
366
|
+
|
|
367
|
+
This list is regenerated by `.github/workflows/contributors.yml` after every push to `develop` or `main`. The workflow reads git log, resolves each author's GitHub login, and opens a PR with any diff. If your commits aren't being credited, either link your commit email under [GitHub Settings → Emails](https://github.com/settings/emails) or add a mapping to [`.github/contributors-overrides.json`](.github/contributors-overrides.json).
|
|
334
368
|
|
|
335
369
|
## License
|
|
336
370
|
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import path from "node:path";
|
|
|
7
7
|
import YAML from "yaml";
|
|
8
8
|
import { z } from "zod/v4";
|
|
9
9
|
import { performance } from "node:perf_hooks";
|
|
10
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import { execSync, spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import micromatch from "micromatch";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import ts from "typescript";
|
|
@@ -209,6 +209,48 @@ const DEFAULT_RULES_YAML = `# Architecture rules (BYO)
|
|
|
209
209
|
# severity: error
|
|
210
210
|
`;
|
|
211
211
|
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/config/extends.ts
|
|
214
|
+
const MAX_DEPTH = 5;
|
|
215
|
+
const isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
216
|
+
const deepMerge = (...sources) => {
|
|
217
|
+
const result = {};
|
|
218
|
+
for (const source of sources) for (const key of Object.keys(source)) {
|
|
219
|
+
const a = result[key];
|
|
220
|
+
const b = source[key];
|
|
221
|
+
result[key] = isPlainObject(a) && isPlainObject(b) ? deepMerge(a, b) : b;
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
};
|
|
225
|
+
const resolveExtendsRef = (ref, fromDir) => {
|
|
226
|
+
if (ref.startsWith("http://") || ref.startsWith("https://")) throw new Error(`URL-based extends not yet supported: ${ref}`);
|
|
227
|
+
if (ref.startsWith("./") || ref.startsWith("../") || path.isAbsolute(ref)) return path.resolve(fromDir, ref);
|
|
228
|
+
throw new Error(`Package-name extends not yet supported: ${ref} (use a relative path for now)`);
|
|
229
|
+
};
|
|
230
|
+
const normalizeExtends = (raw) => {
|
|
231
|
+
if (raw === void 0 || raw === null) return [];
|
|
232
|
+
if (typeof raw === "string") return [raw];
|
|
233
|
+
if (Array.isArray(raw) && raw.every((s) => typeof s === "string")) return raw;
|
|
234
|
+
throw new Error("`extends` must be a string or array of strings");
|
|
235
|
+
};
|
|
236
|
+
const loadConfigChain = (configPath, visited = /* @__PURE__ */ new Set(), depth = 0) => {
|
|
237
|
+
if (depth > MAX_DEPTH) throw new Error(`extends depth exceeded ${MAX_DEPTH} (cycle or runaway chain): ${configPath}`);
|
|
238
|
+
const absPath = path.resolve(configPath);
|
|
239
|
+
if (visited.has(absPath)) throw new Error(`circular extends detected: ${absPath}`);
|
|
240
|
+
if (!fs.existsSync(absPath)) throw new Error(`extends target not found: ${absPath}`);
|
|
241
|
+
const nextVisited = new Set(visited);
|
|
242
|
+
nextVisited.add(absPath);
|
|
243
|
+
const raw = fs.readFileSync(absPath, "utf-8");
|
|
244
|
+
const parsed = YAML.parse(raw) ?? {};
|
|
245
|
+
const refs = normalizeExtends(parsed.extends);
|
|
246
|
+
const fromDir = path.dirname(absPath);
|
|
247
|
+
const parents = refs.map((ref) => {
|
|
248
|
+
return loadConfigChain(resolveExtendsRef(ref, fromDir), nextVisited, depth + 1);
|
|
249
|
+
});
|
|
250
|
+
const { extends: _drop, ...own } = parsed;
|
|
251
|
+
return deepMerge(...parents, own);
|
|
252
|
+
};
|
|
253
|
+
|
|
212
254
|
//#endregion
|
|
213
255
|
//#region src/config/schema.ts
|
|
214
256
|
const DEFAULT_WEIGHTS = {
|
|
@@ -343,8 +385,7 @@ const loadConfig = (directory) => {
|
|
|
343
385
|
const configPath = path.join(configDir, CONFIG_FILE);
|
|
344
386
|
if (!fs.existsSync(configPath)) return DEFAULT_CONFIG;
|
|
345
387
|
try {
|
|
346
|
-
|
|
347
|
-
return parseConfig(YAML.parse(raw));
|
|
388
|
+
return parseConfig(loadConfigChain(configPath));
|
|
348
389
|
} catch (error) {
|
|
349
390
|
const msg = error instanceof Error ? error.message : String(error);
|
|
350
391
|
process.stderr.write(` ⚠ Failed to parse ${configPath}: ${msg}\n ⚠ Using default configuration.\n`);
|
|
@@ -5918,6 +5959,83 @@ const registerHookCommand = (program) => {
|
|
|
5918
5959
|
registerCallbacks(hook);
|
|
5919
5960
|
};
|
|
5920
5961
|
|
|
5962
|
+
//#endregion
|
|
5963
|
+
//#region src/commands/badge.ts
|
|
5964
|
+
const GITHUB_REMOTE_RE = /^(?:git@github\.com:|https:\/\/(?:[^@]+@)?github\.com\/)([^/]+)\/([^/.\s]+?)(?:\.git)?\s*$/;
|
|
5965
|
+
const renderBadgeOutput = ({ owner, repo, svgUrl, pageUrl }) => {
|
|
5966
|
+
const slug = `${owner}/${repo}`;
|
|
5967
|
+
const markdown = `[](${pageUrl})`;
|
|
5968
|
+
return [
|
|
5969
|
+
``,
|
|
5970
|
+
` Repository: ${slug}`,
|
|
5971
|
+
` Badge URL: ${svgUrl}`,
|
|
5972
|
+
``,
|
|
5973
|
+
` Markdown:`,
|
|
5974
|
+
``,
|
|
5975
|
+
` ${markdown}`,
|
|
5976
|
+
``,
|
|
5977
|
+
` Drop the line above into your README. The badge auto-updates after every public scan.`,
|
|
5978
|
+
``
|
|
5979
|
+
].join("\n");
|
|
5980
|
+
};
|
|
5981
|
+
const detectGithubSlugFromGit = (directory) => {
|
|
5982
|
+
let raw;
|
|
5983
|
+
try {
|
|
5984
|
+
raw = execSync("git remote get-url origin", {
|
|
5985
|
+
cwd: path.resolve(directory),
|
|
5986
|
+
encoding: "utf-8",
|
|
5987
|
+
stdio: [
|
|
5988
|
+
"ignore",
|
|
5989
|
+
"pipe",
|
|
5990
|
+
"ignore"
|
|
5991
|
+
]
|
|
5992
|
+
});
|
|
5993
|
+
} catch {
|
|
5994
|
+
return null;
|
|
5995
|
+
}
|
|
5996
|
+
const match = raw.trim().match(GITHUB_REMOTE_RE);
|
|
5997
|
+
if (!match) return null;
|
|
5998
|
+
const owner = match[1];
|
|
5999
|
+
const repo = match[2];
|
|
6000
|
+
if (!owner || !repo) return null;
|
|
6001
|
+
return {
|
|
6002
|
+
owner,
|
|
6003
|
+
repo
|
|
6004
|
+
};
|
|
6005
|
+
};
|
|
6006
|
+
const badgeCommand = async (options = {}) => {
|
|
6007
|
+
let owner = options.owner?.trim();
|
|
6008
|
+
let repo = options.repo?.trim();
|
|
6009
|
+
if (!owner || !repo) {
|
|
6010
|
+
const detected = detectGithubSlugFromGit(options.directory ?? ".");
|
|
6011
|
+
if (!detected) throw new Error("Could not detect a GitHub remote. Run from a repo with `git remote get-url origin` set, or pass --owner and --repo.");
|
|
6012
|
+
owner ??= detected.owner;
|
|
6013
|
+
repo ??= detected.repo;
|
|
6014
|
+
}
|
|
6015
|
+
const svgUrl = `https://badges.scanaislop.com/score/${owner}/${repo}.svg`;
|
|
6016
|
+
const pageUrl = `https://scanaislop.com/${owner}/${repo}`;
|
|
6017
|
+
const output = renderBadgeOutput({
|
|
6018
|
+
owner,
|
|
6019
|
+
repo,
|
|
6020
|
+
svgUrl,
|
|
6021
|
+
pageUrl
|
|
6022
|
+
});
|
|
6023
|
+
if (options.json) process.stdout.write(JSON.stringify({
|
|
6024
|
+
owner,
|
|
6025
|
+
repo,
|
|
6026
|
+
svgUrl,
|
|
6027
|
+
pageUrl
|
|
6028
|
+
}) + "\n");
|
|
6029
|
+
else process.stdout.write(output);
|
|
6030
|
+
return {
|
|
6031
|
+
owner,
|
|
6032
|
+
repo,
|
|
6033
|
+
svgUrl,
|
|
6034
|
+
pageUrl,
|
|
6035
|
+
output
|
|
6036
|
+
};
|
|
6037
|
+
};
|
|
6038
|
+
|
|
5921
6039
|
//#endregion
|
|
5922
6040
|
//#region src/ui/symbols.ts
|
|
5923
6041
|
const TTY = {
|
|
@@ -6334,7 +6452,7 @@ const renderCleanRun = (input, deps = {}) => {
|
|
|
6334
6452
|
|
|
6335
6453
|
//#endregion
|
|
6336
6454
|
//#region src/version.ts
|
|
6337
|
-
const APP_VERSION = "0.
|
|
6455
|
+
const APP_VERSION = "0.7.0";
|
|
6338
6456
|
|
|
6339
6457
|
//#endregion
|
|
6340
6458
|
//#region src/utils/telemetry.ts
|
|
@@ -8980,6 +9098,20 @@ program.command("ci [directory]").description("CI-friendly JSON output with exit
|
|
|
8980
9098
|
program.command("rules [directory]").description("List all available rules").action(async (directory = ".") => {
|
|
8981
9099
|
await rulesCommand(directory);
|
|
8982
9100
|
});
|
|
9101
|
+
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) => {
|
|
9102
|
+
const flags = command.optsWithGlobals();
|
|
9103
|
+
try {
|
|
9104
|
+
await badgeCommand({
|
|
9105
|
+
directory,
|
|
9106
|
+
owner: flags.owner,
|
|
9107
|
+
repo: flags.repo,
|
|
9108
|
+
json: Boolean(flags.json)
|
|
9109
|
+
});
|
|
9110
|
+
} catch (err) {
|
|
9111
|
+
process.stderr.write(`${err?.message ?? "Failed to print badge"}\n`);
|
|
9112
|
+
process.exit(1);
|
|
9113
|
+
}
|
|
9114
|
+
});
|
|
8983
9115
|
registerHookCommand(program);
|
|
8984
9116
|
const main = async () => {
|
|
8985
9117
|
await program.parseAsync();
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-
|
|
1
|
+
import { n as ENGINE_INFO, r as getEngineLabel, t as APP_VERSION } from "./version-BOJR1S8l.js";
|
|
2
2
|
import { n as runSubprocess, t as isToolInstalled } from "./subprocess-CQUJDGgn.js";
|
|
3
3
|
import { r as runGenericLinter, t as fixRubyLint } from "./generic-BrcWMW7E.js";
|
|
4
4
|
import { n as runExpoDoctor } from "./expo-doctor-Bz0LZhQ6.js";
|
|
@@ -103,6 +103,48 @@ const DEFAULT_RULES_YAML = `# Architecture rules (BYO)
|
|
|
103
103
|
# severity: error
|
|
104
104
|
`;
|
|
105
105
|
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/config/extends.ts
|
|
108
|
+
const MAX_DEPTH = 5;
|
|
109
|
+
const isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
110
|
+
const deepMerge = (...sources) => {
|
|
111
|
+
const result = {};
|
|
112
|
+
for (const source of sources) for (const key of Object.keys(source)) {
|
|
113
|
+
const a = result[key];
|
|
114
|
+
const b = source[key];
|
|
115
|
+
result[key] = isPlainObject(a) && isPlainObject(b) ? deepMerge(a, b) : b;
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
const resolveExtendsRef = (ref, fromDir) => {
|
|
120
|
+
if (ref.startsWith("http://") || ref.startsWith("https://")) throw new Error(`URL-based extends not yet supported: ${ref}`);
|
|
121
|
+
if (ref.startsWith("./") || ref.startsWith("../") || path.isAbsolute(ref)) return path.resolve(fromDir, ref);
|
|
122
|
+
throw new Error(`Package-name extends not yet supported: ${ref} (use a relative path for now)`);
|
|
123
|
+
};
|
|
124
|
+
const normalizeExtends = (raw) => {
|
|
125
|
+
if (raw === void 0 || raw === null) return [];
|
|
126
|
+
if (typeof raw === "string") return [raw];
|
|
127
|
+
if (Array.isArray(raw) && raw.every((s) => typeof s === "string")) return raw;
|
|
128
|
+
throw new Error("`extends` must be a string or array of strings");
|
|
129
|
+
};
|
|
130
|
+
const loadConfigChain = (configPath, visited = /* @__PURE__ */ new Set(), depth = 0) => {
|
|
131
|
+
if (depth > MAX_DEPTH) throw new Error(`extends depth exceeded ${MAX_DEPTH} (cycle or runaway chain): ${configPath}`);
|
|
132
|
+
const absPath = path.resolve(configPath);
|
|
133
|
+
if (visited.has(absPath)) throw new Error(`circular extends detected: ${absPath}`);
|
|
134
|
+
if (!fs.existsSync(absPath)) throw new Error(`extends target not found: ${absPath}`);
|
|
135
|
+
const nextVisited = new Set(visited);
|
|
136
|
+
nextVisited.add(absPath);
|
|
137
|
+
const raw = fs.readFileSync(absPath, "utf-8");
|
|
138
|
+
const parsed = YAML.parse(raw) ?? {};
|
|
139
|
+
const refs = normalizeExtends(parsed.extends);
|
|
140
|
+
const fromDir = path.dirname(absPath);
|
|
141
|
+
const parents = refs.map((ref) => {
|
|
142
|
+
return loadConfigChain(resolveExtendsRef(ref, fromDir), nextVisited, depth + 1);
|
|
143
|
+
});
|
|
144
|
+
const { extends: _drop, ...own } = parsed;
|
|
145
|
+
return deepMerge(...parents, own);
|
|
146
|
+
};
|
|
147
|
+
|
|
106
148
|
//#endregion
|
|
107
149
|
//#region src/config/schema.ts
|
|
108
150
|
const DEFAULT_WEIGHTS = {
|
|
@@ -237,8 +279,7 @@ const loadConfig = (directory) => {
|
|
|
237
279
|
const configPath = path.join(configDir, CONFIG_FILE);
|
|
238
280
|
if (!fs.existsSync(configPath)) return DEFAULT_CONFIG;
|
|
239
281
|
try {
|
|
240
|
-
|
|
241
|
-
return parseConfig(YAML.parse(raw));
|
|
282
|
+
return parseConfig(loadConfigChain(configPath));
|
|
242
283
|
} catch (error) {
|
|
243
284
|
const msg = error instanceof Error ? error.message : String(error);
|
|
244
285
|
process.stderr.write(` ⚠ Failed to parse ${configPath}: ${msg}\n ⚠ Using default configuration.\n`);
|
|
@@ -5341,7 +5382,7 @@ const scanCommand = async (directory, config, options) => {
|
|
|
5341
5382
|
});
|
|
5342
5383
|
}
|
|
5343
5384
|
if (options.json) {
|
|
5344
|
-
const { buildJsonOutput } = await import("./json-
|
|
5385
|
+
const { buildJsonOutput } = await import("./json-DwAcCqqG.js");
|
|
5345
5386
|
const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
|
|
5346
5387
|
console.log(JSON.stringify(jsonOut, null, 2));
|
|
5347
5388
|
return { exitCode };
|
|
@@ -29,7 +29,7 @@ const getEngineLabel = (engine) => ENGINE_INFO[engine].label;
|
|
|
29
29
|
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/version.ts
|
|
32
|
-
const APP_VERSION = "0.
|
|
32
|
+
const APP_VERSION = "0.7.0";
|
|
33
33
|
|
|
34
34
|
//#endregion
|
|
35
35
|
export { ENGINE_INFO as n, getEngineLabel as r, APP_VERSION as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aislop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
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": {
|
|
@@ -81,5 +81,10 @@
|
|
|
81
81
|
"@types/node": "^25.6.0",
|
|
82
82
|
"tsdown": "^0.20.3",
|
|
83
83
|
"vitest": "^4.0.18"
|
|
84
|
+
},
|
|
85
|
+
"pnpm": {
|
|
86
|
+
"overrides": {
|
|
87
|
+
"postcss@<8.5.10": "^8.5.10"
|
|
88
|
+
}
|
|
84
89
|
}
|
|
85
90
|
}
|