aislop 0.6.1 → 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
  [![npm version](https://img.shields.io/npm/v/aislop.svg)](https://www.npmjs.com/package/aislop)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/aislop.svg)](https://www.npmjs.com/package/aislop)
7
7
  [![CI](https://github.com/scanaislop/aislop/actions/workflows/ci.yml/badge.svg)](https://github.com/scanaislop/aislop/actions/workflows/ci.yml)
8
+ [![aislop score](https://badges.scanaislop.com/score/scanaislop/aislop.svg)](https://scanaislop.com/scanaislop/aislop)
8
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
9
10
  [![Node >= 20](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](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 (new in 0.6.0)
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.6.1 · the quality gate for agentic coding
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 (new in 0.6.0)**: `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
+ - **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
+ [![aislop](https://badges.scanaislop.com/score/<owner>/<repo>.svg)](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
- [![Contributors](https://contrib.rocks/image?repo=scanaislop/aislop)](https://github.com/scanaislop/aislop/graphs/contributors)
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
- const raw = fs.readFileSync(configPath, "utf-8");
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`);
@@ -2184,6 +2225,10 @@ const getIssueItems = (fileIssue, issueType) => {
2184
2225
  const items = fileIssue[issueType];
2185
2226
  return Array.isArray(items) ? items : [];
2186
2227
  };
2228
+ const shouldIncludeIssue = (issueType, filePath) => {
2229
+ if (issueType !== "binaries") return true;
2230
+ return !filePath.replace(/\\/g, "/").includes(".github/workflows/");
2231
+ };
2187
2232
  const DEPENDENCY_HELP = {
2188
2233
  dependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `npx aislop fix`.",
2189
2234
  devDependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `npx aislop fix`.",
@@ -2193,6 +2238,7 @@ const DEPENDENCY_HELP = {
2193
2238
  };
2194
2239
  const collectIssues = (fileIssue, issueType, rootDir, knipCwd) => {
2195
2240
  const diagnostics = [];
2241
+ if (!shouldIncludeIssue(issueType, fileIssue.file)) return diagnostics;
2196
2242
  const issues = getIssueItems(fileIssue, issueType);
2197
2243
  const category = isDependencyType(issueType) ? "Dependencies" : "Dead Code";
2198
2244
  const severity = issueType === "unlisted" || issueType === "unresolved" ? "error" : "warning";
@@ -5913,6 +5959,83 @@ const registerHookCommand = (program) => {
5913
5959
  registerCallbacks(hook);
5914
5960
  };
5915
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 = `[![aislop](${svgUrl})](${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
+
5916
6039
  //#endregion
5917
6040
  //#region src/ui/symbols.ts
5918
6041
  const TTY = {
@@ -6329,7 +6452,7 @@ const renderCleanRun = (input, deps = {}) => {
6329
6452
 
6330
6453
  //#endregion
6331
6454
  //#region src/version.ts
6332
- const APP_VERSION = "0.6.1";
6455
+ const APP_VERSION = "0.7.0";
6333
6456
 
6334
6457
  //#endregion
6335
6458
  //#region src/utils/telemetry.ts
@@ -8975,6 +9098,20 @@ program.command("ci [directory]").description("CI-friendly JSON output with exit
8975
9098
  program.command("rules [directory]").description("List all available rules").action(async (directory = ".") => {
8976
9099
  await rulesCommand(directory);
8977
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
+ });
8978
9115
  registerHookCommand(program);
8979
9116
  const main = async () => {
8980
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-DukdnmKT.js";
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
- const raw = fs.readFileSync(configPath, "utf-8");
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`);
@@ -2700,6 +2741,10 @@ const getIssueItems = (fileIssue, issueType) => {
2700
2741
  const items = fileIssue[issueType];
2701
2742
  return Array.isArray(items) ? items : [];
2702
2743
  };
2744
+ const shouldIncludeIssue = (issueType, filePath) => {
2745
+ if (issueType !== "binaries") return true;
2746
+ return !filePath.replace(/\\/g, "/").includes(".github/workflows/");
2747
+ };
2703
2748
  const DEPENDENCY_HELP = {
2704
2749
  dependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `npx aislop fix`.",
2705
2750
  devDependencies: "This package is listed in package.json but not imported anywhere. Remove it with `npm uninstall` or `npx aislop fix`.",
@@ -2709,6 +2754,7 @@ const DEPENDENCY_HELP = {
2709
2754
  };
2710
2755
  const collectIssues = (fileIssue, issueType, rootDir, knipCwd) => {
2711
2756
  const diagnostics = [];
2757
+ if (!shouldIncludeIssue(issueType, fileIssue.file)) return diagnostics;
2712
2758
  const issues = getIssueItems(fileIssue, issueType);
2713
2759
  const category = isDependencyType(issueType) ? "Dependencies" : "Dead Code";
2714
2760
  const severity = issueType === "unlisted" || issueType === "unresolved" ? "error" : "warning";
@@ -5336,7 +5382,7 @@ const scanCommand = async (directory, config, options) => {
5336
5382
  });
5337
5383
  }
5338
5384
  if (options.json) {
5339
- const { buildJsonOutput } = await import("./json-DIW4kCBS.js");
5385
+ const { buildJsonOutput } = await import("./json-DwAcCqqG.js");
5340
5386
  const jsonOut = buildJsonOutput(results, scoreResult, projectInfo.sourceFileCount, elapsedMs);
5341
5387
  console.log(JSON.stringify(jsonOut, null, 2));
5342
5388
  return { exitCode };
@@ -1,4 +1,4 @@
1
- import { n as ENGINE_INFO, t as APP_VERSION } from "./version-DukdnmKT.js";
1
+ import { n as ENGINE_INFO, t as APP_VERSION } from "./version-BOJR1S8l.js";
2
2
 
3
3
  //#region src/output/json.ts
4
4
  const buildJsonOutput = (results, scoreResult, fileCount, elapsedMs) => {
@@ -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.6.1";
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.6.1",
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
  }